ytdb-slate 0.5.1 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # ytdb-slate
2
2
 
3
+ [![CI status](https://github.com/JetBrains/ytdb-slate/actions/workflows/tier-1.yml/badge.svg?branch=main)](https://github.com/JetBrains/ytdb-slate/actions/workflows/tier-1.yml)
4
+
3
5
  Slate is a thread-weaving orchestration extension for the [pi coding agent](https://pi.dev).
4
6
 
5
7
  The orchestrator (your main pi session) dispatches **bounded actions** to persistent **worker threads**. Each completed action is compressed by an LLM into an **episode** — a durable, structured record (intent, actions, findings, artifacts, open issues, handoff notes) that the orchestrator composes into further dispatches instead of re-reading raw transcripts. On top of that, Slate injects a mandatory workflow doctrine: research before design, design review, adversarial review, and track review — with optional umbrella **draft-PR publishing** for tracks. An opt-in **model-failover** map adds high availability: when a model API fails, the orchestrator, worker threads, and episode compression each retry once on a configured equal-quality alternative. A second opt-in, **action-level model routing**, gives each dispatched action a model and an effort level chosen to be up to the task and no more, so cost is bounded per action instead of per session.
@@ -57,6 +59,18 @@ Slate can pick the model and the effort level per dispatched action rather than
57
59
 
58
60
  What the code enforces is deliberately narrower than what the routing data advises. The dispatch guards refuse an unlisted model, a level off the target model's ladder and a level the provider rejects outright; they warn about — and mark — an unmeasured level, refusing it only when `router.allowUnmeasuredEffort` is `false`; and the context-window guard substitutes a wider listed model instead of ever blocking an action. The obligations that come from the profile data itself — keeping review and gate actions on measured levels, and honoring a REFUSE such as `anthropic/claude-fable-5`'s for zero-data-retention work — are stated in the doctrine for the orchestrator to honor; they are **not** code-enforced guards. Full semantics: the `router.*` rows in [Configuration](#configuration) and the shipped [`docs/model-routing.md`](docs/model-routing.md) — if they disagree, that document wins.
59
61
 
62
+ ## Optional writing guidance
63
+
64
+ Set `writing.check` to `true` to add writing guidance and human-only telemetry. In orchestrator mode, Slate adds a doctrine rule that states the convention. It also adds a status value such as `writing 1/4`. This value means that one of four measured prose turns had a fail-level finding. Each worker gets a shorter preamble reminder about sentence length, semicolons, and contractions.
65
+
66
+ The option has prompt cost. Each orchestrator system prompt contains the doctrine rule. You pay for this text on every turn. Each worker session also gets a gated preamble addition. Both additions are absent when the option is off. The worker preamble then stays byte-identical to its pre-feature form.
67
+
68
+ > **No conformance claim:** This is a dictionary-free proxy. It embeds no controlled vocabulary and claims no ASD-STE100 conformance.
69
+
70
+ The shipped CLI checks plain files, JSONL records, and unified diffs. See [`docs/writing-guidance.md`](docs/writing-guidance.md) for its rules, limits, output, and command examples.
71
+
72
+ Set `writing.remind` to `true` to add context-paced reminders after eligible tool results. Context-paced means Slate waits for configured context growth between reminders. The reminders are hidden from the normal TUI. They repeat five writing requirements and the scope exclusion before the next assistant response. This option remains off unless `writing.check` is also `true`.
73
+
60
74
  ## Install
61
75
 
62
76
  Install into a project (pinned, project-scoped — recorded in `.pi/settings.json`):
@@ -82,6 +96,9 @@ Optional config file: `slate.json` in the project's pi config dir (`.pi/slate.js
82
96
  | `orchestratorPromptDocs` | string[] | `[]` | Project markdown files (paths relative to the project root) whose **contents** are appended to the orchestrator system prompt. |
83
97
  | `workerPromptDocs` | string[] | `[]` | Project markdown files whose **contents** are appended to every worker-thread system prompt. |
84
98
  | `workflow.draftPRs` | boolean | `false` | Enable umbrella draft-PR publishing for tracks. |
99
+ | `writing.check` | boolean | `false` | Add the writing doctrine rule, per-turn status, and worker guidance. An absent option is silently off. A malformed `writing` value, or a non-boolean `check` value, warns and defaults to off. Unknown `writing` keys warn and are ignored. See [`docs/writing-guidance.md`](docs/writing-guidance.md). |
100
+ | `writing.remind` | boolean | `false` | Send a hidden reminder after an eligible tool result. All gates must pass: orchestrator mode, project trust, `writing.check`, this option, no pause, cadence or handoff force, and one-per-round control. See [`docs/writing-guidance.md`](docs/writing-guidance.md). |
101
+ | `writing.remindPercent` | number | `10` | Set cadence as a percentage of Slate's current effective context budget. The value must be finite and in `(0, 100]`. The interval has an 8,192-token floor and no cap. A handoff force bypasses this threshold. See [`docs/writing-guidance.md`](docs/writing-guidance.md). |
85
102
  | `doctrineExtraPath` | string | — | Project markdown whose **content** is appended to the orchestrator doctrine (project-specific workflow additions). |
86
103
  | `reviewPerspectivesPath` | string | — | Project review charters, each declaring its own finding-ID prefix. The doctrine references this **path**; the orchestrator reads the file alongside the shipped review rules. |
87
104
  | `modelFailover` | object (string → string) | — (empty, failover off) | Map of `provider/id` → equal-quality alternative model; on a model API failure the affected site retries once on the mapped model (worker/orchestrator sites only after pi's own retries are exhausted; episode compression has no pi retry loop). Read once at session start — see [`docs/model-failover.md`](docs/model-failover.md). |
@@ -99,6 +116,11 @@ Example `.pi/slate.json` (the `docs/agents/...` paths are placeholders — point
99
116
  "orchestratorPromptDocs": ["docs/agents/orchestrator-guidelines.md"],
100
117
  "workerPromptDocs": ["docs/agents/thread-guidelines.md"],
101
118
  "workflow": { "draftPRs": true },
119
+ "writing": {
120
+ "check": true,
121
+ "remind": false,
122
+ "remindPercent": 10
123
+ },
102
124
  "doctrineExtraPath": "docs/agents/workflow-additions.md",
103
125
  "reviewPerspectivesPath": "docs/agents/review-perspectives.md",
104
126
  "modelFailover": { "anthropic/claude-sonnet-5": "openai/gpt-5.2" },
@@ -106,6 +128,8 @@ Example `.pi/slate.json` (the `docs/agents/...` paths are placeholders — point
106
128
  }
107
129
  ```
108
130
 
131
+ Older Slate releases warn about these reminder keys and ignore them. Use a Slate release whose documentation includes these keys.
132
+
109
133
  > **Silent skip:** the project-file keys fail silently — no error is shown. For the content-injected keys (`orchestratorPromptDocs`, `workerPromptDocs`, `doctrineExtraPath`) a missing, unreadable, or empty file is skipped and nothing is injected. For `reviewPerspectivesPath` the pointer is omitted only when the file is missing — the file is not read at injection time, so an unreadable or empty file is still cited. Verify your paths after copying the example.
110
134
 
111
135
  **Worker extensions (`workerExtensions`).** By default worker threads load no extensions. This key is a list of regex patterns that select extensions the **host session has already loaded** and load them into every worker too. Each pattern is matched **unanchored** (unlike `contextBudget.overrides`, which is anchored) against a load unit's recorded source spec (e.g. `npm:pi-web-search@1.3.1`), its load-unit path, or the entry path of any tool that unit contributes, so a bare package name matches:
@@ -143,6 +167,7 @@ In orchestrator mode, Slate appends a short **doctrine** (a block of numbered ru
143
167
  - `docs/design-principles.md` — Slate's own design rationale
144
168
  - `docs/model-failover.md` — the opt-in `modelFailover` map (**reference documentation** — unlike the entries above it is not workflow doctrine and is not cited by the doctrine)
145
169
  - `docs/context-budget.md` — the orchestrator `contextBudget`: defaults, per-model overrides, the window clamp, and the pricing rationale (also **reference documentation**, not cited by the doctrine)
170
+ - `docs/writing-guidance.md` — the optional writing convention, `writing.check` behavior, status line, and checker CLI
146
171
  - `docs/model-routing.md` — action-level model routing: the two `router` keys, how a model becomes routable, how an omitted effort level resolves, the dispatch guards, and the warnings a first routed session emits. Unlike the two entries above it **is** cited by the doctrine, by absolute path, but only while the routing rule renders — it is the extra embedded path that appears with the rule, the fourth normally and the fifth when `workflow.draftPRs` is also on, which the size arithmetic in `docs/context-budget.md` accounts for. The routable model list itself is never read from this file: the rule renders it live from the session's own resolution, because that set depends on your registry and credentials.
147
172
 
148
173
  Project-specific additions layer on top — they extend, not replace, the shipped doctrine — via two distinct mechanisms:
@@ -108,6 +108,32 @@ Rationale: the clamp guarantees the pause fires before pi's
108
108
  auto-compaction and leaves room to write the handoff brief; the
109
109
  half-window floor keeps small-window models usable.
110
110
 
111
+ ## Writing-reminder cadence
112
+
113
+ The effective budget also sets the optional hidden writing-reminder cadence.
114
+ When `writing.check` and `writing.remind` are both true, Slate computes:
115
+
116
+ ```
117
+ interval = max(8192, floor(effectiveBudget × remindPercent / 100))
118
+ ```
119
+
120
+ `remindPercent` defaults to 10 and accepts a finite value in `(0, 100]`. The
121
+ context-window clamp therefore shortens the cadence on a model whose configured
122
+ budget does not fit. Slate checks the cadence after tool results. One reminder
123
+ slot can fire after each assistant response.
124
+
125
+ A successful handoff adoption sets `forceNext`. The next eligible tool result
126
+ then bypasses token cadence and missing usage. It does not bypass trust,
127
+ orchestrator mode, `writing.check`, `writing.remind`, or pause gates. Slate
128
+ commits the cadence mark only when pi starts delivery of the custom message.
129
+
130
+ The current hidden message is 320 ASCII characters, including its `[slate]`
131
+ header, five reminder requirements, blank separator, and scope exclusion. That
132
+ is about 80 tokens at four characters per token. This count is for the exact
133
+ rendered production string, without JSONL framing or provider-role overhead. The
134
+ message enters conversation context only when a reminder fires. Later requests
135
+ resend it with the rest of the conversation.
136
+
111
137
  ## Compaction policy
112
138
 
113
139
  In orchestrator mode, for budget-driven configs only: a
@@ -129,69 +155,131 @@ you pay its input tokens every turn.
129
155
 
130
156
  **Its size depends on where the package is installed.** That is not
131
157
  obvious, and it catches anyone who measures: the doctrine cites the
132
- shipped docs by ABSOLUTE path, so three to five full filesystem
133
- paths are embedded in the block — three at minimum
158
+ shipped docs by ABSOLUTE path, so three to six full filesystem paths
159
+ are embedded in the block — three at minimum
134
160
  (`track-workflow.md`, `review-rules.md`, `design-principles.md`),
135
161
  plus `pr-publishing.md` when `workflow.draftPRs` is on, plus
136
- `model-routing.md` when the routing rule renders. Every additional
137
- character in the installed `docs/` directory therefore costs 3–5
162
+ `model-routing.md` when the routing rule renders, plus
163
+ `writing-guidance.md` when `writing.check` is on. Every additional
164
+ character in the installed `docs/` directory therefore costs 3–6
138
165
  characters of doctrine.
139
166
 
140
- So the figures below are **portable characters**, defined exactly as
141
- Slate's own automated size check defines them: the block with each
142
- occurrence of the installed `docs/` DIRECTORY removed, keeping the
143
- filename. That makes them install-invariant by construction —
144
- verified by measuring at a 12-character docs path and at this repo's
145
- 58-character one, which give identical portable counts — and it
146
- makes the arithmetic one multiplication:
167
+ The figures below are **portable characters**, defined exactly as
168
+ Slate's own automated `doctrine-budget` check defines them: the
169
+ block with each occurrence of the installed `docs/` DIRECTORY
170
+ removed, keeping the filename. That makes them install-invariant by
171
+ construction and makes the arithmetic one multiplication:
147
172
 
148
173
  > rendered characters = portable + (embedded paths × length of your
149
174
  > installed `docs/` directory)
150
175
 
151
- Every row below states its full basis, because router state, model
152
- count and `draftPRs` each move the number:
153
-
154
- | router | models | `draftPRs` | paths | portable | lines | rendered at a 58-char docs dir |
176
+ `doctrine-budget` in `verification/resolver-checks.mjs` is the
177
+ definition of record for this convention and its enforced bounds.
178
+ This table is a measured snapshot, not a second authority. It uses
179
+ the same shipped-profile fixture as that check. The six-model rows
180
+ use the six models in this repository's `.pi/slate.json`. Every row
181
+ states its full basis, because router state, model count, `draftPRs`,
182
+ and `writing.check` each move the number. Rows with the same fixture
183
+ and basis must agree with `verification/README.md`. A different basis
184
+ must be named instead of presented as the same measurement:
185
+
186
+ | router | models | `draftPRs` | `writing.check` | paths | portable | lines |
155
187
  | --- | --- | --- | --- | --- | --- | --- |
156
- | off | — | off | 3 | 1,929 | 38 | 2,103 |
157
- | off | — | on | 4 | 1,948 | 38 | 2,180 |
158
- | on | 6 configured | off | 4 | 3,870 | 58 | 4,102 |
159
- | on | 6 configured | on | 5 | 3,889 | 58 | 4,179 |
160
- | on | all 9 shipped | off | 4 | 4,448 | 61 | 4,680 |
161
- | on | all 9 shipped | on | 5 | 4,467 | 61 | 4,757 |
188
+ | off | — | off | off | 3 | 1,929 | 38 |
189
+ | off | — | off | on | 4 | 2,999 | 60 |
190
+ | off | — | on | off | 4 | 1,948 | 38 |
191
+ | off | — | on | on | 5 | 3,018 | 60 |
192
+ | on | `.pi/slate.json` six | off | off | 4 | 3,879 | 58 |
193
+ | on | `.pi/slate.json` six | off | on | 5 | 4,949 | 80 |
194
+ | on | `.pi/slate.json` six | on | off | 5 | 3,898 | 58 |
195
+ | on | `.pi/slate.json` six | on | on | 6 | 4,968 | 80 |
196
+ | on | all 9 shipped | off | off | 4 | 4,434 | 61 |
197
+ | on | all 9 shipped | off | on | 5 | 5,504 | 83 |
198
+ | on | all 9 shipped | on | off | 5 | 4,453 | 61 |
199
+ | on | all 9 shipped | on | on | 6 | 5,523 | 83 |
162
200
 
163
201
  An untrusted project reads the first row whatever its `slate.json`
164
- says: no project config is loaded, so neither optional rule renders.
165
- Line counts do not vary with the install path. Enabling `draftPRs`
166
- costs 19 portable characters plus one more embedded path.
202
+ says: no project config is loaded, so no optional rule renders. Line
203
+ counts do not vary with the install path. Enabling `draftPRs` costs
204
+ 19 portable characters plus one embedded path. Enabling
205
+ `writing.check` costs 1,070 portable characters, adds 22 lines to the
206
+ whole doctrine, and adds one embedded path. Considered alone, the
207
+ writing rule is 1,070 portable characters / 23 lines. Its leading
208
+ newline becomes the separator when appended.
167
209
 
168
210
  Two parts of the block grow with configuration rather than with the
169
211
  path:
170
212
 
171
213
  - The routing rule is a live table with ONE ROW PER ROUTABLE MODEL,
172
214
  so what renders is the models you CONFIGURE — not the nine Slate
173
- ships profiles for. It is 1,941 portable characters / 20 lines for
174
- six configured models and 2,519 / 23 for all nine. A model ROW
175
- costs 154–186 characters; the legend adds a one-off clause per
176
- marker it has to explain, so listing the first cheap-tier model
177
- costs ~50 characters more than its row (two new clauses). That is
178
- why stepping from six to nine averages 193 per model where the
179
- first six average 171.
215
+ ships profiles for. In this snapshot it is 1,950 portable
216
+ characters and adds 20 doctrine lines for six configured models;
217
+ for all nine it is 2,505 characters and adds 23 lines. The six
218
+ model rows are 146–181 characters; all nine are 146–183. The
219
+ legend adds a one-off clause per marker it has to explain, so
220
+ growth is not only the sum of new rows.
180
221
  - The worker-extension rule grows per whitelisted extension and per
181
- tool that extension contributes, so it has no fixed size; one
222
+ tool that extension contributes, so it has no fixed size. One
182
223
  extension contributing two tools measured 373 portable characters
183
224
  / 7 lines.
184
225
 
185
- Against a 256,000-token budget none of this is material — as a rough
186
- estimate, at 4 characters per token (no tokenizer was run, and the
187
- table is denser than prose, so treat it as a floor) the whole block
188
- is ≈480–525 tokens with both optional rules off and ≈970–1,025 with
189
- six models routed, each range spanning portable to as-rendered-here.
190
- That is under half a percent of the default budget, though it is
191
- re-sent on every request rather than paid once. It is listed here
192
- because it is context Slate itself puts in front of the model on
193
- every turn, and because it is the number to check before assuming
194
- the budget's headroom is all conversation.
226
+ The table above isolates the shipped rules. Two representative bases include
227
+ worker extensions and support verification decisions:
228
+
229
+ | basis | models | worker extensions | paths | portable | lines | rough tokens |
230
+ | --- | ---: | --- | ---: | ---: | ---: | ---: |
231
+ | current `.pi/slate.json` dogfood config | 6 | real 2 units / 4 tools | 6 | 5,884 | 90 | ≈1,471 |
232
+ | stable maximal fixture | 9 | synthetic 2 units / 4 tools, every rendered field at its cap | 6 | 6,870 | 93 | ≈1,718 |
233
+
234
+ The dogfood row uses `workflow.draftPRs: true`, `writing.check: true`, and the
235
+ six models in `.pi/slate.json`. Its extension basis is
236
+ `pi-smart-fetch@0.3.12` plus `pi-web-search@1.3.1`. Those packages resolve to two
237
+ units and four tools. Their worker-extension rule is 916 portable characters /
238
+ 11 lines. Raw size remains symbolic: `portable + 6 × length(installed docs
239
+ directory)`. No maintainer checkout path belongs in this shipped document.
240
+
241
+ The stable maximal row uses all nine shipped profiles, draft PRs, and writing.
242
+ Its direct post-resolution worker fixture has two units and four tools. Unit
243
+ labels are 128 characters. Tool names are 64 characters. Descriptions are 140
244
+ characters.
245
+
246
+ Those are the renderer caps. The fixture uses safe ASCII letters. It is
247
+ independent of installed extension labels, descriptions, versions and other
248
+ prose. The worker rule measures 1,347 characters against its 1,600-character
249
+ verification budget.
250
+
251
+ `doctrine-budget` caps that representative maximal doctrine at 7,200 portable
252
+ characters. The measured 6,870 leaves 330 characters, or 4.6 percent of the
253
+ 7,200-character bound. A positive control adds one capped tool
254
+ and three copies of the largest measured model row. It measures 7,634 portable
255
+ characters and exceeds the bound by 434. These figures are verification budgets,
256
+ never runtime limits. Arbitrary user extension rosters can exceed them.
257
+
258
+ The exact values are maintenance tripwires. A deliberate doctrine wording,
259
+ requirement roster, renderer cap, or fixture change requires a fresh render
260
+ through `doctrine-budget`. Update its exact expectations and every published
261
+ figure in the same commit. Keep the positive control steps unchanged unless the
262
+ fixture design itself changes.
263
+
264
+ Against a 256,000-token context budget, these blocks remain small. At four
265
+ characters per token as a rough estimate, the shipped-rule table ranges from
266
+ about 482 tokens to about 1,381 tokens. The current dogfood basis is about 1,471
267
+ tokens. The representative maximum is about 1,718 tokens, or 0.67 percent of the
268
+ default budget.
269
+
270
+ No tokenizer was run, and tables are denser than prose. The block is re-sent on every request rather than paid once. These figures show how
271
+ much headroom Slate consumes before conversation content.
272
+
273
+ ### Worker writing preamble
274
+
275
+ The doctrine table does not include the worker preamble. It belongs
276
+ to each worker session's system prompt, not to the orchestrator's
277
+ per-turn doctrine. The feature-off preamble is 226 bytes. With
278
+ `writing.check` on it is 384 bytes, a 158-byte increase (the
279
+ 157-byte guidance plus its separating space). These are UTF-8 byte
280
+ counts; the current text is ASCII, so its character counts happen to
281
+ be identical. This separate figure states the worker-session cost
282
+ without presenting it as orchestrator doctrine.
195
283
 
196
284
  ## Worker threads: the routing window guard
197
285
 
@@ -293,9 +381,8 @@ number.
293
381
  compaction can land in the same cycle and the handoff brief is
294
382
  written from compacted context — episodes and thread state survive
295
383
  on disk, so handoff still functions.
296
- - The doctrine sizes above cover the shipped rules only. A project
297
- that also injects `doctrineExtraPath` or `orchestratorPromptDocs`,
298
- or whitelists worker extensions, pays for those on top in the same
299
- always-loaded position, and Slate measures none of it against the
300
- budget separately — it simply arrives as context the budget then
301
- has less room for.
384
+ - The doctrine sizes above cover the stated fixtures only. A project that
385
+ injects `doctrineExtraPath` or `orchestratorPromptDocs` pays for those on top.
386
+ A larger worker-extension roster also adds context beyond the representative
387
+ fixtures. Slate imposes no runtime doctrine-size limit. This content simply
388
+ leaves less room under the context budget.
@@ -447,14 +447,13 @@ model's ladder is unreadable.
447
447
  - **In the orchestrator's own system prompt, every turn:** the
448
448
  doctrine gains a routing rule — a table with one row per routable
449
449
  model, plus the rules for reading it. This is the surface you do
450
- not see, and it is the router's standing cost: 1,941 characters /
451
- 20 lines for six configured models, 2,519 / 23 for all nine — a
452
- model row costs 154–186 characters, plus a one-off legend clause
453
- for each marker it introduces (listing the first cheap-tier model
454
- costs ~50 characters beyond its row, which is why six→nine
455
- averages 193 per model against the first six's 171). Configuring
456
- the router roughly doubles Slate's always-loaded block (1,929
457
- characters with routing off). Those are PORTABLE characters — the
450
+ not see, and it is the router's standing cost: 1,950 characters /
451
+ 20 added doctrine lines for six configured models, 2,505 / 23 for
452
+ all nine. In the current snapshot a model row costs 146–183
453
+ characters, plus a one-off legend clause for each marker it
454
+ introduces. Configuring the router roughly doubles Slate's
455
+ always-loaded block (1,929 characters with routing and writing
456
+ off). Those are PORTABLE characters — the
458
457
  doctrine with each occurrence of the installed `docs/` directory
459
458
  removed, filenames kept — because the doctrine embeds absolute doc
460
459
  paths and its raw size therefore depends on where the package is
@@ -37,14 +37,45 @@ automatically).
37
37
 
38
38
  ## 3. Choose perspectives by what the change touches
39
39
 
40
- There is no fixed roster — compose perspectives per change. Each
41
- bullet below is that perspective's charter:
40
+ There is no fixed roster — compose perspectives per change. Every
41
+ perspective has this first step before its specific charter: run
42
+ `node /path/to/slate/extension/writing-check.mjs --diff PATH --format text`
43
+ over the review diff, using the package root that contains this document.
44
+ Report fail-level matches in changed prose governed by the reviewed
45
+ project's writing convention. Diff mode deliberately selects only prose
46
+ file types; inspect user-facing strings and comments in source files
47
+ directly. For this package, [writing-guidance.md](writing-guidance.md)
48
+ defines the convention, its scope and its exclusions.
49
+
50
+ The checker is diagnostic, not authoritative. It embeds no controlled
51
+ vocabulary and establishes no ASD-STE100 conformance. A clean run does
52
+ not prove that writing is good, and a match is not automatically a
53
+ defect. Apply the project's convention scope before grading output:
54
+
55
+ - `fail`: in convention-governed changed text, normally `should-fix`;
56
+ use `blocker` only when the underlying defect prevents safe or correct
57
+ delivery. In excluded text, it is advisory only.
58
+ - `warning`: normally at most a `suggestion`; raise it only when
59
+ independent evidence establishes a more consequential defect.
60
+ - `house-style`: at most a `suggestion` when the applicable convention
61
+ calls for the change; otherwise report nothing.
62
+ - `advisory`: never a defect by itself. File a finding only when
63
+ independent review establishes an underlying defect, graded on its
64
+ own consequences.
65
+
66
+ Reviewer judgment governs every class. The checker cannot decide
67
+ meaning, factual accuracy, claim truth, document structure, topic unity,
68
+ or instruction completeness; those usually matter more than a
69
+ mechanical match. Each bullet below is the remainder of that
70
+ perspective's charter:
42
71
 
43
72
  - **Code baseline** (any production/test code change): correctness &
44
73
  bugs (logic, null safety, resource leaks, lifecycle); code quality
45
74
  (readability, duplication, error handling); test quality (do tests
46
75
  verify behavior, cover the change, and would they fail on
47
- regression?).
76
+ regression?). Verify documentation claims about the code under review
77
+ against the implementation already being read. A contradiction is a
78
+ code-baseline finding, not work to leave for a prose specialist.
48
79
  - **Specialists**, added when the change touches their domain:
49
80
  - concurrency — any defect requiring reasoning about thread
50
81
  interleavings (locks, shared state, atomicity);
@@ -60,14 +91,26 @@ bullet below is that perspective's charter:
60
91
  domain specialists). Each charter in that file MUST declare its own
61
92
  stable finding-ID prefix (uniqueness rules in §4). Compose them per
62
93
  change exactly like the built-in perspectives above.
63
- - **Non-code artifacts** (docs, prompts, process rules, extension
64
- guidance like this file) get their own perspectives INSTEAD of the
65
- code baseline: internal consistency & cross-references; instruction
66
- completeness (can a reader execute this without guessing?); context
67
- budget (what becomes always-loaded vs on-demand); writing style;
68
- and safety of any scripts/hooks/config touched.
69
- - **Mixed diffs**: scope each reviewer to its in-scope files. Code
70
- reviewers never review process/docs files, and vice versa.
94
+ - **Non-code baseline** (docs, prompts, process rules, extension
95
+ guidance like this file): internal consistency & cross-references;
96
+ factual and instruction completeness (can a reader execute this
97
+ without guessing?); context budget (what becomes always-loaded vs
98
+ on-demand); and safety of any scripts/hooks/config touched. This
99
+ baseline applies instead of the code baseline unless a mixed diff
100
+ also changes code.
101
+ - **Text specialist**, added when a change ships user-facing or
102
+ licensing-adjacent prose: licensing and provenance exposure;
103
+ coherence with the project's writing convention; proportionality and
104
+ duplication; register, audience fit, structure, and accuracy as
105
+ prose. User documentation, command help, release notes, prompts, and
106
+ public-facing messages normally trigger it. Do not dispatch it solely
107
+ for a short internal code comment, test name, fixture string, or
108
+ mechanical label unless the text is licensing-adjacent or its prose
109
+ risk independently warrants review.
110
+ - **Mixed diffs**: scope each reviewer to its charter. Code reviewers own
111
+ claims that describe their in-scope code even when those claims live
112
+ in documentation. They do not take on the rest of the non-code or text
113
+ charter, and non-code reviewers do not take on code correctness.
71
114
  - Selection is a judgment call, not a rigid filter — when in doubt,
72
115
  include the perspective. Risk/complexity changes how deep iteration
73
116
  goes (§6), never which perspectives run.
@@ -156,13 +199,19 @@ review dispatch:
156
199
  library code instead of assuming its semantics.
157
200
  - Enumerate cases exhaustively along the changed execution paths
158
201
  (branches, error paths, boundary values); mark each case as checked
159
- or explicitly out of scope.
202
+ or explicitly out of scope. For prose, enumerate the affected
203
+ audiences, reader tasks, claims, definitions, cross-references,
204
+ examples, exceptions, and boundary conditions instead of execution
205
+ paths.
160
206
  - Back every defect claim (blocker or should-fix) with a concrete
161
207
  counterexample: the input, state, or interleaving that triggers the
162
208
  defect, traced through the code.
163
209
  - Back every correctness claim ("no issue here") with a justification
164
210
  for why no counterexample exists — not merely the absence of one
165
- found.
211
+ found. For prose, proof of absence means a bounded, reproducible check
212
+ of the relevant set. Examples include checker output, targeted
213
+ searches, re-resolved references, and comparison with authoritative
214
+ sources. State explicitly what those checks cannot establish.
166
215
  - Before finalizing, run an alternative-hypothesis check: "if the
167
216
  opposite verdict were true, what evidence would exist?" — then look
168
217
  for that evidence.
@@ -170,3 +219,7 @@ review dispatch:
170
219
  sought → confirmed / refuted / refined) instead of wandering.
171
220
  - Derive the final verdict explicitly from the stated definitions and
172
221
  claims, not from overall impression.
222
+ - Treat a fix series as a changed artifact that needs review. Verify the
223
+ addressed finding, then review the fix diff and the cumulative result
224
+ for new paths, claims, and regressions. Clearing the original finding
225
+ does not clear defects introduced by its fix.