Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
0892931
♻️ refactor: template: move the verdict of readiness.md to its own file
pascalandy Oct 31, 2026
dcdfd12
♻️ refactor: template: move the receipt refusals of the release proof…
pascalandy Oct 31, 2026
e71f0f9
🐛 fix: template: release needs just finish in the signoff receipt
pascalandy Oct 31, 2026
6192786
♻️ refactor: contract: move the checks of just finish to their own part
pascalandy Oct 31, 2026
4e65816
✨ feat: template: finish reads a ledger of rounds and findings in rea…
pascalandy Oct 31, 2026
b22166d
♻️ refactor: template: move the readiness review of the skill to its …
pascalandy Oct 31, 2026
4d711d2
📝 docs: template: round 1 starts at the intake, a build stops at 5 ro…
pascalandy Oct 31, 2026
62cbebe
🐛 fix: template: finish reads the history of a build from its intake
pascalandy Oct 31, 2026
a1c2fdb
♻️ refactor: sdk/wrap: move the program check into check.go
pascalandy Oct 31, 2026
c4bc0f7
♻️ refactor: testdata: move the stop cases of the hello wrap scenario…
pascalandy Oct 31, 2026
17b02ba
🐛 fix: sdk/wrap: name the input in signal and timeout answers
pascalandy Oct 31, 2026
3fc33c0
🐛 fix: template: inspect names its file in signal and timeout answers
pascalandy Oct 31, 2026
95e94df
📝 docs: contract: move the scenario commands of the test kit to their…
pascalandy Oct 31, 2026
b82bcfa
✨ feat: sdk/extensiontest: protect also guards the next signal call
pascalandy Oct 31, 2026
005e001
✨ feat: sdk/extensiontest: appears proves that a run never replaces a…
pascalandy Oct 31, 2026
2c9fb21
📝 docs: contract: acceptance.sh checks the output with an independent…
pascalandy Oct 31, 2026
fd00b34
📝 docs: template: the skill names the kit commands that prove a comma…
pascalandy Oct 31, 2026
5859d69
✅ test: template: the proof of finish names an author in place of a p…
pascalandy Oct 31, 2026
8e5da03
🐛 fix: template: a moved intake or a hidden row never resets the ledger
pascalandy Oct 31, 2026
7f3ae60
🐛 fix: template: a forged end of build, an uncommitted round, or a fi…
pascalandy Oct 31, 2026
b3dc8b4
📝 docs: template: the rule index names the checks of the ledger history
pascalandy Oct 31, 2026
38e1f05
🐛 fix: template: a build ends only at a review that just finish would…
pascalandy Oct 31, 2026
30546ad
♻️ refactor: template: the ledger is cumulative, and a round with the…
pascalandy Oct 31, 2026
5404c9f
🐛 fix: template: a round with the Finish pass needs a review that jus…
pascalandy Oct 31, 2026
39bff54
✅ test: sdk/extensiontest: the copy cases share one go test of the fi…
pascalandy Oct 31, 2026
d4dd003
♻️ refactor: scripts: move the proof of the release URL to its own file
pascalandy Oct 31, 2026
7ef57ef
🐛 fix: template: finish reads plain words as words, and release runs …
pascalandy Oct 31, 2026
5bed3fb
✨ feat: template: the ledger records the first failing run, and Open …
pascalandy Oct 31, 2026
591d25a
♻️ refactor: template: move the intake of the skill to its own file
pascalandy Oct 31, 2026
7270c28
📝 docs: template: the intake shows what was read, each generic case g…
pascalandy Oct 31, 2026
0deb72f
🐛 fix: template: the first failing run names its scenario commit, and…
pascalandy Nov 1, 2026
1289fc1
📝 docs: template: the record of the first failing run, the forms that…
pascalandy Nov 1, 2026
4f42521
🐛 fix: template: no finding ID hides outside ## Findings, and the run…
pascalandy Nov 1, 2026
1fa2f51
📝 docs: template: finish-gate names stray finding IDs, and verify-nic…
pascalandy Nov 1, 2026
2619136
🐛 fix: template: a heading outside ## Findings names no stray finding…
pascalandy Nov 1, 2026
2f813aa
🐛 fix: template: a finding heading in fenced code outside ## Findings…
pascalandy Nov 1, 2026
e114cd1
🚑 fix: template: the user can accept an open finding at the limit of …
pascalandy Nov 2, 2026
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
2 changes: 1 addition & 1 deletion .agents/skills/verify-nicely/features/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Preconditions:
- **Remove command.** In the output of `skill view nicely-extensions`, the skill holds the section `## Remove an extension`. It names `ncly doctor extensions --json`, the four files `ncly-SLUG`, `ncly-SLUG.toml`, `ncly-SLUG.locales/`, and `ncly-SLUG.skill/`, `just uninstall`, `NOT_FOUND`, and `GRANT_UNUSED`.
- **Render and install.** Run `just verify prepare "$run" template`. It exits 0. Its log names `just install` of `probe`, the line `installed <run>/scratch/home/.local/share/nicely/extensions/ncly-probe`, and the `python3` that it puts first in the PATH of the wrapper. Then run `ls "$run/scratch/home/.local/share/nicely/extensions"`: it lists `ncly-probe`, `ncly-probe.locales`, `ncly-probe.skill`, and `ncly-probe.toml`, beside the four files of `hello`. Report the output of `prepare` and of `ls` beside the quote.
- **Release and install text.** Run `grep -n -A12 '^## Release$' "$run/scratch/ncly-probe/AGENTS.md"` and `grep -n -A3 '^git clone' "$run/scratch/ncly-probe/README.md"`. Both exit 0. `AGENTS.md` names `just release vX.Y.Z` and the release receipt. `README.md` holds the lines `git clone --branch TAG https://example.com/ncly-probe ncly-probe`, `cd ncly-probe`, `just install`, and `ncly describe probe --json`. Report both outputs beside the quote.
- **Loop text.** Run `grep -n -A10 '^## Done when$' "$run/scratch/ncly-probe/AGENTS.md"`, then `grep -n '^## Open lines$' "$run/scratch/ncly-probe/spec.md"`. Both exit 0. "Done when" names `just finish`, the hosted checks, 5 rounds, and a "full stop" request that runs only after it. Then run `grep -n '^##' "$run/scratch/ncly-probe/.agents/skills/nicely-extension/SKILL.md"`. It lists `## Intake of a program`, `## Acceptance lines`, `## Rounds`, and `## Readiness review`. Report the three outputs beside the quote.
- **Loop text.** Run `grep -n -A12 '^## Done when$' "$run/scratch/ncly-probe/AGENTS.md"`, then `grep -n '^## Open lines$' "$run/scratch/ncly-probe/spec.md"`. Both exit 0. "Done when" names `just finish`, the hosted checks, 5 rounds, and a "full stop" request that runs only after it. Then run `grep -n '^##' "$run/scratch/ncly-probe/.agents/skills/nicely-extension/SKILL.md"`. It lists `## Intake`, `## Acceptance lines`, `## Test files`, `## Rounds`, and `## Readiness review`. Then run `grep -n '^##' "$run/scratch/ncly-probe/.agents/skills/nicely-extension/intake.md"`. It lists `## Intake of a script`, `## Intake of a program`, and `## Evidence of the intake`. Report the four outputs beside the quote.
- **Finish gate.** Run `(cd "$run/scratch/ncly-probe" && just finish)`, whose subshell keeps the folder of the caller. Exit 1. Stderr names each `Fill:` line of `vision.md`, `spec.md`, and `qa.md` as `<file>:<line>: this line of the form is not filled`, and `readiness.md: no readiness verdict is recorded`. Report the output beside the quote.
- **Discovery.** Run `just verify step "$run" template --help`. Exit 0, and the `EXTENSIONS` section lists `probe: Inspect files` beside `hello` and `skill`.
- **Help.** Run `just verify step "$run" template probe --help`. Exit 0, and `COMMANDS` lists `inspect: Give the size and the line count of a file`.
Expand Down
2 changes: 1 addition & 1 deletion docs/milestones/M18-extension-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Once this card ships, remove from the docs each marker that names M18-T3, such a
### M18-T4 Wrap programs and embed workers through the SDK

