diff --git a/GUIDE.md b/GUIDE.md index 1a0b84b..bf02414 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, 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. @@ -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/README.md b/README.md index b36b663..34ba8e3 100644 --- a/README.md +++ b/README.md @@ -794,6 +794,8 @@ 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` | | `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/ROADMAP.md b/ROADMAP.md index b9d5e32..eee55ae 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 | @@ -38,6 +37,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 8b7f063..be4ebdf 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -493,7 +493,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 7ae81e5..290b2c6 100644 --- a/docs/design.md +++ b/docs/design.md @@ -99,7 +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 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/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/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 new file mode 100644 index 0000000..1c8ee2a --- /dev/null +++ b/sandbox/fixture/discuss/notes/meeting-2026-11-03.md @@ -0,0 +1,4 @@ +Revision meeting, 3 November + +- 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.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..0dd498b --- /dev/null +++ b/sandbox/fixture/discuss/ratchet-2026-10.md @@ -0,0 +1,45 @@ +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 + 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 + 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 + 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 e4ed64e..1188a5a 100644 --- a/sandbox/scenarios.yaml +++ b/sandbox/scenarios.yaml @@ -215,6 +215,81 @@ scenarios: agents: ["source-reader"] says: ['(?m)^\W*Excerpt: "', '(?i)not in the file'] + - 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/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/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-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. 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 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. + 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" + - "grep -qiE 'R-004.*accept, reworded' .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 '\\| *\\**`?(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"] + 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 + 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', '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 setup: diff --git a/skills/revise/SKILL.md b/skills/revise/SKILL.md index 1b77af0..53309b8 100644 --- a/skills/revise/SKILL.md +++ b/skills/revise/SKILL.md @@ -4,23 +4,24 @@ 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, 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 ]" +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 +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. @@ -36,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` | +| 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 | @@ -82,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 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 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, +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 +121,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 @@ -148,6 +153,10 @@ 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 `ticket-draft` takes. - `archive-pages/`, from `apply`: a Confluence page body per archive entry, for a person to @@ -192,8 +201,9 @@ 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 -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 79d9275..3c4f40a 100644 --- a/skills/revise/apply.md +++ b/skills/revise/apply.md @@ -1,24 +1,82 @@ # 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. -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; 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, 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. 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 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 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, 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, +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. + +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 +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: - 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 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. 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: @@ -66,8 +124,8 @@ in 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 @@ -86,16 +144,19 @@ 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 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/discuss.md b/skills/revise/discuss.md new file mode 100644 index 0000000..4d3acde --- /dev/null +++ b/skills/revise/discuss.md @@ -0,0 +1,56 @@ +# 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. + +**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 +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 +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, 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:` + 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, + 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 + 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..fe30724 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. @@ -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: @@ -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..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. @@ -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. When "Guidance" ran, run `guidance_clusters.py` again with step 5's arguments and + write its printout to `/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 @@ -374,10 +378,10 @@ 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 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/standards/rule-bar.md b/standards/rule-bar.md index 4d55fe2..70ad64c 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,11 @@ 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. 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 diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index d167bdd..85a5cd4 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -515,7 +515,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):