From 0fba56503316fc0df83c3ec23d94099b23de5ad1 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:07:39 -0700 Subject: [PATCH 01/12] feat(revise): decide in the group's own words; answer how a proposal came about apply reads the discussion (Mark lines, the draft pull request's threads, pasted notes) and settles each proposal's mark for the driver to confirm. A question about a proposal is answered from the report, the clusters printout now kept beside it, and each pull request's capture. Co-Authored-By: Claude Opus 5.5 --- GUIDE.md | 2 +- ROADMAP.md | 1 + docs/contracts.md | 5 +- docs/design.md | 2 + .../discuss/capture/pr-0007/comment-list.json | 83 +++++++++++++++++++ .../discuss/capture/pr-0007/items.json | 83 +++++++++++++++++++ .../discuss/notes/meeting-2026-11-03.md | 4 + .../discuss/ratchet-2026-10.clusters.txt | 19 +++++ sandbox/fixture/discuss/ratchet-2026-10.md | 25 ++++++ sandbox/scenarios.yaml | 64 ++++++++++++++ skills/revise/SKILL.md | 43 ++++++---- skills/revise/apply.md | 34 +++++++- skills/revise/discuss.md | 46 ++++++++++ skills/revise/outline.md | 4 +- skills/revise/prepare.md | 27 +++--- tests/test_sandbox.py | 2 +- 16 files changed, 406 insertions(+), 38 deletions(-) create mode 100644 sandbox/fixture/discuss/capture/pr-0007/comment-list.json create mode 100644 sandbox/fixture/discuss/capture/pr-0007/items.json create mode 100644 sandbox/fixture/discuss/notes/meeting-2026-11-03.md create mode 100644 sandbox/fixture/discuss/ratchet-2026-10.clusters.txt create mode 100644 sandbox/fixture/discuss/ratchet-2026-10.md create mode 100644 skills/revise/discuss.md diff --git a/GUIDE.md b/GUIDE.md index 1a0b84b..e37f906 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -135,7 +135,7 @@ stateDiagram-v2 Figure 2. How a rule is admitted and retired. Every transition happens at a revision, with a person agreeing; the labels name the rows of `standards/dials.md`. A graduated rule is enforced by a lint, pre-commit or CI check, and its wording may stay as prose for what the check cannot judge, as R-012's does. -The driver runs `/lasp-dev:revise` before the monthly group meeting, or once ten pull requests have merged since the last report; nothing schedules it. Between meetings, the driver runs `/lasp-dev:revise capture` weekly: it harvests the window's merged pull requests into the standard's log and reads those new or changed since the last capture, so the monthly run reads them from there instead of starting dozens of agents at once. It rebuilds each merged pull request's record, and `thread-analyst` reads every thread, reviewer's summary and conversation comment; with `measures`, it also reads the tickets those pull requests name, the escaped defects and the build yield. The report proposes to keep, reword, retire or graduate each rule and to admit each candidate that meets the Dials, each to its destination: a rule, agent guidance, a data convention, a decision, a lint setting or a question for the group; and it says which numbers are counted and which are the analyst's reading. It also reports, from a sweep of the window's merged diffs, where the code follows or breaks the rules and the documented practice, and proposes a ticket for a problem still on `main` and a fix for a document the merged work has left behind. It opens with an overview of every proposal by priority and impact; the group takes them in that order, marks each accept, decline or escalate, a change to a data convention `accept, owner: ` and a question `answer: `, and leaves the rest blank, deferred. `revise apply` drafts only what was accepted, as reviewed pull requests, the standard's repository first when the standard lives elsewhere, then this repository's `standards/` and documents, and hands each accepted ticket to `ticket-draft` one at a time. The tiers, graduation and the archive are in the README's "How the corpus grows". +The driver runs `/lasp-dev:revise` before the monthly group meeting, or once ten pull requests have merged since the last report; nothing schedules it. Between meetings, the driver runs `/lasp-dev:revise capture` weekly: it harvests the window's merged pull requests into the standard's log and reads those new or changed since the last capture, so the monthly run reads them from there instead of starting dozens of agents at once. It rebuilds each merged pull request's record, and `thread-analyst` reads every thread, reviewer's summary and conversation comment; with `measures`, it also reads the tickets those pull requests name, the escaped defects and the build yield. The report proposes to keep, reword, retire or graduate each rule and to admit each candidate that meets the Dials, each to its destination: a rule, agent guidance, a data convention, a decision, a lint setting or a question for the group; and it says which numbers are counted and which are the analyst's reading. It also reports, from a sweep of the window's merged diffs, where the code follows or breaks the rules and the documented practice, and proposes a ticket for a problem still on `main` and a fix for a document the merged work has left behind. It opens with an overview of every proposal by priority and impact; the group takes them in that order and discusses each in its own words, on the proposal's `Mark:` line or in review comments and replies on the draft pull request `prepare` can open, and leaves the rest alone, deferred. Anyone can ask how a proposal came about, with `/lasp-dev:revise ` or in a comment the driver has answered, and the answer cites the cases it rests on. `revise apply` first reads the discussion and settles each proposal's mark, accept, accept with the agreed wording, decline or escalate, a change to a data convention `accept, owner: ` and a question `answer: `, which the driver confirms before it is written; then it drafts only what was accepted, as reviewed pull requests, the standard's repository first when the standard lives elsewhere, then this repository's `standards/` and documents, and hands each accepted ticket to `ticket-draft` one at a time. The tiers, graduation and the archive are in the README's "How the corpus grows". A new or reworded rule is first held to `standards/rule-bar.md`, its example resolving in merged code. diff --git a/ROADMAP.md b/ROADMAP.md index 68eb7be..87cc13f 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -38,6 +38,7 @@ merged pull request is its record. Owners are roles. Status is `planned`, `defer | The next round offered, from review to fix and back | After `pr-review` posts, or `pr-fix` pushes, one question offers the next round: make the fixes, commit, push and run `pr-review` again. Asked, never automatic, since one round is sometimes enough and the author may want to move fast. Today each skill ends by naming the next command | plugin maintainer | | idea | | Every decision point says what each answer does and where it lands (#14) | An audit of nine skills found Stop options that do not say what is kept and push gates that do not name the repository and branch | plugin maintainer | one skill at a time, each with sandbox evidence | planned | | Review without a typed command | The review contract run when a pull request opens, by a CI action, and `revise prepare` as a scheduled routine, so neither waits for someone to start it; posting stays behind a person's yes | plugin maintainer | the review's findings are trusted enough to run unattended | idea | +| The revision's draft pull request when the standard is this repository's own (#65) | With no clone, `prepare` leaves the report uncommitted in the working tree and offers no pull request, since `revise` never opens this repository's pull request; the group discusses on the report's `Mark:` lines or in pasted notes. A branch and draft pull request here would give it the review threads a clone's standard gets | plugin maintainer | a team with its standard in its own repository wants to discuss on GitHub | idea | ## Build and work diff --git a/docs/contracts.md b/docs/contracts.md index ca19a36..df1cf98 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -491,7 +491,10 @@ otherwise than its lines, and reads each tally line's fields by place, the quote a quote splits nothing. Writers: the driver, in harvest.md's "Clusters". Readers: `scripts/guidance_clusters.py`; `revise` -`prepare` steps 5, 6 and 10, through its printout; `sandbox/harvest_recall.py` `--clusters`. +`prepare` steps 5, 6 and 10, through its printout; `sandbox/harvest_recall.py` `--clusters`. The +printout the report was written from is kept, as printed, in `/ratchet-YYYY-MM.clusters.txt` +beside the report, and `revise`'s `discuss.md` reads it to trace a proposal, which names its +cluster `C`, to its pull requests and quotes. Pinned by: tests/test_guidance_clusters.py::test_prints_each_cluster_with_its_people_prs_and_quotes diff --git a/docs/design.md b/docs/design.md index 529c133..f73ed63 100644 --- a/docs/design.md +++ b/docs/design.md @@ -101,6 +101,8 @@ Libera's standard. **A skill researches and recommends before it asks; the person decides.** Asking for an answer, a verdict or a mark before anything has read the code asks the person to do that research alone, or to guess. So the planner recommends an answer to each question with its evidence and drafts the plan on it, marking the steps that assume it, so another answer is a visible edit rather than a second plan; `pr-fix`'s assessment recommends a disposition for each thread; and the revision report gives each proposal a `Recommended:` line with the evidence that decides it. A recommendation is never an answer: the plan gate asks with it marked, and the revision's checkboxes stay blank until the group marks them. (`.claude/CLAUDE.md`, "How a skill behaves".) +**The group decides in its own words; `apply` settles the marks.** A mark in a fixed form had to be typed on the report's line, so a meeting that agreed in a review thread, or agreed to different wording, had its outcome copied by hand or lost. No script parses a mark: `apply` reads the report already, so it reads the discussion too, the `Mark:` lines, the draft pull request's threads and pasted notes, and settles each proposal to one mark, which the driver confirms before it is written on the report's line. The fixed marks stay, as what a proposal settles to, because `apply`'s rules on overrides, owners and held candidates hang on them and the report stays the record of what was decided. A question about how a proposal came about is answered from the report, the clusters printout kept beside it and each pull request's capture, never from memory, so whoever asks, whenever, gets the cases it rests on. (`skills/revise/apply.md`; `skills/revise/discuss.md`.) + **The person steers; the sequence is a default.** A skill that runs a fixed sequence leaves the person a passenger, approving each step in turn. So at a natural decision point a skill says where things stand and asks what the person wants to do, the usual next step marked recommended: after the brief, `implement-change` asks whether to plan the change or build it now, since a small change at standard depth does not need a planner run of a quarter of an hour. The gates that stay are the ones before something hard to undo. The shape of a decision point is written once, in a file every asking skill cites, because `.claude/CLAUDE.md` loads only in this repository, and a review of the skills found about twenty widgets, half offering "Let's discuss", four marking a recommendation and several offering no way to stop. The same file says how a skill writes a file, since a heredoc carrying a ticket's markup stops the person at a permission prompt no skill planned. (`.claude/CLAUDE.md`, "How a skill behaves"; `skills/work/decision-points.md`.) What a skill says when a server it needs is missing or failing is written once too, in `skills/work/servers.md`: a review found about twenty skills each wording it their own way, most naming no fix and some stopping with only the error, so a person who had never set a server up could not tell what was lost or what to do. A person whose server is missing meets that message at whichever skill they run first, so it gives the same four things everywhere: which server, what is lost, whether the run goes on, and how to fix it. **A ticket for three readers, each fact once.** Drafts of the same ticket ran 650 to 1,150 words, and most of the excess was one fact told three or four times: a downstream repository in Used by, Not this ticket, Considerations and an Open Question's options. The ticket is written for the developer, the reviewer and the estimation meeting, each fact once, in the section that owns it; the investigation's detail stays on the `.review/` context page, which `implement-change` reads. In the sandbox this cut two real tickets by a quarter, one from 1,039 words to 746 and the other from 1,043 to 782, three runs each. No word count is set: tickets differ in size, and a fixed number, tried first, cut facts a reader needed to make the number. Two rules ride with it, since sessions disagreed on them: a fact the ticket rests on is re-read in the code and given its line, and a ticket that contradicts the page it cites becomes a question for the person rather than a side the agent picks. (`standards/ticket-format.md`; `ticket-draft` steps 4 and 5.) diff --git a/sandbox/fixture/discuss/capture/pr-0007/comment-list.json b/sandbox/fixture/discuss/capture/pr-0007/comment-list.json new file mode 100644 index 0000000..1a908f3 --- /dev/null +++ b/sandbox/fixture/discuss/capture/pr-0007/comment-list.json @@ -0,0 +1,83 @@ +{ + "pr": 7, + "comments": [ + { + "pr": 7, + "id": 101, + "url": "https://github.com/example/widgets/pull/7#discussion_r101", + "author": "alice", + "authorship": "human", + "kind": "thread", + "thread": "PRRT_a", + "path": "widgets/rows.py", + "line": 12, + "created": "2026-10-02T10:00:00Z", + "body": "Please raise on an empty table here instead of returning None.\n\nIn general, a reader should fail loudly on input it cannot use: a silent None turns into a wrong number three steps later." + }, + { + "pr": 7, + "id": 102, + "url": "https://github.com/example/widgets/pull/7#discussion_r102", + "author": "bob", + "authorship": "human", + "kind": "reply", + "thread": "PRRT_a", + "path": "widgets/rows.py", + "line": 12, + "created": "2026-10-03T10:00:00Z", + "body": "Agree, changed to raise ValueError." + }, + { + "pr": 7, + "id": 103, + "url": "https://github.com/example/widgets/pull/7#discussion_r103", + "author": "alice", + "authorship": "human", + "kind": "thread", + "thread": "PRRT_103", + "path": "widgets/rows.py", + "line": 12, + "created": "2026-10-04T10:00:00Z", + "body": "Keep the timeout for this one fetch at 30 s; the upstream archive is slow on Mondays." + }, + { + "pr": 7, + "id": 104, + "url": "https://github.com/example/widgets/pull/7#discussion_r104", + "author": "carol", + "authorship": "human", + "kind": "thread", + "thread": "PRRT_104", + "path": "widgets/rows.py", + "line": 12, + "created": "2026-10-05T10:00:00Z", + "body": "Name the column `flux_w_m2`, not `flux`. Units go in the column name so nobody has to guess them." + }, + { + "pr": 7, + "id": 105, + "url": "https://github.com/example/widgets/pull/7#pullrequestreview-105", + "author": "dana", + "authorship": "human", + "kind": "review", + "thread": null, + "path": null, + "line": null, + "created": "2026-10-06T10:00:00Z", + "body": "Looks good overall. For future PRs: put the ticket key on the first line of the description, the release notes are built from it." + }, + { + "pr": 7, + "id": 106, + "url": "https://github.com/example/widgets/pull/7#issuecomment-106", + "author": "bob", + "authorship": "human", + "kind": "comment", + "thread": null, + "path": null, + "line": null, + "created": "2026-10-07T10:00:00Z", + "body": "Thanks all, merging once CI is green." + } + ] +} \ No newline at end of file diff --git a/sandbox/fixture/discuss/capture/pr-0007/items.json b/sandbox/fixture/discuss/capture/pr-0007/items.json new file mode 100644 index 0000000..94d50d7 --- /dev/null +++ b/sandbox/fixture/discuss/capture/pr-0007/items.json @@ -0,0 +1,83 @@ +{ + "prs": [ + 7 + ], + "comments_read": 6, + "items": [ + { + "pr": 7, + "comment_id": 101, + "url": "https://github.com/example/widgets/pull/7#discussion_r101", + "author": "alice", + "authorship": "human", + "quote": "In general, a reader should fail loudly on input it cannot use", + "generality": "general", + "signal": "states a principle", + "kind": "rule", + "principle": "A reader raises on input it cannot use rather than returning None.", + "rationale": "a silent None turns into a wrong number three steps later.", + "covered_by": null, + "outcome": "changed as asked" + }, + { + "pr": 7, + "comment_id": 101, + "url": "https://github.com/example/widgets/pull/7#discussion_r101", + "author": "alice", + "authorship": "human", + "quote": "Please raise on an empty table here instead of returning None.", + "generality": "pr-specific", + "signal": "none", + "kind": "rule", + "principle": "Raise on an empty table in this reader.", + "rationale": null, + "covered_by": null, + "outcome": "changed as asked" + }, + { + "pr": 7, + "comment_id": 103, + "url": "https://github.com/example/widgets/pull/7#discussion_r103", + "author": "alice", + "authorship": "human", + "quote": "Keep the timeout for this one fetch at 30 s", + "generality": "general", + "signal": "states a principle", + "kind": "rule", + "principle": "Every network call in the project times out at 30 seconds.", + "rationale": "the upstream archive is slow on Mondays.", + "covered_by": null, + "outcome": "unanswered" + }, + { + "pr": 7, + "comment_id": 104, + "url": "https://github.com/example/widgets/pull/7#discussion_r104", + "author": "carol", + "authorship": "human", + "quote": "Units go in the column name so nobody has to guess them.", + "generality": "general", + "signal": "states a principle", + "kind": "data-convention", + "principle": "A column's name carries its units.", + "rationale": "so nobody has to guess them.", + "covered_by": null, + "outcome": "changed as asked" + }, + { + "pr": 7, + "comment_id": 105, + "url": "https://github.com/example/widgets/pull/7#pullrequestreview-105", + "author": "dana", + "authorship": "human", + "quote": "For future PRs: put the ticket key on the first line of the description", + "generality": "general", + "signal": "names a habit", + "kind": "process", + "principle": "A pull request description opens with its ticket key.", + "rationale": "the release notes are built from it.", + "covered_by": null, + "outcome": "unanswered" + } + ] +} \ No newline at end of file diff --git a/sandbox/fixture/discuss/notes/meeting-2026-11-03.md b/sandbox/fixture/discuss/notes/meeting-2026-11-03.md new file mode 100644 index 0000000..c3c12a3 --- /dev/null +++ b/sandbox/fixture/discuss/notes/meeting-2026-11-03.md @@ -0,0 +1,4 @@ +Revision meeting, 3 November + +- F-001: carol walked through it, bob and erin agreed. Take it as written. +- R-004: see erin's note on the report. diff --git a/sandbox/fixture/discuss/ratchet-2026-10.clusters.txt b/sandbox/fixture/discuss/ratchet-2026-10.clusters.txt new file mode 100644 index 0000000..6c04e32 --- /dev/null +++ b/sandbox/fixture/discuss/ratchet-2026-10.clusters.txt @@ -0,0 +1,19 @@ +cluster · C1 · rule · admit 1 · hold 0 · support 0 · prs 1 · people 1 · A reader raises on input it cannot use rather than returning None. +prs · C1 · pr-0007 +people · C1 · alice +priority · C1 · low · 1 pull request(s) +asked · C1 · pr-0007 +said · C1 · "In general, a reader should fail loudly on input it cannot use" — alice, https://github.com/example/widgets/pull/7#discussion_r101 +cluster · C2 · data-convention · admit 1 · hold 0 · support 0 · prs 1 · people 1 · A column holding a quantity names its unit. +prs · C2 · pr-0007 +people · C2 · carol +priority · C2 · low · 1 pull request(s) +asked · C2 · pr-0007 +said · C2 · "Units go in the column name so nobody has to guess them." — carol, https://github.com/example/widgets/pull/7#discussion_r104 +cluster · C3 · process · admit 1 · hold 0 · support 0 · prs 2 · people 1 · A pull request description opens with its ticket key. +prs · C3 · pr-0007, pr-0009 +people · C3 · dana +priority · C3 · medium · 2 pull request(s) +asked · C3 · pr-0007, pr-0009 +said · C3 · "put the ticket key on the first line of the description, the release notes are built from it." — dana, https://github.com/example/widgets/pull/7#pullrequestreview-105 +clusters: 3 · lines: 3 diff --git a/sandbox/fixture/discuss/ratchet-2026-10.md b/sandbox/fixture/discuss/ratchet-2026-10.md new file mode 100644 index 0000000..5fb264d --- /dev/null +++ b/sandbox/fixture/discuss/ratchet-2026-10.md @@ -0,0 +1,25 @@ +window: 2026-10-01..2026-10-31 + +# Standards revision · widgets · 2026-10 + +## Head +standard: this repository's standards/ +records: 1 harvested · 0 missing · 0 baseline +guidance: 6 comments · 5 items · 4 general, 1 pr-specific · general by kind: rule 2, data-convention 1, process 1 + +## Proposals +### Rules +- [ ] R-004 · A reader fails loudly on input it cannot use · single statement · pr-0007 · C1 + said: "In general, a reader should fail loudly on input it cannot use" — alice, https://github.com/example/widgets/pull/7#discussion_r101 + Recommended: accept — a person's general statement, changed as asked + Mark: yes, but word it "raises on input it cannot use", not "fails loudly"; nobody can test "loudly" — erin +### Data conventions +- [ ] F-001 · A column's name carries its units · source: carol · pr-0007 · C2 + said: "Units go in the column name so nobody has to guess them." — carol, https://github.com/example/widgets/pull/7#discussion_r104 + Recommended: accept + Mark: +### Decisions +- [ ] D-001 · A pull request description opens with its ticket key · process · pr-0007, pr-0009 · C3 + said: "put the ticket key on the first line of the description, the release notes are built from it." — dana, https://github.com/example/widgets/pull/7#pullrequestreview-105 + Recommended: accept + Mark: dana: yes. erin: the release-notes job is being retired next month, so this may not matter by then. diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index e4ed64e..b596423 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -213,6 +213,70 @@ scenarios: expect: ends: success agents: ["source-reader"] + + - id: revise-apply-settle-marks + repo: widgets + setup: + - mkdir -p standards/log/widgets/capture .review/revision + - cp "$SBX_FIXTURES/standard-review-rules.md" standards/review-rules.md + - cp "$SBX_FIXTURES/discuss/ratchet-2026-10.md" "$SBX_FIXTURES/discuss/ratchet-2026-10.clusters.txt" standards/log/widgets/ + - mkdir -p standards/log/widgets/capture/pr-0007 + - cp "$SBX_FIXTURES/discuss/capture/pr-0007/items.json" "$SBX_FIXTURES/discuss/capture/pr-0007/comment-list.json" standards/log/widgets/capture/pr-0007/ + - mkdir -p .review/revision/notes + - cp "$SBX_FIXTURES/discuss/notes/meeting-2026-11-03.md" .review/revision/notes/ + origin: "#65" + why: >- + Runs revise apply's first step on a report the group discussed in its own words: a + rewording on R-004's Mark line, an accept for F-001 only in the pasted meeting notes, and two + people on D-001 with no outcome. The run should settle R-004 to accept with the agreed + words, F-001 to accept from the notes, and D-001 to unclear, show the table, and write + nothing before the person's yes. + turns: + - >- + /lasp-dev:revise apply. Settle the marks from what the group said and show me what you + would write, then stop before writing anything. + must_not_run: ["mcp__*", "Agent", "Task", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] + look_at: >- + A table of R-004, F-001 and D-001: R-004 accept, reworded with "raises on input it cannot + use", resting on erin's Mark line; F-001 accept, resting on the meeting notes; D-001 unclear, + dana for and erin doubting with no outcome, never a plain accept. The report's Mark lines are + unchanged and no diff is written. + verify: + - "grep -q '^ Mark: yes, but word it' standards/log/widgets/ratchet-2026-10.md" + - "grep -q '^ Mark:$' standards/log/widgets/ratchet-2026-10.md" + - "! test -e .review/ratchet-proposal.diff" + expect: + ends: success + says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)F-001[^\n]*accept', '(?i)D-001[^\n]*unclear'] + not_says: ['(?i)D-001[^\n|]*\|\s*`?accept`?\s*\|'] + + - id: revise-question-evidence + repo: widgets + setup: + - mkdir -p standards/log/widgets/capture + - cp "$SBX_FIXTURES/standard-review-rules.md" standards/review-rules.md + - cp "$SBX_FIXTURES/discuss/ratchet-2026-10.md" "$SBX_FIXTURES/discuss/ratchet-2026-10.clusters.txt" standards/log/widgets/ + - mkdir -p standards/log/widgets/capture/pr-0007 + - cp "$SBX_FIXTURES/discuss/capture/pr-0007/items.json" "$SBX_FIXTURES/discuss/capture/pr-0007/comment-list.json" standards/log/widgets/capture/pr-0007/ + origin: "#65" + why: >- + Asks revise how two proposals came about. R-004 traces through its cluster, C1, to alice's + comment on pull request 7 and bob's reply that changed the code; D-001's cluster counts pull + request 9 too, which has no capture, so the answer should say that link is missing rather + than describe it. + turns: + - >- + /lasp-dev:revise How did R-004 come about, and which cases does it rest on? And what does + D-001 rest on? + must_not_run: ["mcp__*", "Edit", "Write", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] + look_at: >- + R-004: cluster C1, one person (alice) on pull request 7, discussion_r101 quoted, the + principle that a silent None becomes a wrong number, bob's reply changing it to raise + ValueError. D-001: dana's review on pull request 7 and pull request 9 counted, with pull + request 9 named as having no capture, so its words cannot be shown. + expect: + ends: success + says: ['discussion_r101', '(?i)pr-0009|pull request 9|#9', '(?i)(no capture|not captured|missing|isn.t captured|no .*capture)'] says: ['(?m)^\W*Excerpt: "', '(?i)not in the file'] - id: revise-prepare-uncaptured diff --git a/skills/revise/SKILL.md b/skills/revise/SKILL.md index 1b77af0..ab2530b 100644 --- a/skills/revise/SKILL.md +++ b/skills/revise/SKILL.md @@ -7,20 +7,22 @@ description: > the code and fixes to documents practice has left behind, for the group to mark, and reports where merged diffs follow or break the rules and the documented practice; `capture` keeps each merged pull request's harvest in the log as it merges, `apply` drafts what it - accepted, and `correct` fixes a misreading in the corpus between revisions. Not for + accepted, settling each proposal's mark from the group's discussion on the report and its pull + request, a question about how a proposal came about is answered from its evidence, and `correct` + fixes a misreading in the corpus between revisions. Not for disagreeing with a rule, which goes through Disagree and the revision. -argument-hint: "[prepare [measures] [Confluence page or Jira epic to add] | capture [YYYY-MM-DD] | apply | correct ]" +argument-hint: "[prepare [measures] [Confluence page or Jira epic to add] | capture [YYYY-MM-DD] | apply | correct | ]" disable-model-invocation: true metadata: author: lasp-dev - version: 2.25.0 + version: 2.26.0 --- # Revise the standard A group session with one driver. The driver runs `prepare` before the meeting, which writes a report of proposals with blank checkboxes; the group marks each one in the meeting; the driver -runs `apply` after it, which drafts the accepted changes as one pull request here and, when the +runs `apply` after it, which settles each proposal's mark from what the group said and drafts the accepted changes as one pull request here and, when the standard lives elsewhere, one in the standard's repository. It runs in the code repository whose standard it revises. Between revisions, `correct` fixes a misreading one person reports, everywhere the corpus carries it. @@ -36,7 +38,7 @@ requests have merged, so `prepare` finds the month already read. | Option | Default | |---|---| -| Mode | `prepare`: read `${CLAUDE_SKILL_DIR}/prepare.md`. `capture`, between revisions: read `${CLAUDE_SKILL_DIR}/capture.md`. `apply`, after the group has marked the report: read `${CLAUDE_SKILL_DIR}/apply.md`. `correct`, when a person says the corpus has something wrong: read `${CLAUDE_SKILL_DIR}/correct.md` | +| Mode | `prepare`: read `${CLAUDE_SKILL_DIR}/prepare.md`. `capture`, between revisions: read `${CLAUDE_SKILL_DIR}/capture.md`. `apply`, after the group has marked the report: read `${CLAUDE_SKILL_DIR}/apply.md`. `correct`, when a person says the corpus has something wrong: read `${CLAUDE_SKILL_DIR}/correct.md`. A question about a report or a proposal, such as how it came about or which cases it rests on, in any mode or alone: read `${CLAUDE_SKILL_DIR}/discuss.md` and answer it, then go on with the mode, if one was named | | Window | From the day after the `window:` line's end in the newest `ratchet-*.md` in any `` of the standard; before the first, from this repository's adoption date ("The standard's repository") | | Measures | Not read. `measures`, or the person asking for them, has `prepare` also read `${CLAUDE_SKILL_DIR}/measures.md`: tickets, escaped defects, build yield and what the build missed. No Dial acts on them except the Ticket candidates Dial, which applies only in a run that reads them | | Page or epic to add | None. A named Confluence page or Jira epic is read once and drafted as a context-entry candidate, marked like any other proposal, within `context/README.md`'s ten-entry cap | @@ -98,17 +100,18 @@ else or invent a term or a decision to fill the gap; `correct` stops there. Each proposal in the report is a checklist line, `- [ ] , exemplar/, question/, context/, ticket/ or doc/> · · · `, its detail beneath opening with a `Recommended:` line. -The group writes its verdict on the proposal's `Mark:` line and ticks its box, one proposal at a -time and never in bulk: `accept`, -`decline: ` or `escalate: `, and, on a proposal the bar held, `accept, override: -`, the only mark that accepts it (`rule-bar.md` "Overrides"), except one held `no -override`, which no mark accepts; on a change to a live data -convention, `accept, owner: `, the only mark that accepts it, naming its owner; and on a -`question/`, `answer: `, which settles it as a provisional decision. -A `Recommended:` line is the -report's, never a mark. A proposal whose `Mark:` line is empty is deferred. A draft pull request `prepare` opens on the -report's branch is a place to discuss proposals; a verdict given there counts once it is on the -`Mark:` line, which stays the record `apply` reads. A new or reworded rule is worded as `${CLAUDE_SKILL_DIR}/../writing/SKILL.md` +The group discusses each proposal in its own words, on its `Mark:` line, in review comments and +replies on the draft pull request `prepare` opens on the report's branch, or in notes the driver +pastes, and asks about any proposal there or in a session, as `discuss.md` answers. `apply` reads +all of it and settles each proposal to one mark, which the driver confirms before it is written +on the `Mark:` line, the box ticked for an accepted one: `accept`, `accept, reworded: ""`, `decline: ` or `escalate: `, and, on a proposal the bar held, `accept, +override: `, the only mark that accepts it (`rule-bar.md` "Overrides"), except one held +`no override`, which no mark accepts; on a change to a live data convention, `accept, owner: +`, the only mark that accepts it, naming its owner; and on a `question/`, `answer: `, which settles it as a provisional decision. A `Recommended:` line is the +report's, never a mark, and a proposal nobody spoke to is deferred. Each proposal is settled on +its own, never in bulk. A new or reworded rule is worded as `${CLAUDE_SKILL_DIR}/../writing/SKILL.md` "Conventions" says under Rule wording: a principle with no case in it. ## Files @@ -119,7 +122,10 @@ In ``: guidance items and analysis, and `capture/covered.txt`, the days captured, from `capture` through `scripts/capture.py`; read by `prepare` step 1. - `ratchet-YYYY-MM.md`, the report, first line `window: ..`, as - `${CLAUDE_SKILL_DIR}/outline.md` outlines it; marked by the group, read by `apply`. + `${CLAUDE_SKILL_DIR}/outline.md` outlines it; discussed by the group, its marks settled and + written by `apply`. +- `ratchet-YYYY-MM.clusters.txt`, beside its report, the `guidance_clusters.py` printout the + report's clusters came from, from `prepare` step 10; read by `discuss.md`. - `correction-YYYY-MM-DD-.yaml` and `note-YYYY-MM-DD-.yaml`, from `correct`. In `` and ``, from `apply`: `.md` entries, `R-NNN`, `D-NNN`, `A-NNN` or @@ -192,7 +198,8 @@ Edit this repository's files, `standards/`, the instruction file and the templat ``'s records and report aside when the standard is this repository's own: every other change to them goes into `.review/ratchet-proposal.diff` for the driver to apply. Edit the plugin's `skills/` and `agents/` text. Open this -repository's pull request, or the standard's repository's without a yes. Propose a rule without the +repository's pull request, or the standard's repository's without a yes. Post a reply on a pull +request without a yes. Propose a rule without the evidence its Dial requires. Write to Confluence or Jira; an accepted ticket reaches Jira only through `ticket-draft`, on the person's yes. Delete a rule's reasoning without archiving it first. Write a rule, a rewording or a promotion a gate of `rule-bar.md` fails, unless diff --git a/skills/revise/apply.md b/skills/revise/apply.md index 79d9275..c07c5e6 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -4,11 +4,34 @@ elsewhere, the commit in a clone of its repository; ``, ``, `` and `` are where `revise`'s "The standard's repository" puts them. -Read the marked report first: in a clone, from the branch's head after `git -C pull` when +Read the report first: in a clone, from the branch's head after `git -C pull` when the branch was pushed; with no clone, from `` here. A report with no proposal has nothing to mark: go to step 2, which writes its held -candidates. A report with proposals and every `Mark:` line still empty is not marked yet, or the -meeting deferred everything: stop, there is nothing to write, and name the report's path and, -in a clone, its branch. Then, by mark: +candidates. + +**Settle the marks.** The group decides in its own words, wherever it discussed: on a proposal's +`Mark:` line, in the draft pull request `prepare` opened on the report's branch, found as step 2 +finds it and read through `using-github` (each review comment and its replies, by the line it is +on, the reviews' bodies and the conversation comments), and in notes the driver pastes into +`.review/revision/notes/` since `prepare`. Read them all, then settle each proposal to one mark: +`accept`, `accept, reworded: ""`, `decline: `, `escalate: `, +`accept, override: `, `accept, owner: `, `answer: `, or blank, deferred, +with the rules below on which mark a proposal can take; or `unclear: `, when what was said +does not settle it, such as two people disagreeing with no outcome, or an accept on a held +proposal with no reason the discussion gives. Whose words count is `pr-review`'s "Whose reply +counts", read for the group rather than an author: a person's, and an agent's reply only as its +poster's when its attribution says a person read it; a mark already in the fixed form stands +unless a later word from the group changes it. A question a comment asks is answered as +`/skills/revise/discuss.md` says, and settles nothing on its own. + +Show a table of every proposal in the report's order: its id, the mark settled, and what it +rests on, the comment's link or the `Mark:` line, quoted where short. Then ask: **Write these +marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and +the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is +written, or left blank, deferred, on their word. On the yes, write each mark on its proposal's +`Mark:` line and tick the box of each accepted one, the report being the record of what the group +decided; nothing below reads the threads again. Every `Mark:` line empty after that, the meeting +having deferred everything: stop, there is nothing to write, and name the report's path and, in +a clone, its branch. Then, by mark: - A proposal not marked `accept` gets no archive entry, no diff line of its own and no ticket. A declined, escalated or blank admission, the last one the meeting deferred, returns to the held candidates with its evidence and its quotes, a `question/` included, with `kind: @@ -19,6 +42,9 @@ in a clone, its branch. Then, by mark: not so marked returns to the held candidates; a held rewording, promotion or broadening not so marked leaves its rule as it stands. A held proposal marked a plain `accept` stops `apply`, naming it, until its mark carries the reason. +- A proposal marked `accept, reworded: ""` is drafted with those words in place of its + proposed wording, or its `entry:` lines', and is named in the lint like any other change, so + the bar judges the words the group agreed. 1. **Archive every rule that leaves.** For each rule graduating, retiring, expiring, demoted to the prose tier or rewritten, write all of these: diff --git a/skills/revise/discuss.md b/skills/revise/discuss.md new file mode 100644 index 0000000..a2d6641 --- /dev/null +++ b/skills/revise/discuss.md @@ -0,0 +1,46 @@ +# Questions about the report + +`revise`'s answer to a question about a report or one of its proposals: how a proposal came +about, which cases it rests on, why the bar held it, what a person meant. It is asked at any +time, before the meeting, in it or after, with `/lasp-dev:revise `, or in `apply` +when a comment asks one, and nothing in the report changes for it. `` and +`` are where `revise`'s "The standard's repository" puts them; the report is the +newest `ratchet-*.md` in `` unless the question names another. + +## Where each part of the evidence is + +Answer from these files, in this order, and from nothing remembered: + +- **The proposal in the report:** its id, its Dial, its evidence pointers, its `said:` lines with + the comments' URLs, its `Recommended:` line, and its `scope:`, `example:`, `bar:` and `probe:` + lines; the Head's counts. +- **Its cluster:** `/ratchet-YYYY-MM.clusters.txt`, the `guidance_clusters.py` printout the + report was written from, beside the report. A proposal from a cluster names it, `C`, among its + evidence pointers; its `cluster`, `prs`, `people`, `asked`, `said`, `ask`, `drafted` and `note` + lines give the principle, the pull requests and people counted, and the clearest quotes. +- **Each case:** `/capture/pr-NNNN/`, one per pull request the cluster or the evidence names: + `items.json`, every guidance item read from it with its verbatim quote, generality, kind and + principle; `comment-list.json`, every comment with its author, authorship and thread, so a quote + is read in its thread; `analysis.md`, the analyst's block; `body.md` and `pr.json`, the pull + request itself. A record, `/pr-NNNN.yaml`, gives a rule's findings and their verdicts. +- **The rule or entry it changes:** its entry in ``, and the ledger's + "Candidates below the criteria" for a held candidate's earlier evidence and `prs:` line. +- **The bar and the Dials:** `/standards/rule-bar.md` and `dials.md`, for why a + proposal was held or admitted. + +Answer in a few lines, the finding first: what the proposal rests on, then each case that bears +on the question with its pull request and the comment's URL, quoting the words it rests on as a +`said:` line does. Say which evidence was a person's and which an agent's, as the report marks it. +Where a link in the chain is missing, such as a report from before the clusters file was kept or +a pull request with no capture, say which file is missing and answer from what remains, never +filling the gap. When the evidence does not bear the proposal out, such as a quote that is one +pull request's case or a cluster joining two principles, say so: it is a reason the group may +decline or reword it. + +## A question asked on the pull request + +When the question is a comment on the draft pull request `prepare` opened, and the driver asks for +an answer there, read it and its thread through `using-github`, answer it as above, and show the +reply as it will be posted, with the attribution line `/skills/pr-review/comment-style.md`, "AI assistance level", sets at the level the person +gives. Post it only on the person's yes, as a reply in that thread. diff --git a/skills/revise/outline.md b/skills/revise/outline.md index 510e15a..e6cdccf 100644 --- a/skills/revise/outline.md +++ b/skills/revise/outline.md @@ -2,7 +2,7 @@ The shape of `ratchet-YYYY-MM.md`. The meeting gives it a thirty-minute slot: the driver reads the head, then the overview, then each proposal aloud in the overview's order, and the group -marks it. Everything under "Reference" is +decides on it, in its own words, which `apply` settles into the `Mark:` line. Everything under "Reference" is read before the meeting, or when a proposal points to it, never aloud. A section with nothing in it reads "none". The step that fills each section is `prepare.md`'s. @@ -102,6 +102,8 @@ disagreements shared; rules held provisional for breadth and what would release ### Not read step 2: unanalysed pull requests, Not read: ids, unread guidance slices ``` +A proposal's `` are its pull requests, `pr-NNNN`, and, for one from a +cluster, the cluster, `C`, as `ratchet-YYYY-MM.clusters.txt` beside the report prints it. Every quote in the report, a drafted entry's included, is a `said:` line of the form above, with the comment's URL or the pasted note's file, so `scripts/quote_check.py` can find it; a person's words given any other way are paraphrase. A held proposal is never recommended a plain `accept`. The meeting takes the proposals in the diff --git a/skills/revise/prepare.md b/skills/revise/prepare.md index 82da15f..53bfe14 100644 --- a/skills/revise/prepare.md +++ b/skills/revise/prepare.md @@ -361,7 +361,11 @@ record `sources`. and where `apply` would change it: the standard's repository, this repository's diff, Jira through `ticket-draft` for a `ticket/`, or, for a `question/`, the standard's repository once answered. Write it as `outline.md` gives it, in the words `writing` sets out, every checkbox blank; no - diff and no archive entry. Then check its quotes: run `python3 "`, among its evidence + pointers, and the `guidance_clusters.py` printout the report was written from is kept beside + it as `/ratchet-YYYY-MM.clusters.txt`, written as printed, so a later question about how a + proposal came about traces it to its cases (`discuss.md`); with no cluster, there is no such + file. Then check its quotes: run `python3 "/scripts/quote_check.py" --comments /threads/pr-*/ and each covered repository's /capture/pr-*/, the last holding earlier windows' comments for the @@ -376,8 +380,8 @@ record `sources`. capture holds: it stays, and the check's first line goes into "Head" as printed. Ask, so `apply` can check the branch out: **Commit and push the branch** (Recommended: the group marks it on GitHub or in any checkout), **Commit only**, naming the branch and any clone's path, **Stop**, nothing committed and any clone's path named, - or **Let's discuss**. Either commit answer commits the report in `` with the records and - `/capture/`, in + or **Let's discuss**. Either commit answer commits the report in `` with the records, + `/capture/` and the report's clusters file, in the clone; with no clone, nothing is committed and the question is not asked: the report and the records stay in `` here, as `revise`'s "The standard's repository" says, and no draft pull request is offered. With a clone, in the same widget, a second question, which counts only with **Commit and push the branch**: open the @@ -388,9 +392,9 @@ record `sources`. `Standards revision · · YYYY-MM`, and the body as they will be sent: the report's path and the records' count; that no rule, decision, term or context entry has changed, the diff being the month's records and the report; that a proposal is - discussed in a review comment on its line, and its verdict counts once it is on the proposal's - `Mark:` line in the report, written there on the branch or copied in by the driver, since - `apply` reads the report, not the threads; and the attribution line `pr-create` appends, at + discussed in review comments and replies on its line, in the group's own words, where anyone + can also ask how a proposal came about; that `apply` reads the discussion and settles each + proposal's mark with the driver; and the attribution line `pr-create` appends, at the level `/skills/pr-review/comment-style.md`, "AI assistance level", sets. Only an answer that names the pull request opens it. @@ -410,10 +414,9 @@ record `sources`. - **What it did not change**: no rule, decision, term, context entry or document, here or in the standard; `apply` changes them from the marks, and commits, pushes or opens a pull request only on its own yes. - - **Next**: one line on how the group marks: in the meeting the driver writes each verdict on - its proposal's `Mark:` line and ticks the box, on that branch, on GitHub or in any checkout, - and commits the marks there, copying in any verdict given in the draft pull request's - review; when the branch - was not pushed, where it was committed; with no clone, in the report in this working tree, - left uncommitted for `apply`. When step 2 wrote drafts, a last line says how many, and that + - **Next**: one line on how the group decides: it discusses each proposal in its own words, in + the draft pull request's review or on the proposal's `Mark:` line, on that branch or, when + the branch was not pushed, where it was committed, or with no clone in the report in this + working tree, left uncommitted; `/lasp-dev:revise ` answers how a proposal came + about; `apply` settles the marks from all of it with the driver. When step 2 wrote drafts, a last line says how many, and that `/lasp-dev:skill-feedback ` files each, shown whole before its yes. diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index 0b5b2dc..3bf69ae 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -449,7 +449,7 @@ def test_a_line_separator_inside_a_string_is_not_a_line_break(self): def test_every_generic_scenario_is_well_formed_and_runs_in_the_fixture(self): known = run.scenarios() - self.assertEqual(len(known), 34) + self.assertEqual(len(known), 36) well_formed(self, known, {"widgets"}) def test_every_fixture_a_scenario_copies_is_in_the_pack(self): From 7ae3e983dfd271ba8b6af4f5b6e9c0ecdd833c88 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:10:43 -0700 Subject: [PATCH 02/12] fix(revise): read the draft pull request only with a clone; keep the page scenario's says lines with it Co-Authored-By: Claude Opus 5.5 --- sandbox/scenarios.yaml | 2 +- skills/revise/apply.md | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index b596423..92e64e0 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -213,6 +213,7 @@ scenarios: expect: ends: success agents: ["source-reader"] + says: ['(?m)^\W*Excerpt: "', '(?i)not in the file'] - id: revise-apply-settle-marks repo: widgets @@ -277,7 +278,6 @@ scenarios: expect: ends: success says: ['discussion_r101', '(?i)pr-0009|pull request 9|#9', '(?i)(no capture|not captured|missing|isn.t captured|no .*capture)'] - says: ['(?m)^\W*Excerpt: "', '(?i)not in the file'] - id: revise-prepare-uncaptured repo: widgets diff --git a/skills/revise/apply.md b/skills/revise/apply.md index c07c5e6..a906212 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -9,9 +9,10 @@ the branch was pushed; with no clone, from `` here. A report with no propos candidates. **Settle the marks.** The group decides in its own words, wherever it discussed: on a proposal's -`Mark:` line, in the draft pull request `prepare` opened on the report's branch, found as step 2 -finds it and read through `using-github` (each review comment and its replies, by the line it is -on, the reviews' bodies and the conversation comments), and in notes the driver pastes into +`Mark:` line; with a clone, in the draft pull request `prepare` may have opened on the report's +branch, found as step 2 finds it and read through `using-github` (each review comment and its +replies, by the line it is on, the reviews' bodies and the conversation comments), a server that +cannot be reached said in one line and the rest read; and in notes the driver pastes into `.review/revision/notes/` since `prepare`. Read them all, then settle each proposal to one mark: `accept`, `accept, reworded: ""`, `decline: `, `escalate: `, `accept, override: `, `accept, owner: `, `answer: `, or blank, deferred, From 651f0cb9e4b21eb7197e5ab822070817fe3091d6 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:18:06 -0700 Subject: [PATCH 03/12] fix(revise): review round 1: meeting notes apart from asks, the report read from its branch, people-only and conflicting sources unclear, composite marks, clusters rerun at step 10 Co-Authored-By: Claude Opus 5.5 --- GUIDE.md | 2 +- README.md | 1 + docs/design.md | 4 +- .../fixture/discuss/notes/chat-2026-10-20.md | 3 + .../discuss/notes/meeting-2026-11-03.md | 2 +- sandbox/fixture/discuss/ratchet-2026-10.md | 11 ++- sandbox/scenarios.yaml | 36 +++++----- skills/revise/SKILL.md | 23 ++++--- skills/revise/apply.md | 69 +++++++++++-------- skills/revise/discuss.md | 14 +++- skills/revise/outline.md | 2 +- skills/revise/prepare.md | 8 +-- 12 files changed, 107 insertions(+), 68 deletions(-) create mode 100644 sandbox/fixture/discuss/notes/chat-2026-10-20.md diff --git a/GUIDE.md b/GUIDE.md index e37f906..86d7fb2 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -135,7 +135,7 @@ stateDiagram-v2 Figure 2. How a rule is admitted and retired. Every transition happens at a revision, with a person agreeing; the labels name the rows of `standards/dials.md`. A graduated rule is enforced by a lint, pre-commit or CI check, and its wording may stay as prose for what the check cannot judge, as R-012's does. -The driver runs `/lasp-dev:revise` before the monthly group meeting, or once ten pull requests have merged since the last report; nothing schedules it. Between meetings, the driver runs `/lasp-dev:revise capture` weekly: it harvests the window's merged pull requests into the standard's log and reads those new or changed since the last capture, so the monthly run reads them from there instead of starting dozens of agents at once. It rebuilds each merged pull request's record, and `thread-analyst` reads every thread, reviewer's summary and conversation comment; with `measures`, it also reads the tickets those pull requests name, the escaped defects and the build yield. The report proposes to keep, reword, retire or graduate each rule and to admit each candidate that meets the Dials, each to its destination: a rule, agent guidance, a data convention, a decision, a lint setting or a question for the group; and it says which numbers are counted and which are the analyst's reading. It also reports, from a sweep of the window's merged diffs, where the code follows or breaks the rules and the documented practice, and proposes a ticket for a problem still on `main` and a fix for a document the merged work has left behind. It opens with an overview of every proposal by priority and impact; the group takes them in that order and discusses each in its own words, on the proposal's `Mark:` line or in review comments and replies on the draft pull request `prepare` can open, and leaves the rest alone, deferred. Anyone can ask how a proposal came about, with `/lasp-dev:revise ` or in a comment the driver has answered, and the answer cites the cases it rests on. `revise apply` first reads the discussion and settles each proposal's mark, accept, accept with the agreed wording, decline or escalate, a change to a data convention `accept, owner: ` and a question `answer: `, which the driver confirms before it is written; then it drafts only what was accepted, as reviewed pull requests, the standard's repository first when the standard lives elsewhere, then this repository's `standards/` and documents, and hands each accepted ticket to `ticket-draft` one at a time. The tiers, graduation and the archive are in the README's "How the corpus grows". +The driver runs `/lasp-dev:revise` before the monthly group meeting, or once ten pull requests have merged since the last report; nothing schedules it. Between meetings, the driver runs `/lasp-dev:revise capture` weekly: it harvests the window's merged pull requests into the standard's log and reads those new or changed since the last capture, so the monthly run reads them from there instead of starting dozens of agents at once. It rebuilds each merged pull request's record, and `thread-analyst` reads every thread, reviewer's summary and conversation comment; with `measures`, it also reads the tickets those pull requests name, the escaped defects and the build yield. The report proposes to keep, reword, retire or graduate each rule and to admit each candidate that meets the Dials, each to its destination: a rule, agent guidance, a data convention, a decision, a lint setting or a question for the group; and it says which numbers are counted and which are the analyst's reading. It also reports, from a sweep of the window's merged diffs, where the code follows or breaks the rules and the documented practice, and proposes a ticket for a problem still on `main` and a fix for a document the merged work has left behind. It opens with an overview of every proposal by priority and impact; the group takes them in that order and discusses each in its own words, on the proposal's `Mark:` line, in review comments and replies on the draft pull request `prepare` can open when the standard lives in another repository, or in meeting notes the driver pastes into `.review/revision/meeting/`, and leaves the rest alone, deferred. Anyone can ask how a proposal came about, with `/lasp-dev:revise ` or in a comment the driver has answered, and the answer cites the cases it rests on. `revise apply` first reads the discussion and settles each proposal's mark, accept, accept with the agreed wording, decline or escalate, a change to a data convention `accept, owner: ` and a question `answer: `, which the driver confirms before it is written; then it drafts only what was accepted, as reviewed pull requests, the standard's repository first when the standard lives elsewhere, then this repository's `standards/` and documents, and hands each accepted ticket to `ticket-draft` one at a time. The tiers, graduation and the archive are in the README's "How the corpus grows". A new or reworded rule is first held to `standards/rule-bar.md`, its example resolving in merged code. diff --git a/README.md b/README.md index bb90e7a..fac4fb6 100644 --- a/README.md +++ b/README.md @@ -793,6 +793,7 @@ session-start line or `work` says something unexpected, these files are what it | `revision/drift.txt` | `revise` `prepare` step 2, through `scripts/drift_checks.py`: the scripted claims' `claim` and `break` lines | `prepare` steps 2 and 6, for "Practice" and `ticket/` and `doc/` proposals | Rewritten at each revision | | `revision/state.json` | `revise` `prepare`, through `scripts/revise_state.py`: each stage done or skipped, with why | `prepare` step 10, which writes the report only once every stage is recorded | `revise_state.py start`, at the next `prepare` | | `revision/notes/` | The driver, before `revise` `prepare`: meeting or chat notes, pasted | `revise`, read as asks at `prepare` step 5, and by `scripts/quote_check.py` for the report's pasted quotes | Not cleared by the tools | +| `revision/meeting/` | The driver, before `revise` `apply`: the revision meeting's notes, or a pull request's threads, pasted | `revise` `apply`, when it settles each proposal's mark | Not cleared by the tools | | `revision/spot-check.md` | `revise` `prepare` step 2: one line per guidance item `scripts/spot_check.py` sampled, right or wrong | `scripts/spot_check.py tally`, for the report's Head | Rewritten at each revision | | `revision/tickets/` | `revise` `apply`: each accepted `ticket/` proposal as a prompt (`-.md`) | `ticket-draft`, given a prompt's path, one at a time | Replaced at each `apply` | | `revision/feedback/` | `revise`: a `skill-feedback` draft per procedure finding, from `prepare` step 2 and `correct` step 2 | `skill-feedback`, given a draft's path | Not cleared by the tools | diff --git a/docs/design.md b/docs/design.md index f73ed63..d290593 100644 --- a/docs/design.md +++ b/docs/design.md @@ -99,9 +99,9 @@ Libera's standard. **The ticket comes before the code.** The most useful review comments were often design questions asked after the code was written, so `ticket-draft` asks them first. It asks them against a draft: placement and a rough size come back first, so a duplicate or a split is decided before any question, then the draft with a recommended answer on each Open Question, since walking eight rounds of questions before any ticket text asked the person to hold the ticket in their head. Every question it raises is answered, marked open or dropped by a person; an open one goes to the ticket's Open Questions with why it stays open and what it affects, and every later step offers to settle it. A ticket may keep a question open, since an answer invented to pass a readiness gate is worse than an honest open question; the ticket writer judges readiness when moving it to To Do. The agent never invents a component, an epic key or a link target. No question crosses into the build unanswered, but an answer can be provisional: researched and chosen so the work can go on, and flagged to revisit, with why it is not final and what would make it final, since implementation often answers what planning could not. A provisional decision is shown again at the plan and diff gates, listed in the pull request's description and given to the reviewer as a focus point, since it is where the human review needs to look. Making a decision provisional costs a reason, so the flag keeps its meaning. (`ticket-draft` step 6; `using-jira`, "Never"; `GUIDE.md`, "Why".) -**A skill researches and recommends before it asks; the person decides.** Asking for an answer, a verdict or a mark before anything has read the code asks the person to do that research alone, or to guess. So the planner recommends an answer to each question with its evidence and drafts the plan on it, marking the steps that assume it, so another answer is a visible edit rather than a second plan; `pr-fix`'s assessment recommends a disposition for each thread; and the revision report gives each proposal a `Recommended:` line with the evidence that decides it. A recommendation is never an answer: the plan gate asks with it marked, and the revision's checkboxes stay blank until the group marks them. (`.claude/CLAUDE.md`, "How a skill behaves".) +**A skill researches and recommends before it asks; the person decides.** Asking for an answer, a verdict or a mark before anything has read the code asks the person to do that research alone, or to guess. So the planner recommends an answer to each question with its evidence and drafts the plan on it, marking the steps that assume it, so another answer is a visible edit rather than a second plan; `pr-fix`'s assessment recommends a disposition for each thread; and the revision report gives each proposal a `Recommended:` line with the evidence that decides it. A recommendation is never an answer: the plan gate asks with it marked, and the revision's checkboxes stay blank until `apply` writes the marks the group decided, on the driver's yes. (`.claude/CLAUDE.md`, "How a skill behaves".) -**The group decides in its own words; `apply` settles the marks.** A mark in a fixed form had to be typed on the report's line, so a meeting that agreed in a review thread, or agreed to different wording, had its outcome copied by hand or lost. No script parses a mark: `apply` reads the report already, so it reads the discussion too, the `Mark:` lines, the draft pull request's threads and pasted notes, and settles each proposal to one mark, which the driver confirms before it is written on the report's line. The fixed marks stay, as what a proposal settles to, because `apply`'s rules on overrides, owners and held candidates hang on them and the report stays the record of what was decided. A question about how a proposal came about is answered from the report, the clusters printout kept beside it and each pull request's capture, never from memory, so whoever asks, whenever, gets the cases it rests on. (`skills/revise/apply.md`; `skills/revise/discuss.md`.) +**The group decides in its own words; `apply` settles the marks.** A mark in a fixed form had to be typed on the report's line, so a meeting that agreed in a review thread, or agreed to different wording, had its outcome copied by hand or lost. No script parses a mark: `apply` reads the report already, so it reads the discussion too, the `Mark:` lines, the draft pull request's threads and the meeting notes the driver pastes, kept apart from the asks pasted for `prepare` so an ask is never taken for a decision, and settles each proposal to one mark, which the driver confirms before it is written on the report's line. The fixed marks stay, as what a proposal settles to, because `apply`'s rules on overrides, owners and held candidates hang on them and the report stays the record of what was decided. A question about how a proposal came about is answered from the report, the clusters printout kept beside it and each pull request's capture, read from the revision's branch until it merges, never from memory, so whoever asks, whenever, gets the cases it rests on. Settling is conservative: only people's words decide, and sources that disagree, or an accept missing the reason or owner a mark needs, settle to unclear for the driver rather than to a guess. (`skills/revise/apply.md`; `skills/revise/discuss.md`.) **The person steers; the sequence is a default.** A skill that runs a fixed sequence leaves the person a passenger, approving each step in turn. So at a natural decision point a skill says where things stand and asks what the person wants to do, the usual next step marked recommended: after the brief, `implement-change` asks whether to plan the change or build it now, since a small change at standard depth does not need a planner run of a quarter of an hour. The gates that stay are the ones before something hard to undo. The shape of a decision point is written once, in a file every asking skill cites, because `.claude/CLAUDE.md` loads only in this repository, and a review of the skills found about twenty widgets, half offering "Let's discuss", four marking a recommendation and several offering no way to stop. The same file says how a skill writes a file, since a heredoc carrying a ticket's markup stops the person at a permission prompt no skill planned. (`.claude/CLAUDE.md`, "How a skill behaves"; `skills/work/decision-points.md`.) What a skill says when a server it needs is missing or failing is written once too, in `skills/work/servers.md`: a review found about twenty skills each wording it their own way, most naming no fix and some stopping with only the error, so a person who had never set a server up could not tell what was lost or what to do. A person whose server is missing meets that message at whichever skill they run first, so it gives the same four things everywhere: which server, what is lost, whether the run goes on, and how to fix it. diff --git a/sandbox/fixture/discuss/notes/chat-2026-10-20.md b/sandbox/fixture/discuss/notes/chat-2026-10-20.md new file mode 100644 index 0000000..be77465 --- /dev/null +++ b/sandbox/fixture/discuss/notes/chat-2026-10-20.md @@ -0,0 +1,3 @@ +Chat, 20 October + +- erin: R-004 should go, it is one comment on one pull request. diff --git a/sandbox/fixture/discuss/notes/meeting-2026-11-03.md b/sandbox/fixture/discuss/notes/meeting-2026-11-03.md index c3c12a3..1c8ee2a 100644 --- a/sandbox/fixture/discuss/notes/meeting-2026-11-03.md +++ b/sandbox/fixture/discuss/notes/meeting-2026-11-03.md @@ -1,4 +1,4 @@ Revision meeting, 3 November -- F-001: carol walked through it, bob and erin agreed. Take it as written. +- F-002: carol walked through it, bob and erin agreed. Take it as written. - R-004: see erin's note on the report. diff --git a/sandbox/fixture/discuss/ratchet-2026-10.md b/sandbox/fixture/discuss/ratchet-2026-10.md index 5fb264d..0ae35e0 100644 --- a/sandbox/fixture/discuss/ratchet-2026-10.md +++ b/sandbox/fixture/discuss/ratchet-2026-10.md @@ -14,7 +14,16 @@ guidance: 6 comments · 5 items · 4 general, 1 pr-specific · general by kind: Recommended: accept — a person's general statement, changed as asked Mark: yes, but word it "raises on input it cannot use", not "fails loudly"; nobody can test "loudly" — erin ### Data conventions -- [ ] F-001 · A column's name carries its units · source: carol · pr-0007 · C2 +- [ ] F-001 · change · a count of -1 is also the fill value in the parts CSV, not only the inventory CSV · owner: the warehouse service's export specification · pr-0007 + Recommended: escalate: the warehouse service's export specification, section 3 + entry: + ### F-001 · A count of -1 means not counted + + _2026-10_ + > In an inventory or parts CSV, a count of -1 is the fill value for a part not counted that day. Never sum it or treat it as zero. + source: the warehouse service's export specification, section 3 + Mark: makes sense to me, the parts export works the same way — bob +- [ ] F-002 · A column's name carries its units · source: carol · pr-0007 · C2 said: "Units go in the column name so nobody has to guess them." — carol, https://github.com/example/widgets/pull/7#discussion_r104 Recommended: accept Mark: diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 92e64e0..167fa5b 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -220,36 +220,40 @@ scenarios: setup: - mkdir -p standards/log/widgets/capture .review/revision - cp "$SBX_FIXTURES/standard-review-rules.md" standards/review-rules.md + - cp "$SBX_FIXTURES/shim-data-conventions.md" standards/data-conventions.md - cp "$SBX_FIXTURES/discuss/ratchet-2026-10.md" "$SBX_FIXTURES/discuss/ratchet-2026-10.clusters.txt" standards/log/widgets/ - mkdir -p standards/log/widgets/capture/pr-0007 - cp "$SBX_FIXTURES/discuss/capture/pr-0007/items.json" "$SBX_FIXTURES/discuss/capture/pr-0007/comment-list.json" standards/log/widgets/capture/pr-0007/ - - mkdir -p .review/revision/notes - - cp "$SBX_FIXTURES/discuss/notes/meeting-2026-11-03.md" .review/revision/notes/ + - mkdir -p .review/revision/meeting .review/revision/notes + - cp "$SBX_FIXTURES/discuss/notes/meeting-2026-11-03.md" .review/revision/meeting/ + - cp "$SBX_FIXTURES/discuss/notes/chat-2026-10-20.md" .review/revision/notes/ origin: "#65" why: >- Runs revise apply's first step on a report the group discussed in its own words: a - rewording on R-004's Mark line, an accept for F-001 only in the pasted meeting notes, and two - people on D-001 with no outcome. The run should settle R-004 to accept with the agreed - words, F-001 to accept from the notes, and D-001 to unclear, show the table, and write - nothing before the person's yes. + rewording on R-004's Mark line, an accept for F-002 only in the pasted meeting notes, two + people on D-001 with no outcome, an agreement to a change to the live convention F-001 that + names no owner, and a chat note pasted for prepare that wanted R-004 gone. The run should + settle R-004 to accept with the agreed words, F-002 to accept from the meeting notes, D-001 + and F-001 to unclear, ignore the chat note, show the table, and write nothing before the + person's yes. turns: - >- - /lasp-dev:revise apply. Settle the marks from what the group said and show me what you - would write, then stop before writing anything. - must_not_run: ["mcp__*", "Agent", "Task", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] + /lasp-dev:revise apply. The group met on 3 November; settle the marks from what it said. + must_not_run: ["mcp__*", "Agent", "Task", "Edit", "Write", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] look_at: >- - A table of R-004, F-001 and D-001: R-004 accept, reworded with "raises on input it cannot - use", resting on erin's Mark line; F-001 accept, resting on the meeting notes; D-001 unclear, - dana for and erin doubting with no outcome, never a plain accept. The report's Mark lines are - unchanged and no diff is written. + A table of R-004, F-001, F-002 and D-001: R-004 accept, reworded with "raises on input it + cannot use", resting on erin's Mark line, not declined from the chat note; F-002 accept, + resting on the meeting notes; F-001 unclear, no owner named; D-001 unclear, dana for and erin + doubting with no outcome, never a plain accept. The report's Mark lines are unchanged and no + diff is written. verify: - "grep -q '^ Mark: yes, but word it' standards/log/widgets/ratchet-2026-10.md" - "grep -q '^ Mark:$' standards/log/widgets/ratchet-2026-10.md" - "! test -e .review/ratchet-proposal.diff" expect: ends: success - says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)F-001[^\n]*accept', '(?i)D-001[^\n]*unclear'] - not_says: ['(?i)D-001[^\n|]*\|\s*`?accept`?\s*\|'] + says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)F-002\W*\|\W*accept\b(?!ed)', '(?i)F-001[^\n]*unclear', '(?i)D-001[^\n]*unclear'] + not_says: ['(?i)^(?!.*unclear).*D-001[^\n]*\baccept\b(?!ed|,? (reworded|owner|override))', '(?i)^(?!.*unclear).*F-001[^\n]*\baccept\b(?!ed)', '(?i)R-004[^\n]*decline'] - id: revise-question-evidence repo: widgets @@ -277,7 +281,7 @@ scenarios: request 9 named as having no capture, so its words cannot be shown. expect: ends: success - says: ['discussion_r101', '(?i)pr-0009|pull request 9|#9', '(?i)(no capture|not captured|missing|isn.t captured|no .*capture)'] + says: ['discussion_r101', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (check|verify|read))|(no capture|not captured|missing|no `?capture)[^\n]*(pr-0009|pull request 9|#9)'] - id: revise-prepare-uncaptured repo: widgets diff --git a/skills/revise/SKILL.md b/skills/revise/SKILL.md index ab2530b..f1beaa6 100644 --- a/skills/revise/SKILL.md +++ b/skills/revise/SKILL.md @@ -4,12 +4,11 @@ description: > Prepares the monthly standards revision: from merged pull requests' review records and what reviewers said, proposes rules to retire, reword, graduate or add, agent guidance, data conventions, decisions, lint settings and questions for the group, tickets for problems still in - the code and fixes to documents practice has left behind, for the group to mark, and reports where merged diffs + the code and fixes to documents practice has left behind, for the group to decide on, and reports where merged diffs follow or break the rules and the documented practice; `capture` keeps each merged pull - request's harvest in the log as it merges, `apply` drafts what it - accepted, settling each proposal's mark from the group's discussion on the report and its pull - request, a question about how a proposal came about is answered from its evidence, and `correct` - fixes a misreading in the corpus between revisions. Not for + request's harvest in the log as it merges, `apply` settles each proposal's mark from the + group's discussion and drafts what it accepted, and `correct` fixes a misreading in the corpus + between revisions; a question about how a proposal came about is answered from its evidence. Not for disagreeing with a rule, which goes through Disagree and the revision. argument-hint: "[prepare [measures] [Confluence page or Jira epic to add] | capture [YYYY-MM-DD] | apply | correct | ]" disable-model-invocation: true @@ -21,7 +20,7 @@ metadata: # Revise the standard A group session with one driver. The driver runs `prepare` before the meeting, which writes a -report of proposals with blank checkboxes; the group marks each one in the meeting; the driver +report of proposals with blank checkboxes; the group discusses and decides each one, in the meeting or on the report's pull request; the driver runs `apply` after it, which settles each proposal's mark from what the group said and drafts the accepted changes as one pull request here and, when the standard lives elsewhere, one in the standard's repository. It runs in the code repository whose standard it revises. Between revisions, `correct` fixes a misreading one person reports, everywhere the corpus carries it. @@ -38,7 +37,7 @@ requests have merged, so `prepare` finds the month already read. | Option | Default | |---|---| -| Mode | `prepare`: read `${CLAUDE_SKILL_DIR}/prepare.md`. `capture`, between revisions: read `${CLAUDE_SKILL_DIR}/capture.md`. `apply`, after the group has marked the report: read `${CLAUDE_SKILL_DIR}/apply.md`. `correct`, when a person says the corpus has something wrong: read `${CLAUDE_SKILL_DIR}/correct.md`. A question about a report or a proposal, such as how it came about or which cases it rests on, in any mode or alone: read `${CLAUDE_SKILL_DIR}/discuss.md` and answer it, then go on with the mode, if one was named | +| Mode | `prepare`: read `${CLAUDE_SKILL_DIR}/prepare.md`. `capture`, between revisions: read `${CLAUDE_SKILL_DIR}/capture.md`. `apply`, after the group has decided on the report: read `${CLAUDE_SKILL_DIR}/apply.md`. `correct`, when a person says the corpus has something wrong: read `${CLAUDE_SKILL_DIR}/correct.md`. A question about a report or a proposal, such as how it came about or which cases it rests on, in any mode or alone: read `${CLAUDE_SKILL_DIR}/discuss.md` and answer it, then go on with the mode, if one was named | | Window | From the day after the `window:` line's end in the newest `ratchet-*.md` in any `` of the standard; before the first, from this repository's adoption date ("The standard's repository") | | Measures | Not read. `measures`, or the person asking for them, has `prepare` also read `${CLAUDE_SKILL_DIR}/measures.md`: tickets, escaped defects, build yield and what the build missed. No Dial acts on them except the Ticket candidates Dial, which applies only in a run that reads them | | Page or epic to add | None. A named Confluence page or Jira epic is read once and drafted as a context-entry candidate, marked like any other proposal, within `context/README.md`'s ten-entry cap | @@ -84,9 +83,9 @@ outside the sandbox on the person's yes), on a branch `revision/-YYYY-MM` commit, and push that branch and open its pull request as `using-github` sets out, only on the yes each mode's file asks for. After a push, remove the directory and say where it was; any other answer leaves it in place and names its path. With no clone, `prepare` writes the records and -the report into `` in this repository's working tree, uncommitted, where the group marks the +the report into `` in this repository's working tree, uncommitted, where the group decides on the report and `apply` reads it; every other change to `` goes into this repository's diff, -and the driver commits the records, the report and the diff together in the revision's pull +and the driver commits the records, the report, its clusters file and the diff together in the revision's pull request here. Nothing is written into the installed plugin. "Shared" means the plugin's own `standards/`: `dials.md` and `rule-bar.md`, read from `, exemplar/, question/, context/, ticket/ or doc/> · · · `, its detail beneath opening with a `Recommended:` line. The group discusses each proposal in its own words, on its `Mark:` line, in review comments and -replies on the draft pull request `prepare` opens on the report's branch, or in notes the driver -pastes, and asks about any proposal there or in a session, as `discuss.md` answers. `apply` reads +replies on the draft pull request `prepare` opens on the report's branch when the standard is in a +clone, or in meeting notes the driver pastes into `.review/revision/meeting/`, and asks about any proposal there or in a session, as `discuss.md` answers. `apply` reads all of it and settles each proposal to one mark, which the driver confirms before it is written on the `Mark:` line, the box ticked for an accepted one: `accept`, `accept, reworded: ""`, `decline: ` or `escalate: `, and, on a proposal the bar held, `accept, @@ -154,6 +153,8 @@ In this repository's `.review/`: Review Rules section where a ledger still keeps wording there, the pull request template and the documents an accepted `doc/` proposal names, for the driver to apply and open as a pull request. - `revision/notes/`, meeting or chat notes the driver pastes before `prepare`, read as asks. +- `revision/meeting/`, the revision meeting's notes, or a pull request's threads, the driver pastes + before `apply`, read by `apply` alone when it settles the marks. - `revision/tickets/-.md`, from `apply`: each accepted `ticket/` proposal as a prompt `ticket-draft` takes. - `archive-pages/`, from `apply`: a Confluence page body per archive entry, for a person to diff --git a/skills/revise/apply.md b/skills/revise/apply.md index a906212..c8aa52e 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -1,6 +1,6 @@ # Apply -`revise`'s `apply` mode: from the marked report to the diff here and, when the standard lives +`revise`'s `apply` mode: from the report the group decided on to the diff here and, when the standard lives elsewhere, the commit in a clone of its repository; ``, ``, `` and `` are where `revise`'s "The standard's repository" puts them. @@ -11,41 +11,54 @@ candidates. **Settle the marks.** The group decides in its own words, wherever it discussed: on a proposal's `Mark:` line; with a clone, in the draft pull request `prepare` may have opened on the report's branch, found as step 2 finds it and read through `using-github` (each review comment and its -replies, by the line it is on, the reviews' bodies and the conversation comments), a server that -cannot be reached said in one line and the rest read; and in notes the driver pastes into -`.review/revision/notes/` since `prepare`. Read them all, then settle each proposal to one mark: +replies, by the line it is on, the reviews' bodies and the conversation comments); and in +meeting notes the driver pastes into `.review/revision/meeting/`, which `prepare` never reads, so +an ask pasted for `prepare` into `revision/notes/` is not taken for a decision. When that pull +request exists, or the check for it fails, and its threads cannot be read, stop here as +`/skills/work/servers.md` says, since a verdict given there would be missed; the +person can paste the threads into `revision/meeting/` instead. Read them all, then settle each +proposal to one mark: `accept`, `accept, reworded: ""`, `decline: `, `escalate: `, -`accept, override: `, `accept, owner: `, `answer: `, or blank, deferred, -with the rules below on which mark a proposal can take; or `unclear: `, when what was said -does not settle it, such as two people disagreeing with no outcome, or an accept on a held -proposal with no reason the discussion gives. Whose words count is `pr-review`'s "Whose reply -counts", read for the group rather than an author: a person's, and an agent's reply only as its -poster's when its attribution says a person read it; a mark already in the fixed form stands -unless a later word from the group changes it. A question a comment asks is answered as +`accept, override: `, `accept, owner: `, `answer: `, or blank, deferred; +a held proposal or a change to a live data convention given new words takes both parts, +`accept, reworded: "", override: ` or `accept, reworded: "", owner: `, +and agreed words that hold a double quote write it as a single one. Or `unclear: `, when +what was said does not settle it. + +Only people's words decide: a reply an agent drafted, whatever its attribution, and a bot's are +read for what they say, never as the group's verdict; a person's reply in their own words is read +for what it decides, as `pr-review`'s "Whose reply counts" judges a reply that opens with no +word. A proposal settles to `unclear` when people disagree with no outcome, when its sources +disagree (a `Mark:` line and a thread, or two notes), since neither carries a time to order them, +when an accept on a held proposal gives no reason, and when an accept on a change to a live data +convention names no owner. A question a comment asks is answered as `/skills/revise/discuss.md` says, and settles nothing on its own. Show a table of every proposal in the report's order: its id, the mark settled, and what it -rests on, the comment's link or the `Mark:` line, quoted where short. Then ask: **Write these -marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and -the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is -written, or left blank, deferred, on their word. On the yes, write each mark on its proposal's -`Mark:` line and tick the box of each accepted one, the report being the record of what the group -decided; nothing below reads the threads again. Every `Mark:` line empty after that, the meeting -having deferred everything: stop, there is nothing to write, and name the report's path and, in -a clone, its branch. Then, by mark: +rests on, the comment's link, the `Mark:` line or the note's file, quoted where short; above it, +one line naming each file read from `revision/meeting/`, so a stale one is seen. Then ask: +**Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the +proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before +anything is written, or left blank, deferred, on their word. On the yes, write each mark on its +proposal's `Mark:` line and tick the box of each accepted one, the report being the record of +what the group decided; nothing below reads the discussion again. Every `Mark:` line empty after +that, the meeting having deferred everything: stop, there is nothing to write, and name the +report's path and, in a clone, its branch. Then, by mark: - A proposal not marked `accept` gets no archive entry, no diff line of its own and no ticket. A declined, escalated or blank admission, the last one the meeting deferred, returns to the held candidates with its evidence and its quotes, a `question/` included, with `kind: open-question`; one marked `answer:` is settled, and becomes a decision. - A held proposal whose held line says `no override` returns to the held candidates whatever its mark. -- A proposal held by the bar is accepted only by `accept, override: `. A held admission - not so marked returns to the held candidates; a held rewording, promotion or broadening not so - marked leaves its rule as it stands. A held proposal marked a plain `accept` stops `apply`, - naming it, until its mark carries the reason. -- A proposal marked `accept, reworded: ""` is drafted with those words in place of its - proposed wording, or its `entry:` lines', and is named in the lint like any other change, so - the bar judges the words the group agreed. +- A proposal held by the bar is accepted only by a mark carrying `override: `. A held + admission not so marked returns to the held candidates; a held rewording, promotion or + broadening not so marked leaves its rule as it stands. A held proposal marked a plain `accept`, + or `accept, reworded` with no override, stops `apply`, naming it, until its mark carries the + reason. +- A mark carrying `reworded: ""` drafts those words in place of the rule's proposed + wording, its `> ` lines, or the drafted entry's statement, the rest of its `entry:` lines kept + as they are; it is named in the lint like any other change, so the bar judges the words the + group agreed. 1. **Archive every rule that leaves.** For each rule graduating, retiring, expiring, demoted to the prose tier or rewritten, write all of these: @@ -93,8 +106,8 @@ a clone, its branch. Then, by mark: - `A-NNN` and `F-NNN` entries go at the end of `/agent-guidance.md` and `data-conventions.md`, each file started from `/standards/templates/` when the standard has none, their months the month the entry reaches `main`. A change to a live - `F-NNN` is written only when marked `accept, owner: `, its owner the one named, and - `owner: · ` goes on its `source:` line; marked a plain `accept`, it stops + `F-NNN` is written only when its mark carries `owner: `, its owner the one named, and + `owner: · ` goes on its `source:` line; marked an accept with no owner, it stops `apply`, naming it, as a held proposal does. Whatever the marks, each agent guidance entry the report's "Rules" lists as seen again takes its new `seen` month. - `D-NNN` entries go at the end of `/decisions.md`, and so does each answered diff --git a/skills/revise/discuss.md b/skills/revise/discuss.md index a2d6641..83d4577 100644 --- a/skills/revise/discuss.md +++ b/skills/revise/discuss.md @@ -4,12 +4,20 @@ about, which cases it rests on, why the bar held it, what a person meant. It is asked at any time, before the meeting, in it or after, with `/lasp-dev:revise `, or in `apply` when a comment asks one, and nothing in the report changes for it. `` and -`` are where `revise`'s "The standard's repository" puts them; the report is the -newest `ratchet-*.md` in `` unless the question names another. +`` are where `revise`'s "The standard's repository" puts them. + +**Which report.** With the standard in this repository, the report is in `` here. With it +in another, this month's report is on the revision branch, `revision/-YYYY-MM`, until +`apply` merges it, not on the standard's `main`: read it in the clone `prepare` left when its path +is known, or through `using-github` from that branch, and the standard's `main` only for a month +already merged. The report is the month the question names, or else the newest one found; say +which month and branch it is in the answer's first line. When the month asked about has no +report where it should be, say so and name where it looked, rather than answering from another +month's, whose ids may name other proposals. ## Where each part of the evidence is -Answer from these files, in this order, and from nothing remembered: +Answer from these files, read from the same place as the report, in this order, and from nothing remembered: - **The proposal in the report:** its id, its Dial, its evidence pointers, its `said:` lines with the comments' URLs, its `Recommended:` line, and its `scope:`, `example:`, `bar:` and `probe:` diff --git a/skills/revise/outline.md b/skills/revise/outline.md index e6cdccf..fe30724 100644 --- a/skills/revise/outline.md +++ b/skills/revise/outline.md @@ -47,7 +47,7 @@ quotes: · from comments: · from notes: · unchecked: , quote_ch ### Data conventions - [ ] F-0NN · · source: · | F-0NN · change · · owner: said: … - Recommended: … (a change to a live one: escalate: ; it is accepted only marked accept, owner: ) + Recommended: … (a change to a live one: escalate: ; it is accepted only by a mark carrying owner: ) entry: the drafted entry or the changed one, indented bar: ok | Mark: diff --git a/skills/revise/prepare.md b/skills/revise/prepare.md index 53bfe14..d1281d2 100644 --- a/skills/revise/prepare.md +++ b/skills/revise/prepare.md @@ -362,10 +362,10 @@ record `sources`. through `ticket-draft` for a `ticket/`, or, for a `question/`, the standard's repository once answered. Write it as `outline.md` gives it, in the words `writing` sets out, every checkbox blank; no diff and no archive entry. A proposal from a cluster names it, `C`, among its evidence - pointers, and the `guidance_clusters.py` printout the report was written from is kept beside - it as `/ratchet-YYYY-MM.clusters.txt`, written as printed, so a later question about how a - proposal came about traces it to its cases (`discuss.md`); with no cluster, there is no such - file. Then check its quotes: run `python3 "/ratchet-YYYY-MM.clusters.txt` beside the report, as printed, its + `clusters: 0` line included when there was none, so a later question about how a proposal came + about traces it to its cases (`discuss.md`); without "Guidance", no such file is written. Then check its quotes: run `python3 "/scripts/quote_check.py" --comments /threads/pr-*/ and each covered repository's /capture/pr-*/, the last holding earlier windows' comments for the From b8668b4474585a50970d4d6a141797885f2c2cc0 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:19:12 -0700 Subject: [PATCH 04/12] test(sandbox): match only a settled mark in the settle scenario's not_says; apply looks for no pull request without a clone Co-Authored-By: Claude Opus 5.5 --- sandbox/scenarios.yaml | 2 +- skills/revise/apply.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 167fa5b..98d7f2d 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -253,7 +253,7 @@ scenarios: expect: ends: success says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)F-002\W*\|\W*accept\b(?!ed)', '(?i)F-001[^\n]*unclear', '(?i)D-001[^\n]*unclear'] - not_says: ['(?i)^(?!.*unclear).*D-001[^\n]*\baccept\b(?!ed|,? (reworded|owner|override))', '(?i)^(?!.*unclear).*F-001[^\n]*\baccept\b(?!ed)', '(?i)R-004[^\n]*decline'] + not_says: ['(?i)^\W*D-001\W+accept\b(?!ed)', '(?i)^\W*F-001\W+accept\b(?!ed)', '(?i)^\W*R-004\W+decline'] - id: revise-question-evidence repo: widgets diff --git a/skills/revise/apply.md b/skills/revise/apply.md index c8aa52e..607baa7 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -11,7 +11,8 @@ candidates. **Settle the marks.** The group decides in its own words, wherever it discussed: on a proposal's `Mark:` line; with a clone, in the draft pull request `prepare` may have opened on the report's branch, found as step 2 finds it and read through `using-github` (each review comment and its -replies, by the line it is on, the reviews' bodies and the conversation comments); and in +replies, by the line it is on, the reviews' bodies and the conversation comments), with no clone +there being none to look for; and in meeting notes the driver pastes into `.review/revision/meeting/`, which `prepare` never reads, so an ask pasted for `prepare` into `revision/notes/` is not taken for a decision. When that pull request exists, or the check for it fails, and its threads cannot be read, stop here as From 38254857763c840c58beba825f815435de75501f Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:26:34 -0700 Subject: [PATCH 05/12] fix(revise): review round 2: keep the group's words under a settled mark, ask when threads cannot be read, lint reworded overrides, reworded only where there is wording, other repositories' captures, comments placed by their hunk Co-Authored-By: Claude Opus 5.5 --- GUIDE.md | 2 +- ROADMAP.md | 1 - sandbox/scenarios.yaml | 6 +++--- skills/revise/SKILL.md | 2 +- skills/revise/apply.md | 40 +++++++++++++++++++++++++--------------- skills/revise/discuss.md | 6 ++++-- skills/revise/prepare.md | 2 +- standards/rule-bar.md | 9 +++++---- 8 files changed, 40 insertions(+), 28 deletions(-) diff --git a/GUIDE.md b/GUIDE.md index 86d7fb2..bf02414 100644 --- a/GUIDE.md +++ b/GUIDE.md @@ -209,7 +209,7 @@ Every finding is a proposal. Every count the revision reports is per rule, never - **Corpus:** the shared files in this plugin's `standards/`: the review contract, terminology, cross-repository decisions, context links, the archive and the records. - **Dial:** a named threshold in `standards/dials.md`, such as the Admission Dial; the revision applies no number that is not one. - **Held candidate:** a proposed rule whose evidence does not yet meet the Dials; the revision keeps it and judges it again as evidence accumulates. -- **Rule bar:** `standards/rule-bar.md`, what a new or reworded rule passes before the group marks it, its example resolving in merged code. +- **Rule bar:** `standards/rule-bar.md`, what a new or reworded rule passes before the group decides on it, its example resolving in merged code. - **Driver:** the person who runs the month's revision; the role rotates. - **Tier:** where a rule is enforced: `prose`, the instruction file only, which the reviewer does not check; `reviewer`, a ledger entry the reviewer checks every diff against; `check`, a tool's configuration, with the ledger entry kept as a pointer (the README's "How the corpus grows"). - **Provisional, established, graduated:** a rule's status on its ledger entry. A rule is admitted provisional, becomes established when the Established Dial is met, and is graduated when the revision judges that a check enforces it; one that leaves is retired. diff --git a/ROADMAP.md b/ROADMAP.md index 87cc13f..7ec331c 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -9,7 +9,6 @@ merged pull request is its record. Owners are roles. Status is `planned`, `defer | --- | --- | --- | --- | --- | | Evidence by citation, in place of the record store | Each rule cites the pull requests it fired on, and counts are derived from the distinct citations ([docs/standards.md](docs/standards.md), "Evidence for the revision"). Replaces the review records, which move to the standard's `log//`, and most of `ratchet_counts.py` and `archive_records.py` | plugin maintainer | after the talk | planned | | Other repositories' threads in the revision's analysts | `prepare` reads every covered repository's comments for guidance and clusters them together, but runs `thread-analyst` and the sweep only on this repository, since its `diff:` verdict and the bar's probes read this repository's code | plugin maintainer | a project wants one revision run to draft rules for several repositories | deferred | -| Revision marks read from the pull request | `prepare` can open the report's branch as a draft pull request for discussion, but a verdict counts only on the report's `Mark:` line, so the driver copies verdicts given in review. `apply` could read them from the review threads through the GitHub server, with a rule for whose reply counts as `pr-review`'s "Whose reply counts" has, and write them into the report before acting | plugin maintainer | a revision where copying verdicts from the pull request proves a burden | idea | | A rule ages only by pull requests in its scope | The expiry Dials count checked pull requests, ones a review or a build ran the rules on. Once a family's revision harvests every member repository, a rule scoped to one repository's paths would also age by members' pull requests it could never fire on, so the count should take only the pull requests whose changed files its `scope:` matches; the records do not yet carry a pull request's files | plugin maintainer | with the family harvest | planned | | A held candidate's form in the ledger | No writer says how a held candidate is written below `## Candidates below the criteria`, and a held admission is drafted with its `### R-NNN` heading; `parse_entries` in `ratchet_counts.py`, which `rule_lint.py` shares, reads such a heading anywhere in the file as a live rule. Either the writers state the held form, or `parse_entries` stops at the heading as `standard_file.sh` does. `guidance_clusters.py` reads a held candidate by its `###` heading or the bold title its bullet opens with, the two forms ledgers hold. Its `kind:` and `prs:` lines are now set (`docs/contracts.md`, "The ledger's held-candidates heading"); its heading and the rest of its form are not | plugin maintainer | before a revision holds a candidate it has drafted as a rule | planned | | People's asks as evidence for a live rule | `prepare.md` step 6 turns a candidate a live rule covers into a reword proposal, with no outcome for asks that are only more evidence for the rule. Established counts agreed findings, not asks, so 12 asks for R-002, R-006 and R-015 in the dry run led nowhere; nor does it say whether Admission or Recurring ask governs a rewording | plugin maintainer, with the team | before the second revision | idea | diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 98d7f2d..4df1627 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -252,8 +252,8 @@ scenarios: - "! test -e .review/ratchet-proposal.diff" expect: ends: success - says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)F-002\W*\|\W*accept\b(?!ed)', '(?i)F-001[^\n]*unclear', '(?i)D-001[^\n]*unclear'] - not_says: ['(?i)^\W*D-001\W+accept\b(?!ed)', '(?i)^\W*F-001\W+accept\b(?!ed)', '(?i)^\W*R-004\W+decline'] + says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)^(?!.*unclear)[^\n]*F-002[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)F-001[^\n]*unclear', '(?i)D-001[^\n]*unclear'] + not_says: ['(?i)^(?!.*unclear)[^\n]*D-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^(?!.*unclear)[^\n]*F-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^\W*D-001\W+accept\b(?!ed)', '(?i)^\W*F-001\W+accept\b(?!ed)', '(?i)^[^\n]*R-004[^\n|]*\|[^\n|]*\bdecline'] - id: revise-question-evidence repo: widgets @@ -281,7 +281,7 @@ scenarios: request 9 named as having no capture, so its words cannot be shown. expect: ends: success - says: ['discussion_r101', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (check|verify|read))|(no capture|not captured|missing|no `?capture)[^\n]*(pr-0009|pull request 9|#9)'] + says: ['discussion_r101', 'discussion_r102|ValueError', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (check|verify|read)|absent|does(n.t| not) exist|not (available|present))|(no capture|not captured|missing|no `?capture|absent)[^\n]*(pr-0009|pull request 9|#9)'] - id: revise-prepare-uncaptured repo: widgets diff --git a/skills/revise/SKILL.md b/skills/revise/SKILL.md index f1beaa6..f17bfe7 100644 --- a/skills/revise/SKILL.md +++ b/skills/revise/SKILL.md @@ -204,4 +204,4 @@ request without a yes. Propose a rule without the evidence its Dial requires. Write to Confluence or Jira; an accepted ticket reaches Jira only through `ticket-draft`, on the person's yes. Delete a rule's reasoning without archiving it first. Write a rule, a rewording or a promotion a gate of `rule-bar.md` fails, unless -the group marked it `accept, override: `. +the group's mark carries `override: `. diff --git a/skills/revise/apply.md b/skills/revise/apply.md index 607baa7..82a3d7c 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -11,20 +11,26 @@ candidates. **Settle the marks.** The group decides in its own words, wherever it discussed: on a proposal's `Mark:` line; with a clone, in the draft pull request `prepare` may have opened on the report's branch, found as step 2 finds it and read through `using-github` (each review comment and its -replies, by the line it is on, the reviews' bodies and the conversation comments), with no clone -there being none to look for; and in -meeting notes the driver pastes into `.review/revision/meeting/`, which `prepare` never reads, so -an ask pasted for `prepare` into `revision/notes/` is not taken for a decision. When that pull -request exists, or the check for it fails, and its threads cannot be read, stop here as -`/skills/work/servers.md` says, since a verdict given there would be missed; the -person can paste the threads into `revision/meeting/` instead. Read them all, then settle each -proposal to one mark: +replies, the reviews' bodies and the conversation comments), with no clone, or a branch never +pushed, there being none to look for; and in meeting notes the driver pastes into +`.review/revision/meeting/`, which `prepare` never reads, so an ask pasted for `prepare` into +`revision/notes/` is not taken for a decision. A review comment belongs to the proposal its +`diff_hunk` ends in, read against the report at the comment's own commit, never the head's line +numbers, which later edits shift. When the branch was pushed and its pull request's threads +cannot be read, the server unreachable or the check for the pull request failing, say so as +`/skills/work/servers.md` words it and ask: **Paste the threads** into +`revision/meeting/`, read in their place, **Go on without them**, the table saying the pull +request was not read, or **Stop**. Read them all, then settle each proposal to one mark: `accept`, `accept, reworded: ""`, `decline: `, `escalate: `, `accept, override: `, `accept, owner: `, `answer: `, or blank, deferred; a held proposal or a change to a live data convention given new words takes both parts, `accept, reworded: "", override: ` or `accept, reworded: "", owner: `, -and agreed words that hold a double quote write it as a single one. Or `unclear: `, when -what was said does not settle it. +and agreed words that hold a double quote write it as a single one. `reworded` is for a proposal +with wording: a rule admitted, reworded, promoted or broadened, and an `A-`, `F-` or `D-` entry; +new words for any other settle it to `unclear`. Or `unclear: `, when what was said does not +settle it. A `Mark:` line that already holds a mark in one of these forms with a `discussed:` +line beneath it is one `apply` wrote on an earlier yes: it is settled, and read as the group's +mark, not as discussion. Only people's words decide: a reply an agent drafted, whatever its attribution, and a bot's are read for what they say, never as the group's verdict; a person's reply in their own words is read @@ -41,8 +47,9 @@ one line naming each file read from `revision/meeting/`, so a stale one is seen. **Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is written, or left blank, deferred, on their word. On the yes, write each mark on its -proposal's `Mark:` line and tick the box of each accepted one, the report being the record of -what the group decided; nothing below reads the discussion again. Every `Mark:` line empty after +proposal's `Mark:` line, moving any words the group had written there to a `discussed: ` line beneath it so none is lost, and tick the box of each accepted one, the report being +the record of what the group decided; nothing below reads the discussion again. Every `Mark:` line empty after that, the meeting having deferred everything: stop, there is nothing to write, and name the report's path and, in a clone, its branch. Then, by mark: - A proposal not marked `accept` gets no archive entry, no diff line of its own and no ticket. A @@ -59,7 +66,9 @@ report's path and, in a clone, its branch. Then, by mark: - A mark carrying `reworded: ""` drafts those words in place of the rule's proposed wording, its `> ` lines, or the drafted entry's statement, the rest of its `entry:` lines kept as they are; it is named in the lint like any other change, so the bar judges the words the - group agreed. + group agreed. With an override too, it is still named: the override waives only the gate the + proposal was held on, its lint line going beside the reason, and a failure of any other gate + stops as step 2's lint does. 1. **Archive every rule that leaves.** For each rule graduating, retiring, expiring, demoted to the prose tier or rewritten, write all of these: @@ -127,8 +136,9 @@ report's path and, in a clone, its branch. Then, by mark: under `mktemp -d` and run `rule_lint.py` on them with `--ledger`, `--instructions` only for a linked instruction file, `--repo .`, the `--decisions` and `--profiles` files `prepare.md` step 7 names, the copied `decisions.md` in place of its own, and `--changed` naming each accepted proposal that admits, - rewords, promotes or broadens a rule, less those accepted with an override, whose lint lines - go into the report beside the reason, and less each marked "check from ": that one is + rewords, promotes or broadens a rule, less those accepted with an override and no new words, whose lint lines + go into the report beside the reason (one reworded with an override is named, as "Settle the + marks" says, its held gate's line beside the reason and any other failure stopping), and less each marked "check from ": that one is linted apart on its own `--entry` with `--checks sync,wording,status,fields`, since `scope` and `anchors` read another repository's code, and named in a second run of the applied copy, `--checks budget --changed `, so it cannot carry its repositories over budget; its diff --git a/skills/revise/discuss.md b/skills/revise/discuss.md index 83d4577..4d3acde 100644 --- a/skills/revise/discuss.md +++ b/skills/revise/discuss.md @@ -8,7 +8,7 @@ when a comment asks one, and nothing in the report changes for it. `` and **Which report.** With the standard in this repository, the report is in `` here. With it in another, this month's report is on the revision branch, `revision/-YYYY-MM`, until -`apply` merges it, not on the standard's `main`: read it in the clone `prepare` left when its path +its pull request merges, not on the standard's `main`: read it in the clone `prepare` left when its path is known, or through `using-github` from that branch, and the standard's `main` only for a month already merged. The report is the month the question names, or else the newest one found; say which month and branch it is in the answer's first line. When the month asked about has no @@ -26,7 +26,9 @@ Answer from these files, read from the same place as the report, in this order, report was written from, beside the report. A proposal from a cluster names it, `C`, among its evidence pointers; its `cluster`, `prs`, `people`, `asked`, `said`, `ask`, `drafted` and `note` lines give the principle, the pull requests and people counted, and the clearest quotes. -- **Each case:** `/capture/pr-NNNN/`, one per pull request the cluster or the evidence names: +- **Each case:** `/capture/pr-NNNN/`, one per pull request the cluster or the evidence names, + and for one named `/pr-NNNN` in a standard with `profiles/`, that repository's log, + `/log//capture/pr-NNNN/`: `items.json`, every guidance item read from it with its verbatim quote, generality, kind and principle; `comment-list.json`, every comment with its author, authorship and thread, so a quote is read in its thread; `analysis.md`, the analyst's block; `body.md` and `pr.json`, the pull diff --git a/skills/revise/prepare.md b/skills/revise/prepare.md index d1281d2..1dcab0c 100644 --- a/skills/revise/prepare.md +++ b/skills/revise/prepare.md @@ -378,7 +378,7 @@ record `sources`. offered for commit before. A quote no comment, note or held candidate holds is not the person's, and comes out. An `unchecked` line is a held candidate's quote from a pull request no capture holds: it stays, and the check's first line goes into "Head" as printed. Ask, so `apply` can check the branch out: **Commit and push the - branch** (Recommended: the group marks it on GitHub or in any checkout), **Commit only**, + branch** (Recommended: the group discusses it on GitHub or in any checkout), **Commit only**, naming the branch and any clone's path, **Stop**, nothing committed and any clone's path named, or **Let's discuss**. Either commit answer commits the report in `` with the records, `/capture/` and the report's clusters file, in diff --git a/standards/rule-bar.md b/standards/rule-bar.md index 4d55fe2..6fe7bfa 100644 --- a/standards/rule-bar.md +++ b/standards/rule-bar.md @@ -1,13 +1,13 @@ # The rule bar -What a rule passes before the group marks it. Read by `onboard`'s first rules, `revise` +What a rule passes before the group decides on it. Read by `onboard`'s first rules, `revise` (its `correct` mode included) and the `rule_lint.py` script under `scripts/`; not loaded into reviewers. ## What it is for Every reviewer run loads every rule, and every finding costs a person a reply. A rule written from a thread's words rather than from the code that merged flags the fix the team accepted. The bar is -what a new, reworded, promoted or broadened rule passes before the group marks it; a rule already +what a new, reworded, promoted or broadened rule passes before the group decides on it; a rule already in the ledger is measured against it at each revision and brought up to it when it is next touched. @@ -64,8 +64,9 @@ carries `implements `, is never wrapped either. ## Overrides -The group may accept a proposal a gate fails only by marking it `accept, override: ` in -the report; a plain `accept` on it does not accept it. `apply` carries the reason onto the entry's +The group may accept a proposal a gate fails only by a mark carrying `override: ` in +the report, `accept, override: ` or, with new words, `accept, reworded: "", +override: `; a plain `accept` on it does not accept it. `apply` carries the reason onto the entry's evidence line as `override : `, and the next revision's report lists every override. A held admission the group does not accept this way goes into the ledger's `## Candidates below the criteria` with the check it failed; a held rewording, promotion or From 3c9308e28e38baf57515da6151fbe94a341cfdab Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:29:00 -0700 Subject: [PATCH 06/12] fix(revise): show the settled marks' table before the question, not only inside it Co-Authored-By: Claude Opus 5.5 --- skills/revise/apply.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/skills/revise/apply.md b/skills/revise/apply.md index 82a3d7c..a6ea34f 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -43,7 +43,8 @@ convention names no owner. A question a comment asks is answered as Show a table of every proposal in the report's order: its id, the mark settled, and what it rests on, the comment's link, the `Mark:` line or the note's file, quoted where short; above it, -one line naming each file read from `revision/meeting/`, so a stale one is seen. Then ask: +one line naming each file read from `revision/meeting/`, so a stale one is seen. The table is +written as text before the question, never only inside it. Then ask: **Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is written, or left blank, deferred, on their word. On the yes, write each mark on its From 20abe6a870fedaf75e26e173fbcc01235c069c41 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:31:39 -0700 Subject: [PATCH 07/12] fix(revise): the settled marks' table is written whole before the question Co-Authored-By: Claude Opus 5.5 --- skills/revise/apply.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/skills/revise/apply.md b/skills/revise/apply.md index a6ea34f..f446f3b 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -43,8 +43,10 @@ convention names no owner. A question a comment asks is answered as Show a table of every proposal in the report's order: its id, the mark settled, and what it rests on, the comment's link, the `Mark:` line or the note's file, quoted where short; above it, -one line naming each file read from `revision/meeting/`, so a stale one is seen. The table is -written as text before the question, never only inside it. Then ask: +one line naming each file read from `revision/meeting/`, so a stale one is seen. The table, +every proposal a row, is what the yes approves, so it is written in the message before the +question, whole, however short `decision-points.md` keeps where things stand, and never only +inside the question. Then ask: **Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is written, or left blank, deferred, on their word. On the yes, write each mark on its From 2b8510d9c4e6ad427c64fb2be19a3a4f986594fd Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:36:06 -0700 Subject: [PATCH 08/12] fix(revise): apply writes the settled marks to revision/marks.md before the yes; the scenario checks that file Co-Authored-By: Claude Opus 5.5 --- README.md | 1 + sandbox/scenarios.yaml | 12 +++++++++--- skills/revise/SKILL.md | 2 ++ skills/revise/apply.md | 12 ++++++------ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index fac4fb6..66e74dc 100644 --- a/README.md +++ b/README.md @@ -793,6 +793,7 @@ session-start line or `work` says something unexpected, these files are what it | `revision/drift.txt` | `revise` `prepare` step 2, through `scripts/drift_checks.py`: the scripted claims' `claim` and `break` lines | `prepare` steps 2 and 6, for "Practice" and `ticket/` and `doc/` proposals | Rewritten at each revision | | `revision/state.json` | `revise` `prepare`, through `scripts/revise_state.py`: each stage done or skipped, with why | `prepare` step 10, which writes the report only once every stage is recorded | `revise_state.py start`, at the next `prepare` | | `revision/notes/` | The driver, before `revise` `prepare`: meeting or chat notes, pasted | `revise`, read as asks at `prepare` step 5, and by `scripts/quote_check.py` for the report's pasted quotes | Not cleared by the tools | +| `revision/marks.md` | `revise` `apply`, when it settles the marks | The driver, before the yes that writes them into the report | Rewritten at each `apply` | | `revision/meeting/` | The driver, before `revise` `apply`: the revision meeting's notes, or a pull request's threads, pasted | `revise` `apply`, when it settles each proposal's mark | Not cleared by the tools | | `revision/spot-check.md` | `revise` `prepare` step 2: one line per guidance item `scripts/spot_check.py` sampled, right or wrong | `scripts/spot_check.py tally`, for the report's Head | Rewritten at each revision | | `revision/tickets/` | `revise` `apply`: each accepted `ticket/` proposal as a prompt (`-.md`) | `ticket-draft`, given a prompt's path, one at a time | Replaced at each `apply` | diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 4df1627..aab133c 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -239,9 +239,9 @@ scenarios: turns: - >- /lasp-dev:revise apply. The group met on 3 November; settle the marks from what it said. - must_not_run: ["mcp__*", "Agent", "Task", "Edit", "Write", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] + must_not_run: ["mcp__*", "Agent", "Task", "Edit", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] look_at: >- - A table of R-004, F-001, F-002 and D-001: R-004 accept, reworded with "raises on input it + .review/revision/marks.md and the message before the question: a table of R-004, F-001, F-002 and D-001: R-004 accept, reworded with "raises on input it cannot use", resting on erin's Mark line, not declined from the chat note; F-002 accept, resting on the meeting notes; F-001 unclear, no owner named; D-001 unclear, dana for and erin doubting with no outcome, never a plain accept. The report's Mark lines are unchanged and no @@ -250,9 +250,15 @@ scenarios: - "grep -q '^ Mark: yes, but word it' standards/log/widgets/ratchet-2026-10.md" - "grep -q '^ Mark:$' standards/log/widgets/ratchet-2026-10.md" - "! test -e .review/ratchet-proposal.diff" + - "grep -qiE 'R-004.*accept, reworded' .review/revision/marks.md" + - "grep -qi 'raises on input it cannot use' .review/revision/marks.md" + - "grep -iE 'F-002' .review/revision/marks.md | grep -iv unclear | grep -qiE '\\baccept\\b'" + - "grep -iE 'F-001' .review/revision/marks.md | grep -qi unclear" + - "grep -iE 'D-001' .review/revision/marks.md | grep -qi unclear" + - "! grep -iE '^[^|]*\\|[^|]*R-004[^|]*\\|[^|]*decline' .review/revision/marks.md" expect: ends: success - says: ['(?i)R-004[^\n]*accept, reworded', '(?i)raises on input it cannot use', '(?i)^(?!.*unclear)[^\n]*F-002[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)F-001[^\n]*unclear', '(?i)D-001[^\n]*unclear'] + files: [".review/revision/marks.md"] not_says: ['(?i)^(?!.*unclear)[^\n]*D-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^(?!.*unclear)[^\n]*F-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^\W*D-001\W+accept\b(?!ed)', '(?i)^\W*F-001\W+accept\b(?!ed)', '(?i)^[^\n]*R-004[^\n|]*\|[^\n|]*\bdecline'] - id: revise-question-evidence diff --git a/skills/revise/SKILL.md b/skills/revise/SKILL.md index f17bfe7..53309b8 100644 --- a/skills/revise/SKILL.md +++ b/skills/revise/SKILL.md @@ -153,6 +153,8 @@ In this repository's `.review/`: Review Rules section where a ledger still keeps wording there, the pull request template and the documents an accepted `doc/` proposal names, for the driver to apply and open as a pull request. - `revision/notes/`, meeting or chat notes the driver pastes before `prepare`, read as asks. +- `revision/marks.md`, from `apply`: the mark it settled for each proposal and what it rests on, + shown before the driver's yes. - `revision/meeting/`, the revision meeting's notes, or a pull request's threads, the driver pastes before `apply`, read by `apply` alone when it settles the marks. - `revision/tickets/-.md`, from `apply`: each accepted `ticket/` proposal as a prompt diff --git a/skills/revise/apply.md b/skills/revise/apply.md index f446f3b..24292cd 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -41,12 +41,12 @@ when an accept on a held proposal gives no reason, and when an accept on a chang convention names no owner. A question a comment asks is answered as `/skills/revise/discuss.md` says, and settles nothing on its own. -Show a table of every proposal in the report's order: its id, the mark settled, and what it -rests on, the comment's link, the `Mark:` line or the note's file, quoted where short; above it, -one line naming each file read from `revision/meeting/`, so a stale one is seen. The table, -every proposal a row, is what the yes approves, so it is written in the message before the -question, whole, however short `decision-points.md` keeps where things stand, and never only -inside the question. Then ask: +Write the settled marks to `.review/revision/marks.md`, a table of every proposal in the +report's order: its id, the mark settled, and what it rests on, the comment's link, the `Mark:` +line or the note's file, quoted where short; above it, one line naming each file read from +`revision/meeting/`, so a stale one is seen. It is what the yes approves, so the message before +the question shows the table too, or, when it is long, the unclear and reworded rows with the +file named as a link, as `decision-points.md` says of longer text. Then ask: **Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is written, or left blank, deferred, on their word. On the yes, write each mark on its From ec343d6e4dc17aaf90a7d3a51134515988c0aa12 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:38:13 -0700 Subject: [PATCH 09/12] test(sandbox): drop the settle scenario's line-start not_says, which an option line matched Co-Authored-By: Claude Opus 5.5 --- sandbox/scenarios.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index aab133c..adba5b0 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -259,7 +259,7 @@ scenarios: expect: ends: success files: [".review/revision/marks.md"] - not_says: ['(?i)^(?!.*unclear)[^\n]*D-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^(?!.*unclear)[^\n]*F-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^\W*D-001\W+accept\b(?!ed)', '(?i)^\W*F-001\W+accept\b(?!ed)', '(?i)^[^\n]*R-004[^\n|]*\|[^\n|]*\bdecline'] + not_says: ['(?i)^(?!.*unclear)[^\n]*D-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^(?!.*unclear)[^\n]*F-001[^\n|]*\|[^\n|]*\baccept\b(?!ed)', '(?i)^[^\n]*R-004[^\n|]*\|[^\n|]*\bdecline'] - id: revise-question-evidence repo: widgets From 1ed00a4feb0eaf96458c1cd6ed3e08d2abd477bc Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:50:23 -0700 Subject: [PATCH 10/12] fix(revise): review round 3: reworded holds the whole wording, a settled mark always carries discussed:, override waives only the held gate in rule-bar, a person-read reply counts, README row Co-Authored-By: Claude Opus 5.5 --- README.md | 2 +- sandbox/fixture/discuss/ratchet-2026-10.md | 11 +++++++ sandbox/scenarios.yaml | 9 +++--- skills/revise/apply.md | 37 +++++++++++++--------- skills/revise/prepare.md | 2 +- standards/rule-bar.md | 4 ++- 6 files changed, 43 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 66e74dc..339685e 100644 --- a/README.md +++ b/README.md @@ -510,7 +510,7 @@ Most days need one command, `work`. Started, in the skills table: `typed only` r | | `process-doc` | typed or asked | Write a page on how something is done, a team process, a how-to or a user guide, from your notes and the pages you point at: its purpose, readers and status confirmed with you, the steps and who does each settled with you, in plain language and a shape that suits its kind; publishes on your yes. Its `revise` mode brings a published page in line with how things are done now | | | `drawio` | typed or asked | Draw a context, component, flow or deployment diagram as draw.io XML, checked by `scripts/drawio_check.py`, to paste into a Confluence draw.io macro; `architecture-doc`, `design-doc` and `process-doc` run it for their diagrams | | | `writing` | typed or asked | How tickets, PR bodies, commit bodies and rule wording are structured and worded; run it to review existing text. Never changes a fact | -| | `revise` | typed only | Monthly: prepare the revision report before the meeting, with `measures` adding tickets, escaped defects and build yield; a sweep of the window's merged diffs reports where they follow or break the rules and the documented practice; `apply` drafts what the group accepted; `correct`, between revisions, corrects something the shared standards got wrong, everywhere it is carried, while a disagreement with a rule goes through Disagree and the revision; `capture`, weekly or whenever pull requests have merged, harvests and reads them into the standard's log so `prepare` reads the month from there | +| | `revise` | typed only | Monthly: prepare the revision report before the meeting, with `measures` adding tickets, escaped defects and build yield; a sweep of the window's merged diffs reports where they follow or break the rules and the documented practice; `apply` settles each proposal's mark from the group's discussion on the report, its draft pull request or the meeting notes, and drafts what was accepted; `/lasp-dev:revise ` answers how a proposal came about, from its cases; `correct`, between revisions, corrects something the shared standards got wrong, everywhere it is carried, while a disagreement with a rule goes through Disagree and the revision; `capture`, weekly or whenever pull requests have merged, harvests and reads them into the standard's log so `prepare` reads the month from there | | | `ticket-audit` | typed only | Sweeps across the project's Jira, written to `.review/audit/`, then the actions they propose applied in Jira through one write plan on your yes | | | `skill-feedback` | typed or asked | File a complaint about a skill or agent as a `skill-feedback` issue here, in your words, with the session's last turns attached when you choose, or a draft `revise` wrote for a procedure finding; you see the whole issue before your yes | | | `rebase` | typed or asked | Rebase the current branch onto the default branch, or its pull request's base when you choose it; resolve the conflicts with one objectively clear resolution and stop at any where the two sides differ in intent or approach, showing both with a recommendation; never pushes, and prints the push command, its destination explicit and any `--force-with-lease` pinned to the commit it fetched | diff --git a/sandbox/fixture/discuss/ratchet-2026-10.md b/sandbox/fixture/discuss/ratchet-2026-10.md index 0ae35e0..0dd498b 100644 --- a/sandbox/fixture/discuss/ratchet-2026-10.md +++ b/sandbox/fixture/discuss/ratchet-2026-10.md @@ -12,6 +12,7 @@ guidance: 6 comments · 5 items · 4 general, 1 pr-specific · general by kind: - [ ] R-004 · A reader fails loudly on input it cannot use · single statement · pr-0007 · C1 said: "In general, a reader should fail loudly on input it cannot use" — alice, https://github.com/example/widgets/pull/7#discussion_r101 Recommended: accept — a person's general statement, changed as asked + scope: widgets/**/*.py / example: pr-0007 widgets/rows.py, the empty-table read now raises / bar: ok / probe: fires Mark: yes, but word it "raises on input it cannot use", not "fails loudly"; nobody can test "loudly" — erin ### Data conventions - [ ] F-001 · change · a count of -1 is also the fill value in the parts CSV, not only the inventory CSV · owner: the warehouse service's export specification · pr-0007 @@ -26,9 +27,19 @@ guidance: 6 comments · 5 items · 4 general, 1 pr-specific · general by kind: - [ ] F-002 · A column's name carries its units · source: carol · pr-0007 · C2 said: "Units go in the column name so nobody has to guess them." — carol, https://github.com/example/widgets/pull/7#discussion_r104 Recommended: accept + entry: + ### F-002 · A column's name carries its units + + _2026-11 admitted_ + > A column holding a physical quantity names its unit, as `_`, such as `flux_w_m2`. + source: carol + bar: ok Mark: ### Decisions - [ ] D-001 · A pull request description opens with its ticket key · process · pr-0007, pr-0009 · C3 said: "put the ticket key on the first line of the description, the release notes are built from it." — dana, https://github.com/example/widgets/pull/7#pullrequestreview-105 Recommended: accept + entry: + ### D-001 · A pull request description opens with its ticket key + A pull request's description opens with its ticket key, since the release notes are built from it. Mark: dana: yes. erin: the release-notes job is being retired next month, so this may not matter by then. diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index adba5b0..0ce739d 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -241,8 +241,8 @@ scenarios: /lasp-dev:revise apply. The group met on 3 November; settle the marks from what it said. must_not_run: ["mcp__*", "Agent", "Task", "Edit", "Bash(git push*)", "Bash(git commit*)", "Bash(claude *)"] look_at: >- - .review/revision/marks.md and the message before the question: a table of R-004, F-001, F-002 and D-001: R-004 accept, reworded with "raises on input it - cannot use", resting on erin's Mark line, not declined from the chat note; F-002 accept, + .review/revision/marks.md and the message before the question: a table of R-004, F-001, F-002 and D-001: R-004 accept, reworded with the whole sentence "A reader + raises on input it cannot use", resting on erin's Mark line, not declined from the chat note; F-002 accept, resting on the meeting notes; F-001 unclear, no owner named; D-001 unclear, dana for and erin doubting with no outcome, never a plain accept. The report's Mark lines are unchanged and no diff is written. @@ -251,11 +251,12 @@ scenarios: - "grep -q '^ Mark:$' standards/log/widgets/ratchet-2026-10.md" - "! test -e .review/ratchet-proposal.diff" - "grep -qiE 'R-004.*accept, reworded' .review/revision/marks.md" - - "grep -qi 'raises on input it cannot use' .review/revision/marks.md" + - "grep -qiE 'R-004.*reworded: .?A reader raises on input it cannot use' .review/revision/marks.md" - "grep -iE 'F-002' .review/revision/marks.md | grep -iv unclear | grep -qiE '\\baccept\\b'" - "grep -iE 'F-001' .review/revision/marks.md | grep -qi unclear" - "grep -iE 'D-001' .review/revision/marks.md | grep -qi unclear" - - "! grep -iE '^[^|]*\\|[^|]*R-004[^|]*\\|[^|]*decline' .review/revision/marks.md" + - "! grep -iE '\\| *\\**`?(D-001|F-001)`?\\** *\\| *\\**`?accept' .review/revision/marks.md" + - "! grep -iE '\\| *\\**`?R-004`?\\** *\\| *\\**`?decline' .review/revision/marks.md" expect: ends: success files: [".review/revision/marks.md"] diff --git a/skills/revise/apply.md b/skills/revise/apply.md index 24292cd..f55b8a3 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -25,17 +25,21 @@ request was not read, or **Stop**. Read them all, then settle each proposal to o `accept, override: `, `accept, owner: `, `answer: `, or blank, deferred; a held proposal or a change to a live data convention given new words takes both parts, `accept, reworded: "", override: ` or `accept, reworded: "", owner: `, -and agreed words that hold a double quote write it as a single one. `reworded` is for a proposal +and agreed words that hold a double quote write it as a single one. A `reworded:` mark holds the +whole new wording, the agreed change applied to the proposal's text, never only the phrase that +changed. `reworded` is for a proposal with wording: a rule admitted, reworded, promoted or broadened, and an `A-`, `F-` or `D-` entry; new words for any other settle it to `unclear`. Or `unclear: `, when what was said does not -settle it. A `Mark:` line that already holds a mark in one of these forms with a `discussed:` -line beneath it is one `apply` wrote on an earlier yes: it is settled, and read as the group's -mark, not as discussion. +settle it. A `Mark:` line with a `discussed:` line beneath it is one `apply` wrote on an earlier +yes, blank ones included: it is settled, read as the group's mark or as deferred, not as +discussion. -Only people's words decide: a reply an agent drafted, whatever its attribution, and a bot's are -read for what they say, never as the group's verdict; a person's reply in their own words is read -for what it decides, as `pr-review`'s "Whose reply counts" judges a reply that opens with no -word. A proposal settles to `unclear` when people disagree with no outcome, when its sources +Only people's words decide. A bot's reply, and an answer `revise` itself posted, are read for +what they say, never as the group's verdict. A reply a person posted is theirs, however an agent +worded it, when its attribution shows they read it, any level below `5/5`; one at `5/5`, or with +no sign it was read, stays the agent's (`docs/design.md`, "The revision weighs where a rule held"). +A person's reply is read for what it decides, as `pr-review`'s "Whose reply counts" judges a reply +that opens with no word. A proposal settles to `unclear` when people disagree with no outcome, when its sources disagree (a `Mark:` line and a thread, or two notes), since neither carries a time to order them, when an accept on a held proposal gives no reason, and when an accept on a change to a live data convention names no owner. A question a comment asks is answered as @@ -50,8 +54,10 @@ file named as a link, as `decision-points.md` says of longer text. Then ask: **Write these marks** (Recommended, when none is unclear), **Change some**, the person naming the proposal and the mark, or **Let's discuss**. An `unclear` mark is settled with the person before anything is written, or left blank, deferred, on their word. On the yes, write each mark on its -proposal's `Mark:` line, moving any words the group had written there to a `discussed: ` line beneath it so none is lost, and tick the box of each accepted one, the report being +proposal's `Mark:` line, blank for a deferred one, with a `discussed:` line beneath it holding +the words the group had written there, or `discussed: none on this line`, so none is lost and a +rerun knows the mark is settled; tick the box of each accepted proposal and each answered +`question/`, the report being the record of what the group decided; nothing below reads the discussion again. Every `Mark:` line empty after that, the meeting having deferred everything: stop, there is nothing to write, and name the report's path and, in a clone, its branch. Then, by mark: @@ -69,9 +75,8 @@ report's path and, in a clone, its branch. Then, by mark: - A mark carrying `reworded: ""` drafts those words in place of the rule's proposed wording, its `> ` lines, or the drafted entry's statement, the rest of its `entry:` lines kept as they are; it is named in the lint like any other change, so the bar judges the words the - group agreed. With an override too, it is still named: the override waives only the gate the - proposal was held on, its lint line going beside the reason, and a failure of any other gate - stops as step 2's lint does. + group agreed. With an override too, it is still named, and the override waives only the gate + the proposal was held on, as `rule-bar.md`'s "Overrides" says. 1. **Archive every rule that leaves.** For each rule graduating, retiring, expiring, demoted to the prose tier or rewritten, write all of these: @@ -148,8 +153,10 @@ report's path and, in a clone, its branch. Then, by mark: mark stays on its entry's evidence line. With nothing to name and no new decision, skip the lint. A failure stops, with its lines, and nothing is written. Once a repository is over its budget, any named change to a rule counted there fails the budget gate, a narrowing included, so the proposals - that bring it back under go in the same apply; an admission accepted with an override is not - named, so it passes with its budget line printed beside the reason. + that bring it back under go in the same apply; an admission accepted with an override and no + new words is not named, so it passes with its budget line printed beside the reason, while one + reworded with an override is named and stops on the budget gate unless that is the gate it was + held on. Write the diff to `.review/ratchet-proposal.diff`, in `git diff` form with `a/` and `b/` paths relative to this repository, and the side in the standard's repository, when that is another, diff --git a/skills/revise/prepare.md b/skills/revise/prepare.md index 1dcab0c..5747f73 100644 --- a/skills/revise/prepare.md +++ b/skills/revise/prepare.md @@ -1,7 +1,7 @@ # Prepare `revise`'s `prepare` mode: from the window's merged pull requests to the report the group -marks. Each step fills the section of `/skills/revise/outline.md` it names. Apply every Dial +decides on. Each step fills the section of `/skills/revise/outline.md` it names. Apply every Dial by name from the shared `dials.md`; a threshold the Dials do not state is not applied. Steps 1, 2, 5 and 7 run the sections of `/skills/revise/harvest.md` they name; read it at step 1. diff --git a/standards/rule-bar.md b/standards/rule-bar.md index 6fe7bfa..70ad64c 100644 --- a/standards/rule-bar.md +++ b/standards/rule-bar.md @@ -66,7 +66,9 @@ carries `implements `, is never wrapped either. The group may accept a proposal a gate fails only by a mark carrying `override: ` in the report, `accept, override: ` or, with new words, `accept, reworded: "", -override: `; a plain `accept` on it does not accept it. `apply` carries the reason onto the entry's +override: `; a plain `accept` on it does not accept it. With new words, the override +waives only the gate the proposal was held on: the new words pass every other gate, and a +failure there stops `apply` as any lint failure does. `apply` carries the reason onto the entry's evidence line as `override : `, and the next revision's report lists every override. A held admission the group does not accept this way goes into the ledger's `## Candidates below the criteria` with the check it failed; a held rewording, promotion or From 27257165bfc5c550b8ba6967c3cd8b9304a2bb7e Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 10:53:15 -0700 Subject: [PATCH 11/12] fix(revise): cite design.md without a heading check_static cannot find; accept more ways of saying a capture is missing Co-Authored-By: Claude Opus 5.5 --- sandbox/scenarios.yaml | 2 +- skills/revise/apply.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 0ce739d..2310e25 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -288,7 +288,7 @@ scenarios: request 9 named as having no capture, so its words cannot be shown. expect: ends: success - says: ['discussion_r101', 'discussion_r102|ValueError', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (check|verify|read)|absent|does(n.t| not) exist|not (available|present))|(no capture|not captured|missing|no `?capture|absent)[^\n]*(pr-0009|pull request 9|#9)'] + says: ['discussion_r101', 'discussion_r102|ValueError', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (answer|check|verify|read)|couldn.t (answer|check|verify|read)|holds only|absent|does(n.t| not) exist|not (available|present))|(no capture|not captured|missing|no `?capture|absent)[^\n]*(pr-0009|pull request 9|#9)'] - id: revise-prepare-uncaptured repo: widgets diff --git a/skills/revise/apply.md b/skills/revise/apply.md index f55b8a3..3c4f40a 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -37,7 +37,7 @@ discussion. Only people's words decide. A bot's reply, and an answer `revise` itself posted, are read for what they say, never as the group's verdict. A reply a person posted is theirs, however an agent worded it, when its attribution shows they read it, any level below `5/5`; one at `5/5`, or with -no sign it was read, stays the agent's (`docs/design.md`, "The revision weighs where a rule held"). +no sign it was read, stays the agent's, as `docs/design.md` explains for the revision's evidence. A person's reply is read for what it decides, as `pr-review`'s "Whose reply counts" judges a reply that opens with no word. A proposal settles to `unclear` when people disagree with no outcome, when its sources disagree (a `Mark:` line and a thread, or two notes), since neither carries a time to order them, From d8ffa5071eac29b9dfcf661153b7c59b676c0646 Mon Sep 17 00:00:00 2001 From: Matthew Maclay Date: Sat, 7 Nov 2026 11:12:32 -0700 Subject: [PATCH 12/12] test(sandbox): spell the missing-capture pattern so codespell passes Co-Authored-By: Claude Opus 5.5 --- sandbox/scenarios.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sandbox/scenarios.yaml b/sandbox/scenarios.yaml index 2310e25..1188a5a 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -288,7 +288,7 @@ scenarios: request 9 named as having no capture, so its words cannot be shown. expect: ends: success - says: ['discussion_r101', 'discussion_r102|ValueError', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (answer|check|verify|read)|couldn.t (answer|check|verify|read)|holds only|absent|does(n.t| not) exist|not (available|present))|(no capture|not captured|missing|no `?capture|absent)[^\n]*(pr-0009|pull request 9|#9)'] + says: ['discussion_r101', 'discussion_r102|ValueError', '(?i)(pr-0009|pull request 9|#9)[^\n]*(no capture|not captured|missing|isn.t captured|no `?capture|can.t (answer|check|verify|read)|could(n.t| not) (answer|check|verify|read)|holds only|absent|does(n.t| not) exist|not (available|present))|(no capture|not captured|missing|no `?capture|absent)[^\n]*(pr-0009|pull request 9|#9)'] - id: revise-prepare-uncaptured repo: widgets