- **Read:** [Wrapped programs](../north-star/contract/wrap.md), [Programs that ncly runs](../north-star/contract/programs-that-ncly-runs.md), [Commands that core calls](../north-star/contract/commands-that-core-calls.md), [Required programs](../north-star/core-spec.md#required-programs), [SDK](../north-star/contract/sdk.md), [D047](../north-star/decisions/D047-terminal-process-group.md)
- **Proves:** `sdk/wrap/`, `testdata/extensions/hello/testdata/script/wrap.txtar`, and `testdata/script/session.txtar`. They cover a missing program, a version probe that times out, an exit other than 0, malformed output, and a timeout with exit 1. They also cover Ctrl-C with exit 130, an empty stdin, and a child environment with only the declared keys. A worker reads ./input.md from the caller's folder, and an open of /dev/tty fails at once under a real PTY, on Linux and macOS
- **Proves:** `sdk/wrap/`, `testdata/extensions/hello/testdata/script/wrap.txtar`, `wrap_stop.txtar` beside it, and `testdata/script/session.txtar`. They cover a missing program, a version probe that times out, an exit other than 0, malformed output, and a timeout with exit 1. They also cover Ctrl-C with exit 130, an empty stdin, and a child environment with only the declared keys. A worker reads ./input.md from the caller's folder, and an open of /dev/tty fails at once under a real PTY, on Linux and macOS

Add `sdk/wrap`, as [Wrapped programs](../north-star/contract/wrap.md) defines, and state its default size limit there. Add `Resources` to `extension.Extension`, with the extraction that [SDK](../north-star/contract/sdk.md) defines. Give the `hello` fixture one wrapped command with a worker, which the scenarios run.

Expand Down
2 changes: 2 additions & 0 deletions docs/north-star/contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ Each part is one file, in reading order.
- [Wrapped programs](contract/wrap.md)
- [Test kit](contract/test-kit.md)
- [Manifest examples](contract/manifest-examples.md)
- [Scenario commands](contract/scenario-commands.md)
- [Spec IDs](contract/spec-ids.md)
- [Generic cases](contract/generic-cases.md)
- [Extension repository](contract/extension-repository.md)
- [Finish gate](contract/finish-gate.md)
- [Update an extension](contract/update-an-extension.md)
- [Operations](contract/operations.md)
- [Records and evidence](contract/records-and-evidence.md)
Expand Down
16 changes: 3 additions & 13 deletions docs/north-star/contract/extension-repository.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The repository is one Go module. Its `go.mod` pins one Nicely version, as [Compa
| File | Holds |
|---|---|
| `README.md` | What the extension adds, and the commands that install a release |
| `vision.md` | The job of the extension in one sentence, the source and revision of each program that it wraps, and what it refuses |
| `vision.md` | The job of the extension in one sentence, the source and revision of each wrapped program, its intake facts, and what it refuses |
| `spec.md` | One section per command, titled `## ncly <path>`: what proves success, why each effect exists, each failure and its code, and the limits that the user accepted. Then `## Acceptance`: the real input, the properties that the output keeps, and each input that the wrapped program accepts but the domain refuses. Then `## Open lines`: the lines that no test proves yet. Each line of Failures and of Acceptance starts with its spec ID, as [Test kit](test-kit.md#spec-ids) says |
| `manifest.toml`, `locales/`, `skill/` | The manifest, the catalogs, and the skill, which the extension embeds as [SDK](sdk.md) defines |
| `worker/` | The scripts that it embeds as workers, one per command that wraps one |
Expand All @@ -18,7 +18,7 @@ The repository is one Go module. Its `go.mod` pins one Nicely version, as [Compa
| `AGENTS.md` | The reading order, the change steps, the recipes, and the release steps, for an agent that changes the extension |
| `AGENTS/rule-index.md` | The check that enforces each rule of `AGENTS.md` |
| `.agents/skills/nicely-extension/` | The skill that builds each command in rounds, from intake to a verified command, with a readiness review at the end of each round |
| `readiness.md` | The last readiness review: the commit that it reviewed, its reviewer, its verdict, and its findings |
| `readiness.md` | The last readiness review: the commit that it reviewed, its reviewer, its verdict, and its findings. Then the ledger of the build, as [Finish gate](finish-gate.md#the-ledger) defines |
| `.copier-answers.yml`, `lefthook.yml` | The template version that `just update` reads, and the `just check` that Lefthook runs before each push |
| `.github/workflows/check.yml` | The `just check` that GitHub runs on Ubuntu, plus macOS for public repositories, for each push to `main` and each pull request |

Expand All @@ -36,17 +36,7 @@ Public repositories also run the same steps on `macos-15`, and both runners are
The repository tests itself through the [test kit](test-kit.md). Its `justfile` has nine recipes:

- `just check` runs every check: `go mod tidy -diff`, `gofmt`, `go vet`, and `go test`, whose test kit installs the extension into scratch folders
- `just finish` is the exit of the loop of the skill, and `just check` stays green for an incomplete round. `just finish` fails on each of these:
- A missing `vision.md`, `spec.md`, or `qa.md`
- A line of `vision.md`, `spec.md`, or `qa.md` that starts with `Fill:` after its indent, also in fenced code
- A line under `## Open lines`
- A line under **Limits.** that is no table row with a spec ID, the state `limit`, and the user's acceptance
- A `readiness.md` whose first three lines hold no `Commit: <sha>`, no `Reviewer: <who>`, or no verdict `Ready` or `Ready with stated limits`
- A reviewed commit that differs from the working tree outside `readiness.md`
- A finding of `readiness.md` without its own `###` heading under `## Findings`, or without one severity: `block`, `warn`, or `advise`
- A severity under `## Findings` outside the line `- **Severity**:` of a finding, such as in a table, in fenced code, or in prose
- `Ready` with a `block` or `warn` finding, or with a limit in `spec.md`
- `Ready with stated limits` with a `block` finding, or with a `warn` whose **Limit** names no accepted limit
- `just finish` is the exit of the loop of the skill, and `just check` stays green for an incomplete round. [Finish gate](finish-gate.md) lists what it checks
- `just signoff` runs `just finish` and `just check` on the committed SHA in isolation, then signs that SHA, as [Local checks](../decisions/D016-local-ci.md) defines
- `just release <tag> [fixture]` publishes the signed SHA and proves the README install, as [Release and install](extensions.md#release-and-install) defines
- `just install` builds the executable and exports its sidecars from that build. It replaces the previous install of the slug in the extensions folder of [Discovery](discovery.md), the executable last
Expand Down
4 changes: 3 additions & 1 deletion docs/north-star/contract/extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ An author outside Nicely keeps an extension in a Git repository of its own, whic
An author releases a version with `just release vX.Y.Z [fixture]`.
The SDK requires a clean HEAD equal to the fetched default branch of `origin`.
A successful signoff receipt must name that SHA, with exit 0 for every check and signoff.
Release runs `just acceptance [fixture]`, then creates and pushes an annotated tag whose message holds that signoff receipt.
Its checks must hold `just finish`, so a receipt with only `just check` never releases.
Release runs `just finish` again on that clean HEAD, because a hand can write a receipt (#419).
Then it runs `just acceptance [fixture]`, and creates and pushes an annotated tag whose message holds that signoff receipt.
It executes the fenced `sh` block under `## Install` in `README.md`, with `TAG` replaced by the new tag.
The install uses scratch HOME and XDG folders, without the caller's keys or installed extensions.
The clone's HEAD must equal the tagged SHA, even when another README paragraph names the new tag.
Expand Down
90 changes: 90 additions & 0 deletions docs/north-star/contract/finish-gate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Finish gate

This part says what `just finish` checks in an extension repository, as [Extension repository](extension-repository.md) defines it.
`just finish` is the exit of the loop of the `nicely-extension` skill. `just check` stays green for an incomplete round.

`just finish` fails on each of these:

- A missing `vision.md`, `spec.md`, or `qa.md`
- A line of `vision.md`, `spec.md`, or `qa.md` that starts with `Fill:` after its indent, also in fenced code
- A line under `## Open lines`
- A line under **Limits.** that is no table row with a spec ID, the state `limit`, and the user's acceptance
- A `readiness.md` whose first three lines hold no `Commit: <sha>`, no `Reviewer: <who>`, or no verdict `Ready` or `Ready with stated limits`. A byte order mark before them is ignored
- A reviewed commit that differs from the working tree outside `readiness.md`, and outside the part **Limits.** of `spec.md`. So the user accepts a limit after a review, with no new round
- A finding of `readiness.md` without its own `###` heading under `## Findings`, or without one severity: `block`, `warn`, or `advise`
- A severity after the first three lines, outside the line `- **Severity**:` of a finding, in a form that grades. Such a form is code, such as `` `warn` ``, also in fenced code. A cell of a table that holds only a severity is such a form too, also with marks such as `**` around it. The word severity before a severity, such as "Severity: block", is a form that grades too. Under `## Findings`, the word severity fails alone. The message names the word or the table that caused it
- A finding heading with an ID, such as `### R1-F2`, outside `## Findings`. Also a line outside `## Findings` that names the ID of no finding under it
- `Ready` with a `block` or `warn` finding that is not `fixed`, or with a limit in `spec.md`
- `Ready with stated limits` with a `block` finding that is not `fixed`, or with a `warn` that is neither `fixed` nor an accepted `limit`

Finish reads a severity only in the line `- **Severity**:` of a finding.
The plain words block, warn, and advise pass in a title, in evidence, and in prose (#419).
An example is the title `### R1-F1 Large inputs block the terminal`.

## The ledger

`readiness.md` also holds the ledger of the repository (#417).
A build of a command goes from its intake to "Done when".
The ledger keeps each round and each finding of each build.
The reviewer of each round updates it, and commits `readiness.md` alone.

Under `## Rounds`, a table has the columns Round, Head, Change review, Change commits, Readiness review, and Finish.
Each row is one round, numbered from 1. The numbers go on from one build to the next.
Head is the full SHA of the reviewed commit.
Change commits names the commits that the change review covered, as `<first>..<last>`.
The readiness review covers Head.
When one pass did both reviews, both cells name the same reviewer.
Finish is `pass` or `fail` for an earlier round, and `pending` for the last round, whose result is this run.
A round before the last whose Finish is `pass` ends a build. The next round starts the next build.

Under `## Order of round 1`, a table with the columns Step and Commit names the commit of each step of the current build: Intake, Spec, Scenario, and Handler.

Under `## First failing run`, four lines record the run of the first scenario of the current build at its commit, before the handler (#420):

- `- **Commit**:` the Scenario commit of `## Order of round 1`, so a record of an earlier build never stands in
- `- **Command**:` the command that ran the scenario
- `- **Exit**:` its exit status, which is not 0
- `- **Assertion**:` the assertion of the scenario that failed

The reviewer refuses a failure that is only a compile error, because it does not prove the missing behavior.

Each finding under `## Findings` starts its heading with a stable ID, such as `### R2-F3`.
`R2` is the round that first reported it, and the ID stays in later rounds.
The reviewer of a later round gives each earlier finding one state, in a line `- **State**:`:

| State | Meaning | Parts |
|---|---|---|
| `open` | Work remains | `- **Spec**:` with its spec ID |
| `fixed` | A test proves the fix | `- **Commit**:` with the fixing commit, and `- **Spec**:` with a line of Failures or Acceptance |
| `limit` | The user accepted the loss | `- **Spec**:` with the ID of an accepted row under **Limits.** |
| `declined` | The author does not fix it | `- **Reason**:` with the reason of the author. Only an `advise` can be declined |

A finding of the last round can have no state yet.

`just finish` fails on each of these:

- No round under `## Rounds`, or a row without its six cells, its number, a full Head, or both reviewers
- A line under `## Rounds` or `## Order of round 1` that is no row of its table, such as a label that would hide rows
- A build of more than 5 rounds, also a build that ended. The message tells the agent to stop and to report to the user
- A round with the Finish `pass` whose newest committed review would fail `just finish` as the repository stood then. Its forms hold no `Fill:` line and no open line, and only `readiness.md` and **Limits.** of `spec.md` differ from its Head. Its verdict and states follow from its findings, which keep each finding of the reviews before it
- An Intake that is not after the Head of the round that ended the build before
- A commit of a round or of a fixed finding that is not HEAD or an ancestor of HEAD
- A last round whose Head is not the `Commit:` of `readiness.md`
- A step of round 1 that is missing, out of order, or in the commit of the step before it
- No part `## First failing run` outside fenced code. Also a part without the Scenario commit, without its command, without an exit status other than 0, or without its assertion
- A finding without an ID, a finding of an earlier round without a state, or a state without its parts
- A finding with the state `open`, even when `## Open lines` holds `None.`
- A finding with the state `open` whose ID no line under `## Open lines` of `spec.md` names. So `spec.md` and the ledger agree on the open work (#420)
- A `warn` or `block` finding with the state `declined`
- A finding of an earlier committed `readiness.md` that is missing, or whose severity went down. A finding without an ID keeps its title
- A committed review whose `Commit:` is the Head of no round, or a round before the last without a committed review of its Head
- A round of an earlier committed ledger that is missing or changed. Only a Finish that was `pending` changes

The history is every committed `readiness.md` of HEAD, so a moved Intake hides no finding and resets no round.
Finish checks a review that ends a build with the `spec.md` of its own commit. A later `spec.md` before the next round stands in when only **Limits.** differs, so a limit committed after the review counts.
The rows of `## Open lines` whose IDs became rows of **Limits.** may leave too, so the user can accept an open finding at the limit of rounds.
A limit or a spec ID that a later build changes does not change that check.
A review without a ledger, from a template before the ledger, needs a passing verdict only to end a build.
Its form of #409, a line `- **Limit**:` with a spec ID, reads as the state `limit`.

At 5 rounds, a failing `just finish` also tells the agent to stop and to report each open line and each open finding.
Loading
Loading