Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 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
b6e5e21
♻️ refactor: sdk/extensiontest: the answer schema check moves to its …
pascalandy Nov 1, 2026
1939de6
♻️ refactor: sdk/contract: the schema compiler moves to its own file
pascalandy Nov 1, 2026
491f1da
🐛 fix: contract: ncly starts in a folder whose path holds a percent sign
pascalandy Nov 1, 2026
bdd4485
♻️ refactor: template: the severity forms of finish move to their own…
pascalandy Nov 1, 2026
7afe8ba
🐛 fix: template: finish fails on a grade in free form, and states its…
pascalandy Nov 1, 2026
59d33f3
🐛 fix: template: a finding of this round with no state is open
pascalandy Nov 1, 2026
066d48f
♻️ refactor: sdk/extensiontest: the coverage report moves to its own …
pascalandy Nov 1, 2026
08f05e7
✨ feat: template: just signoff runs the first failing run again
pascalandy Nov 1, 2026
1944800
♻️ refactor: template: the proof of a command that replaces a file mo…
pascalandy Nov 1, 2026
51b7c04
✨ feat: template: spec.md lists one case matrix before the first review
pascalandy Nov 1, 2026
b987e58
♻️ refactor: template: the error mapping of the skill moves to its ow…
pascalandy Nov 1, 2026
7bf5dc1
📝 docs: template: each fact of the intake links the part that the age…
pascalandy Nov 1, 2026
c415bd8
📝 docs: template: a test file with private text in a block that a rea…
pascalandy Nov 1, 2026
5ecbb96
🐛 fix: template: an ordinary table cell that ends with block passes f…
pascalandy Nov 1, 2026
84117d6
✅ test: template: the replay refuses another failing line, and just s…
pascalandy Nov 1, 2026
171c79c
🐛 fix: template: a stateless advise can be declined, and other review…
pascalandy Nov 1, 2026
9c94f6b
📝 docs: template: the reviewer marks a repair fixed only with a pair …
pascalandy Nov 1, 2026
2e649bb
🐛 fix: template: finish reads None in code in the matrix, and no grad…
pascalandy Nov 1, 2026
6744069
🐛 fix: template: only the first assertion of the first failing run is…
pascalandy Nov 1, 2026
f9dbd7e
✅ test: template: finish reads the first assertion of the first faili…
pascalandy Nov 1, 2026
97ddaf7
📝 docs: verify-nicely: the loop text of the template names its case m…
pascalandy Nov 1, 2026
a3b4730
📝 docs: template: the case matrix check reads the references only
pascalandy Nov 1, 2026
1a4556c
♻️ refactor: docs: move the limit of the parser to its own file
pascalandy Nov 2, 2026
19ff6be
📝 docs: finish gate: an accepted open finding leaves ## Open lines
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$\|^## Case matrix$' "$run/scratch/ncly-probe/spec.md"`. Both exit 0, and `spec.md` lists both parts. "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
4 changes: 2 additions & 2 deletions cmd/ncly/scriptcmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -558,8 +558,8 @@ func cmdSchema(ts *testscript.TestScript, neg bool, args []string) {
compiler := jsonschema.NewCompiler()
compiler.DefaultDraft(jsonschema.Draft2020)
compiler.UseLoader(noRemote{})
ts.Check(compiler.AddResource("describe.json", doc))
schema, err := compiler.Compile("describe.json")
ts.Check(compiler.AddResource("urn:describe", doc))
schema, err := compiler.Compile("urn:describe")
ts.Check(err)
answer, err := jsonschema.UnmarshalJSON(strings.NewReader(ts.ReadFile(args[0])))
ts.Check(err)
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
19 changes: 5 additions & 14 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,18 +36,9 @@ 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 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 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. After `just finish`, it runs the first failing run again, as [Finish gate](finish-gate.md#the-first-failing-run-again) says
- `just scenario <name>` runs the scenario `testdata/script/<name>.txtar` alone, and the test kit skips its coverage checks
- `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
- `just uninstall` removes the install of the slug from that folder, the executable first. With nothing installed, it says so and exits 0
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
Loading
Loading