Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,25 @@ the documentation true.

Installed as a plugin, it answers to `harness`: `/harness:task`, `/harness:status`.

![The failing tests are written first, they fail, and you are the one who sees the
failure before any code is written](docs/demo/tests-and-gate.gif)

Stage 6 of a real task. The tests are written before the feature, they fail for the
reason they were meant to, and the question you are asked is the plugin's — Claude
neither writes it nor sees your answer. Declining sends the task back.

It governs three things, and they are independent of each other:

- **Documents** — a short top-level design, a searchable record of decisions, and a
throwaway document per task.
- **Workspace** — one per task, isolated, findable again weeks later.
- **Stages** — ten of them, three ending in a dialog you answer.

> **Status: early.** The harness has been through one real task — its own, fixing
> five defects that its first run exposed — and the ten stages, the refusal and the
> approval dialogs all held.
> **Status: early.** The harness has been through two tasks. Its own — fixing five
> defects that its first run exposed — and the one above, run end to end on a
> separate repository it had never seen: interview, design, decision records,
> failing tests, implementation, acceptance, commit. The ten stages and the three
> dialogs all held, and no source file was written before the design was approved.

## Why

Expand Down Expand Up @@ -51,6 +60,12 @@ advancing is refused unless the stage actually produced something. The stages in
bold end with a dialog whose wording the plugin owns — Claude cannot phrase the
question you are asked, and your answer never passes through it.

![The design and its test cases are presented, and stage 5 asks whether to accept
them before any code may be written](docs/demo/design-gate.gif)

Stage 5, from the same task: the test cases are laid out, and accepting them is what
unlocks writing code in the repository. Until then the refusal below is in force.

## The one refusal

Until you approve the design at stage 5, **nothing inside a repository is
Expand Down
Binary file added docs/demo/design-gate.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/demo/tests-and-gate.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 15 additions & 0 deletions test/conventions.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ test('53 — both top-level documents are within the bound stated for them', ()
assert.match(read('skills/init/SKILL.md'), /about 200 lines, architecture\s+at about 350/);
});

test('54 — every local file the README points at is there', () => {
const readme = read('README.md');

// ![alt](path) and [text](path), minus anything with a scheme — a broken image in
// the first screen of the README is the most visible way for this project to be
// caught saying something untrue about itself.
const links = [...readme.matchAll(/!?\[[^\]]*\]\(([^)]+)\)/g)]
.map((m) => m[1].split('#')[0].trim())
.filter((target) => target && !/^[a-z][a-z0-9+.-]*:/i.test(target));

assert.ok(links.length > 0, 'the README should point at something local');
const missing = links.filter((target) => !existsSync(join(REPO, target)));
assert.deepEqual(missing, [], 'the README points at files that do not exist');
});

test('38 — one version, declared in three files, and an install line that reaches it', () => {
const plugin = JSON.parse(read('.claude-plugin/plugin.json'));
const marketplace = JSON.parse(read('.claude-plugin/marketplace.json'));
Expand Down