@am_shork/attest 1.0.0 → 1.1.0

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/CHANGELOG.md CHANGED
@@ -90,6 +90,248 @@ above and 110 stale lines on this one.
90
90
  security hole takes a major, on whatever schedule a hole is found. That price is
91
91
  now stated rather than unknown, which is what made the line decidable at all.
92
92
 
93
+ ## [1.1.0] - 2026-09-02
94
+
95
+ ### Added
96
+
97
+ - **`ATX-89`, `ATX-90`, `ATX-91`: `check` reports a workflow document the
98
+ installed version no longer writes (`stale-workflow-doc`, INFO).** The
99
+ document `attest init` writes is a pure function of the installed version, and
100
+ the adopter's tree records nothing about which version wrote it —
101
+ `skillContent()` and `workflowBody()` take no argument, deliberately, because
102
+ that is what makes re-running `init` safe. So the file carries no version
103
+ stamp and a diff against the installed build is the only detector its own
104
+ design permits. Measured on a 0.9.1 → 0.11.0 upgrade report: **110 changed
105
+ lines across 9 hunks**, 403 lines against 458, found only because the reporter
106
+ wrote a script importing `workflowBody()` out of `dist/`, with `check`,
107
+ `cover`, `verify` and `render --check` all green.
108
+ *The level is an asymmetry of obligation rather than a severity judgement, and
109
+ that is the whole argument for it.* Keeping the document current is Attest's
110
+ obligation, because Attest is what changes it; taking the update is nobody
111
+ else's, because an adopter is free to run an older workflow, a modified one, or
112
+ none. A finding whose subject the reader owes nothing about is exactly the
113
+ finding that may only inform — so `ok`, the exit code and every gate are
114
+ untouched, and a reader who ignores this forever pays what they pay today.
115
+ *This is not `init --check`*, which stays rejected below. That gate turns red
116
+ on a diff the person who must commit it has no way to review: a regenerated
117
+ `SPEC.md` shows what **you** changed about your promises, and a regenerated
118
+ `SKILL.md` shows what Attest's authors changed. It also fires in CI, where
119
+ nobody can run `init`, blocking whoever pushes next for an upgrade somebody
120
+ else made.
121
+ *Three requirements because there are three ways to be wrong, and this file
122
+ already carves the identical problem up this way for renderings.* `ATX-89` is
123
+ the report; `ATX-90` is that an **absent** document draws nothing, which is the
124
+ one the obvious implementation breaks — `staleIssue` reports a rendering that
125
+ was never generated under its own code, and copying that here would fire on two
126
+ of this repository's own three targets and on the majority of adopters, since
127
+ `init` writes one target by default and nothing obliges a project to run it;
128
+ `ATX-91` is that the comparison is of the document rather than the bytes, which
129
+ is deliberately the **opposite** of `ATX-84` and could not be had by widening
130
+ `ATX-29`. `ATX-84` compares this repository's copy byte-for-byte and may,
131
+ because `.gitattributes` here is committed and pins `eol=lf` — that file is
132
+ Attest's here and nobody's in an adopter's tree, so one rule cannot cover both.
133
+ *What it costs, measured before it was built:* 0.234 ms for all three target
134
+ comparisons, plus 4.19 ms to import `targets.ts` and the 26 KB template behind
135
+ it, against a `check self --json` of 814–1,183 ms — **~0.5%**. No
136
+ `SCHEMA_VERSION` bump: the field exists, the value is new, and `ok` is
137
+ untouched because INFO is below `hasError`.
138
+ *Two things the build found that the proposal did not contain.* The fix hint
139
+ had to name what `init` does to the file and not only the command — `runInit`
140
+ writes with `writeAtomic`, no merge and no backup, so an adopter who edited
141
+ these instructions is being pointed at something destructive. `targets.ts`
142
+ rule 1 makes that defensible, Attest owning the path being the whole reason it
143
+ may overwrite, and it was nowhere stated to the reader. And the two negative
144
+ scenarios had to be **paired with the case that must fire**, in the same
145
+ scenario, because an absence assertion alone is green in a tree where nothing
146
+ is implemented — which is the defect `never-red` exists to name, arriving in
147
+ the scenarios written to attest a rule about not firing.
148
+ *One cost that is not paid and is stated rather than left to be discovered:*
149
+ this repository cannot dogfood it. `check` reads the project root, which here
150
+ is `self/`, while the document sits at the repository root — so `check self`
151
+ finds it absent and is correctly silent. `ATX-84`'s scenario reaches two
152
+ directories up because it is a test; a command cannot. All three scenarios
153
+ build a temporary project, and `self/` gives this rule none of the continuous
154
+ exercise `ATX-84` gets.
155
+ *What the judgement tables did with the three ids, since that is the
156
+ counter-pressure earning its place rather than a chore paid to it.* All four
157
+ demanded rows and got them. `compound-requirement` **missed `ATX-89`** — two
158
+ obligations, "reports it" and "at that level", and the keyword heuristic sees
159
+ one — which is the "never wrong, nearly blind" reading holding at 18 of 42
160
+ rather than a new finding. `divergent-param` produced a **new false positive
161
+ and re-keyed an old one**: `level` joins `code`, `file`, `flag` and `field` as
162
+ a fifth natural word for a kind of constant, and `level:ATX-60,ATX-89` is the
163
+ second key where the two requirements genuinely *are* related — both about the
164
+ severity ladder and the `hasError` predicate — while the rule relates them for
165
+ a reason that is not that. The echo detector flagged both of `ATX-89`'s params
166
+ and both were measured `independent` by mutation, one `verify self` each,
167
+ taking it to 4 true positives of 11.
168
+ - **`ATX-88`: `tests-red` names the timeout the run applied and how many
169
+ scenarios reached it, so a suite that went red on the clock can be told from
170
+ one that went red on its code.** Filed from a 0.9.1 → 0.11.0 upgrade report
171
+ where `npx vitest run` was 170/170 green on the same tree on which `verify`
172
+ was red two runs in three. The standing answer — run the suite yourself under
173
+ your own reporter — is circular in exactly that case, because the
174
+ disagreement between the two runs *is* the failure; what the reporter did
175
+ instead was time every scenario by hand and cross-reference which of eleven
176
+ over four seconds declared no `timeoutMs`. **This is deliberately not the
177
+ pass-through**, which stays open at the foot of this file: carrying the
178
+ child's failures means new fields, a `SCHEMA_VERSION` question and a third
179
+ party's assertion wording in the field consumers are told not to branch on.
180
+ It is the narrow half that is not blocked — the isolated child run drops the
181
+ config that would have raised the timeout, so *did anything run out the clock*
182
+ is the one question a green direct run cannot answer and this one can, and it
183
+ needs nothing the report does not already carry. A count and never a list, for
184
+ the same reason.
185
+ *The number is read from the resolved child config rather than written down,
186
+ and that is the half a constant would have got wrong.* It is the one value
187
+ here Attest does not choose: Vitest's own default when the run is isolated,
188
+ the supplied config's value when `--vitest-config` raised it. `ATX-88.params`
189
+ pins the isolated default at 5000 because two documents quote it in prose —
190
+ the workflow document `init` writes and the troubleshooting page — and until
191
+ now nothing would have failed if a Vitest release moved it.
192
+ *Detected by duration and never by the child's error text.* Vitest does say
193
+ "Test timed out in 5000ms." and matching that would be shorter; it is also the
194
+ category error this project refuses in its own diagnostics, since a message is
195
+ prose in somebody else's package too. Measured rather than assumed: a scenario
196
+ that reaches the ceiling ends `fail` a hair past it — 5015 ms against 5000 —
197
+ while an assertion failure ends `fail` in under a millisecond, so `>=` needs no
198
+ tolerance and a tolerance would start claiming timeouts for slow assertions
199
+ that failed honestly. A missing duration reads as *not* a timeout, which is the
200
+ direction that fails quietly; the other reports every red as a timeout, which
201
+ is a wrong diagnosis printed with confidence.
202
+ *Stated over the run rather than over the two commands that render it*, unlike
203
+ `ATX-51` beside it, and by this project's own rule rather than by preference:
204
+ what `verify` and `archive` add is a sentence in `Issue.message`, which is
205
+ prose and never API, so a scenario asserting it would be asserting wording.
206
+ Additive to `--json` only in the sense that nothing there moved at all — no
207
+ field, no code, no `schemaVersion` question.
208
+
209
+ - **`examples/judgement-table/` states the counting rule, which is the one piece
210
+ of content that decides nothing and without which two tables cannot be
211
+ compared.** Decided under `Planned` on 2026-08-31 on the first table built from
212
+ that example, which distrusted its own obligation column above all its others
213
+ and said why: two tables that count differently are not a second measurement of
214
+ the same thing, they are a second vocabulary. The rule is not a column and
215
+ answers nothing — what a requirement carries is still the adopter's judgement —
216
+ so it sits on the same side of the split as the two mechanical properties, and
217
+ was being withheld along with the answers, which was the wrong side. It fixes
218
+ the test (two clauses are two obligations when an implementation can satisfy
219
+ one completely and fail the other), the six kinds of clause that read as a
220
+ second obligation and are not (scope, criterion, purpose, the other side, a
221
+ consequence, and the states a scenario happens to construct), the
222
+ one-promise-per-emitted-artifact case, and the tiebreak — perturb one half and
223
+ see whether the other half's scenario stays green. Extracted from the labels in
224
+ `tests/intent-rule-candidates.spec.ts` rather than invented, which is why it is
225
+ a description of a rule already in use and not a proposal. It also names what it
226
+ does **not** ask: whether the obligations belong under one id is a different
227
+ question, and keeping the two apart is what lets the count stay a measurement.
228
+
229
+ ### Changed
230
+
231
+ - **The example's anchor is no longer a field to be typed, because the first
232
+ table built from that file filled it in for all 80 of its rows mechanically.**
233
+ `Judgement` shipped with an optional `pin`, documented as not a judgement
234
+ column and nevertheless the only concrete field in the interface — and a field
235
+ gets populated. Copying 80 scenario names out of the AST and back into the
236
+ table is not judgement, and it is exactly the form-filling the empty frame
237
+ exists to prevent. `pin` is now out of `Judgement` entirely and sits in a
238
+ sibling `PINS` map, and an anchor on an id declaring fewer than two scenarios is
239
+ **refused**: with one scenario the anchor is a function of the project,
240
+ `inspectProject` already computes it, and the line naming it is transcription
241
+ wearing a judgement's clothes. What survives is the case where the anchor is a
242
+ real choice, which is the case a machine cannot make. *The cost is named rather
243
+ than hidden:* a sole scenario deleted and replaced under a different name is a
244
+ revisit `PINS` will not force. That is what not asking anyone to transcribe
245
+ costs, and it is paid where a rename cannot change which scenario the row meant.
246
+ - **The frame's emptiness is now typed, which the obvious implementation of this
247
+ fix would have left unenforced.** Deleting `pin` leaves `interface Judgement {}`,
248
+ and an empty interface is not an empty row type — it admits `0` and `""`, so
249
+ every row could quietly acquire anything at all while the file claimed to ship
250
+ no fields. `Record<string, never>` is the type that means what the sentence
251
+ says. Caught by `eslint` rather than by reasoning, which is `How an entry is
252
+ re-measured`'s question about what a repair makes reachable arriving one step to
253
+ the side of where it was expected. Measured both directions: a row carrying an
254
+ undeclared field does not compile, and replacing the line with an interface
255
+ holding one **required** column errors on every existing row.
256
+ - **The example's own subject gained a second scenario**, so `EX-1`'s anchor is a
257
+ choice among several rather than a value the machine could have derived — the
258
+ refusal above has nothing to demonstrate on a project where every id declares
259
+ one scenario. It is also the counting rule standing on its own subject: two
260
+ scenarios, one obligation, because "before it is stored" is the criterion for
261
+ "rejected" and not a second promise. All three assertions in the anchor test
262
+ were shown to fail before this was called done — a derivable anchor, a renamed
263
+ scenario, and an anchor with no row.
264
+
265
+ ### Documentation
266
+
267
+ - **Three places carried a backstop claim that is now false — `ATX-84`'s
268
+ rationale, `skill.ts`'s header, and design §9 in both languages — and all
269
+ three withdraw it.** The claim is that every mistake a stale workflow
270
+ document can cause is a diagnostic carrying a fix hint the agent corrects
271
+ itself from, and it is one of the two arguments for shipping no freshness gate
272
+ to adopters. An upgrade report measured the counterexample: a
273
+ copy six weeks and one upgrade behind still described `never-red`'s ordering
274
+ rule, which `[0.10.0]` archived as `evidence-not-ordering` because the
275
+ ordering was never in the engine. An agent reading it does something the
276
+ engine does not require — more conservative, not wrong-shaped — so **no issue
277
+ fires at any severity** and there is nothing to correct itself from. The
278
+ backstop covers a stale document producing bad input; it does not cover one
279
+ producing unnecessary work, and the second is what the drift produced.
280
+ *All three, because they are one argument stored three times and this
281
+ repository's whole subject is what happens when those disagree.* Two were
282
+ known from the entry that priced this; the third was found by grepping for the
283
+ claim rather than for the files that discuss it, and design §9 is the copy
284
+ with the widest audience. `ATX-84`'s rationale cites "the decision in
285
+ `skill.ts`", which cites the design — so correcting any one of them alone
286
+ leaves a citation pointing at a document that says something else. The
287
+ workflow document's own bytes are untouched — the claim lives in the module
288
+ header, above `BODY` — so no `init` re-run is owed and `ATX-84` stays green
289
+ byte-for-byte.
290
+ *What does not change is the requirement.* `ATX-84` gates this repository's
291
+ copy and is not adopter-facing, and the surviving objection — that a gate
292
+ charges every adopter a diff they did not ask for — carries it alone. What the
293
+ withdrawal reopens is the adopter-facing question, which is priced under
294
+ `Under consideration` rather than restated in a rationale, since a second copy
295
+ of an argument that is still moving is the failure this entry is about.
296
+ - **The workflow document named one of the two things the isolated child run
297
+ withholds, and the troubleshooting page named both.** Re-measuring this before
298
+ building it is what found that: the ask was filed as "have the workflow
299
+ document say that a scenario near the default needs an explicit `timeoutMs`",
300
+ and that sentence already existed — in `tests-red`'s troubleshooting section,
301
+ worked example included. What did not exist was the pointer. The workflow
302
+ document's own bullet on why a green direct run does not predict the gate lists
303
+ aliases, transforms and a DOM, and stops there; an agent reading it was told
304
+ about the environment and not about the clock, while the section it links to
305
+ covers both. So what was owed was a second bullet rather than a new
306
+ explanation, which is cheaper than the entry priced it.
307
+ - **`tests-red`'s troubleshooting section stops saying the report carries
308
+ nothing about the failure**, in both languages, because it now carries exactly
309
+ one thing. The sentence that had to change is the one that reads as a general
310
+ promise — "not from this report, because it does not carry them" — and the
311
+ worked example beside it says which half crossed and why the other half is a
312
+ decision rather than an omission. History rows on `verify` and `archive` in
313
+ both references, since a reader on 1.0.0 gets the older report.
314
+ - **`CLAUDE.md`'s "it will not tell you why" gotcha gains its exception.** The
315
+ gotcha is still true of the file, the scenario and the assertion; it is no
316
+ longer true of the clock, and a gotcha that overstates itself is how somebody
317
+ stops reading the report.
318
+
319
+ - **`intent-quality.md` step 2 points at the counting rule, and says why that
320
+ one pointer does not contradict the page's own sequencing.** The page asks a
321
+ reader to do step 2 before reading our table, because a table built by someone
322
+ who read ours first is an echo of ours. A rule that fixes the *unit* is the
323
+ opposite case — sharing it is what makes a second table comparable, where
324
+ sharing an answer is what makes it a copy — and a page that withheld both
325
+ without distinguishing them would have been asking for an incomparable
326
+ measurement on principle. Both languages, and a line added to *Read our table
327
+ after step 2, not before* so the exemption is stated where the rule it excepts
328
+ is. The History row is owed for the second reason rather than the first: step 4
329
+ tells a reader to **copy** `examples/judgement-table/`, and that file changed
330
+ under this version, so a copy taken at `[1.0.0]` is no longer the file this
331
+ page describes — which is exactly what that table is for and the kind of row
332
+ easiest to miss, because nothing on the page announces that it hands out a
333
+ file.
334
+
93
335
  ## [1.0.0] - 2026-09-01
94
336
 
95
337
  ### Added
@@ -6506,211 +6748,6 @@ re-measuring an entry before building it is at the foot of the file, under
6506
6748
  `How an entry is re-measured` — it applies to all three, and was filed here only
6507
6749
  because the first of it was written here.
6508
6750
 
6509
- - **An outward-facing form of the judgement tables, so an adopter can run this
6510
- counter-pressure on their own registry.**
6511
- `tests/intent-rule-candidates.spec.ts` maintains four hand-judged tables over
6512
- this repo's registry and enforces their completeness. Design §11 records the
6513
- properties they defend and that nothing enforces any of them; what the tables
6514
- add is a counter-pressure holding for **one** registry whose authors agreed to
6515
- maintain it. An adopter got none of it, and the reason was sharper
6516
- than "nobody wrote it for them": the package exported `requirement`,
6517
- `scenario`, `defineRequirements`, `delta` and types, and nothing else.
6518
- `parseSpecs`, `findFiles` and `readRegistrySource` were internal, so an
6519
- adopter **could not enumerate their own scenario names** — the table's central
6520
- rot check, that a claimed scenario is one a spec really declares, was not
6521
- merely unwritten downstream but unreachable. **That half shipped under
6522
- `[0.9.2]` as `@am_shork/attest/inspect`** and is recorded at the foot of
6523
- this entry; what stays here is the schema built on top of it, and the three
6524
- blockers below are untouched by the export.
6525
- *The shape that makes it shippable at all is a split, not a feature.* The
6526
- engine can compute which ids exist, which statements match the universal
6527
- quantifier regex, which params are list-valued, and whether a named scenario
6528
- is declared — all four already, all statically. It cannot compute how many
6529
- obligations a requirement carries, what a quantifier ranges over, or whether a
6530
- scenario iterates a param. So the shippable object is **the enforcement, never
6531
- the judgement**: an opt-in `*.judge.ts` read by the same static reader and
6532
- under the same literal-only rule the registry is (it is metadata about
6533
- requirements under review, so `check` must execute nothing to read it), whose
6534
- contents are entirely the adopter's. Opting in is creating the file; once it
6535
- exists, `requirement-unjudged`, `orphan-judgement`, `unbound-site` and
6536
- `quantifier-unjudged` make it complete and keep it from rotting. The
6537
- asymmetry is the only way this can exist without Attest asserting a ground
6538
- truth it refuses to compute (§0), and it is the same asymmetry `never-red`
6539
- already runs on: the archive gate would owe judgement only for ids a change
6540
- ADDs, so a brownfield adoption is not blocked wholesale.
6541
- *One part of the existing table cannot ship in any form.* `DOMAINS.pin` comes
6542
- in two kinds — a scenario name, checkable against the AST, and a **source-text
6543
- fragment** grepped out of the spec file. The second is asserting on the
6544
- wording of somebody's test, which is the `Issue.message` category error under
6545
- another name. Only the scenario-name kind is exportable, and what that costs
6546
- is real: the grep is what caught a deleted pin that `toContain` had passed.
6547
- *Why no detector ships with it, and this is the part that is already
6548
- measured.* Three rule families were built and scored against the whole corpus
6549
- (86 entries, counting a fixture id once per registry file) before any was
6550
- written into the engine, which is the order `divergent-param` was originally
6551
- designed in the wrong way round. Every figure below is a quotation of the
6552
- suite's inline snapshots; the snapshot is the fresh copy, and this paragraph
6553
- is only ever as fresh as its last edit.
6554
- **(1) `compound-requirement`** — 34 requirements carry more than one
6555
- obligation, it flags 16, is **wrong about none**, and misses 18. Never wrong
6556
- and nearly blind, and the 18 are the form the coverage incentive actually
6557
- rewards: extra clauses under a single keyword. **(2) The widened variant** —
6558
- 29 correct, but 14 wrong and still 5 missed, and it is silenced by deleting an
6559
- "and". A rule whose cheapest fix degrades the artifact it protects does not
6560
- ship, and its precision falls as the registry grows. **(3) `divergent-param`**
6561
- — 5 divergences on this repo, **all 5 judged wrong**, zero true positives ever
6562
- recorded. The `code` group now spans 19 ids, and the failure is structural
6563
- rather than a matter of naming: the key is built by discarding the evidence of
6564
- relatedness, so the one genuinely paired divergence in the group is the one it
6565
- cannot show, and the key has churned three times under `flag` on additions
6566
- that changed no verdict. A fourth candidate — the echo detector, scored at
6567
- half precision — belongs to the tautology-detector entry now under
6568
- *Considered and rejected* and is not re-quoted here, because a figure stated in two places goes stale in one. Taken together
6569
- these are the measured range of what a token or single-body detector over
6570
- prose can do here, and they are why the proposal is enforcement-only.
6571
- *What blocks the decision — three things.* **(1) One adapter.** This
6572
- repository's table is the only one that exists, and its author wrote both the
6573
- schema and every answer in it. By this project's own rule a second table is
6574
- what makes the seam real, and freezing a `Judgement` type now is deciding for
6575
- everyone from 34 requirements written by one person. **(2) The breaking form
6576
- of this was already rejected** — see *Countable obligations* below — and one
6577
- of its three reasons survives the separate-file shape intact: **nothing can
6578
- force the count to be true.** The other two do not survive, which is what is
6579
- new here: a separate artifact touches no `--json` field, no delta apply and no
6580
- `render` output, and completeness *across ids* is computable in a way
6581
- completeness *within* an obligation array never was. So this is a re-proposal
6582
- with one of three objections answered and the sharpest one standing.
6583
- **(3) `quantifier-unjudged` reuses a rejected detector as its trigger.**
6584
- Firing on the quantifier token was rejected below at 43% reach and 70% wrong;
6585
- the claim here is that the same regex is acceptable when it asks a question an
6586
- author discharges in one line (`over: 'values'`) rather than issuing a
6587
- verdict. That claim is untested on anyone who did not also write the regex.
6588
- *The half that is not blocked has shipped, under `[0.9.2]`.*
6589
- `@am_shork/attest/inspect` is one function answering what a project declares —
6590
- AST-only, no runner peer, landing on the existing static/executing boundary.
6591
- It ships no schema and decides nothing, which is why it could go ahead of the
6592
- three blockers rather than waiting on them. What it changes about this entry
6593
- is the *kind* of thing that is now missing: the mechanism is no longer
6594
- unreachable, only unwritten, so a second table is something an adopter can
6595
- build rather than something only this repository could.
6596
- *What would decide it:* one judgement table maintained by someone who did not
6597
- design it. If it comes back the same shape, the seam is real and the schema
6598
- follows; if it comes back different, `inspect` was the whole answer and the
6599
- schema never needs to exist. *(One came back on 2026-08-31, and the disjunction
6600
- above is too clean to survive it: partly the same shape, partly not, and the
6601
- same half is the contaminated half. The close of this entry is where that is
6602
- read.)*
6603
- **Nothing currently makes that table arrive, which is a separate problem from
6604
- the three blockers and the one that gates all of them.** Raised in a product
6605
- review of `[0.9.1]`–`[0.9.3]` on 2026-08-19. The deciding evidence is an
6606
- artifact only an adopter can produce, and the path to producing it is: a row in
6607
- the README's document table, then a 214-line page, then a hand pass over four
6608
- questions, then writing the two mechanical properties from a prose
6609
- description, then choosing to report back. Five voluntary steps with no
6610
- scaffold at any of them, against a decision this entry has been waiting on
6611
- since `[0.9.2]`. An entry blocked on evidence that no shipped surface asks for
6612
- is blocked indefinitely, and the wait is not itself evidence of anything.
6613
- *The proposal is scaffolding the mechanism while still withholding the answer,*
6614
- which is the same split `[0.9.2]` already made and not a retreat from it: an
6615
- `examples/` directory holding the completeness and rot-binding assertions with
6616
- **no columns** — the enumeration that forces a row, and the check that a named
6617
- scenario is one `inspectProject` really declares — and the judgement columns
6618
- left for the adopter to add. What the page withholds is this repository's four
6619
- columns, and that stays withheld; what it currently also withholds is the two
6620
- properties it names as *mechanical*, and those are the half where a copy is not
6621
- an echo, because their content is fixed and the fourth finding does not apply
6622
- to them.
6623
- *What blocks it.* Whether a skeleton with no columns is still a shape that
6624
- comes back as a copy — the page's argument is that a template makes the answer
6625
- return its own question, and it is untested whether an adopter reads an empty
6626
- frame as an invitation or as a form to fill in. And it is a second artifact
6627
- claiming to be runnable consumer usage of the public API, which `pre-commit-check`
6628
- §6 already records as the category no gate reaches: the page's three samples
6629
- are unasserted today, and a fourth in a directory that looks executable would
6630
- be worse than a fenced block, not better, unless something runs it.
6631
- **Built under `[0.11.0]`, and the second of those two blockers was the
6632
- buildable one.** `examples/judgement-table/` is in the default suite, the
6633
- specifier is aliased rather than relativised so the file stays copyable
6634
- verbatim, and both assertions were shown to fail before it was called done.
6635
- So the category `pre-commit-check` §6 names now has exactly one member that a
6636
- gate reaches, and the page's three prose samples are still not it.
6637
- *Two things the build found that the proposal did not contain.* A third
6638
- assertion — `issues` empty, before either of the other two — which is not a
6639
- refinement but a hole: an unread registry file and a smaller project are the
6640
- same value, and the two completeness directions pass over both. It was found
6641
- by an invalid mutation during the bite test rather than by reasoning, which is
6642
- `How an entry is re-measured`'s question about what a repair makes reachable,
6643
- arriving from the other end. And a **type-level** forcing move the runtime
6644
- assertion cannot make: a required field added to `Judgement` fails every
6645
- existing row at compile time, so the schema-shaped half of "a row cannot
6646
- arrive unjudged" costs one line and no engine rule. That is worth noting
6647
- against blocker **(1)** above, which is about freezing a `Judgement` type:
6648
- what shipped freezes nothing, because the type is in the adopter's copy.
6649
- **What is untouched is the first blocker, and it is now the only thing between
6650
- this entry and its evidence.** Whether an empty frame reads as an invitation
6651
- or as a form to fill in is still untested, and cannot be tested here — the
6652
- people who would answer it are the ones who have not arrived. What the build
6653
- changes is only that the five voluntary steps are now four, and the one
6654
- removed was the one with a fixed answer. If the next report is still nobody,
6655
- the scaffold was not what was missing, and that is worth knowing too.
6656
- **Decided 2026-08-31: build the counting rule, and not the schema.** The
6657
- blocker was whether an empty frame reads as an invitation or as a form to fill
6658
- in, it could not be tested here, and a 0.9.1 → 0.11.0 upgrade report from
6659
- `mine-capablanca` tested it. The answer is **invitation**, and the report names
6660
- the sentence that did it: not the empty `interface Judgement`, which reads as a
6661
- stub and sent the reporter looking for what belonged in it, but the comment
6662
- above it — *"It ships with no columns, and that is the finished state of this
6663
- file rather than a gap in it."* Declaring the absence finished is what turns a
6664
- blank into a question, and it is the cheapest sentence in the example.
6665
- *The forcing move was one the build found rather than one it proposed.* The
6666
- instruction to add the first column as a **required** field made all 80 rows
6667
- stop compiling at once, and there was then no path back to green that did not
6668
- go through having an opinion about all 80 — *"a form gets partially filled in;
6669
- a type does not"*. That is the type-level forcing move described above,
6670
- working on somebody who did not write it.
6671
- *And the frame leaks in one place, which is a defect in the example rather
6672
- than in the idea.* `pin` is documented as not a judgement column and is the
6673
- only concrete field in the interface, so the reporter's first act was to
6674
- populate it for all 80 rows mechanically — *"there is a field, populate it"*.
6675
- Defensible, since it makes rot-binding bite for 80 rows instead of none, and
6676
- exactly the form-filling behaviour the frame exists to prevent. Generating
6677
- `pin` from `inspectProject` rather than leaving a field to be typed is the fix,
6678
- and it belongs in the example.
6679
- *What does **not** follow, and the report is why it does not.* Two of the three
6680
- columns that came back are the same shape as two of this repository's —
6681
- `clauses` is `OBLIGATIONS`, `quantifier` is a flattened `QUANTIFIED` — and it
6682
- is tempting to read that as the seam being real. It cannot be read that way:
6683
- the reporter names the `attest init` workflow document as prior influence, and
6684
- that document discusses quantifiers and param pinning at length. So the
6685
- convergence says **something** transmits those two ideas, and the document is
6686
- at least as likely a channel as the frame. What is uncontaminated is the third
6687
- column, `venue`, which has no counterpart here: nine of its rows record a
6688
- promise held by a Playwright suite Attest's scope does not reach. A column this
6689
- repository could not have written came back, which is a seam behaving like one.
6690
- *What to build is smaller than a schema, and the three things the reporter
6691
- wanted and did not get are its specification.* **(1) A counting rule for
6692
- "obligation" precise enough that two people count the same.** The report
6693
- distrusts its own `clauses` column above all the others and says why: two
6694
- tables that count differently are not comparable, which defeats the point of
6695
- there being a second one. A counting rule is content that does **not** depend
6696
- on the adopter — the same category as the two mechanical assertions — and it is
6697
- currently withheld along with the columns, which is the wrong side of the
6698
- split. **(2) A way to bind a clause to a scenario rather than a row.**
6699
- Rot-binding binds a row to *a* scenario; nothing binds the sixth obligation of
6700
- a six-clause requirement to anything, so a six-clauses-three-scenarios row is a
6701
- number a reader has to notice instead of an assertion that fails. **(3)
6702
- Somewhere for a row to say the promise is held where Attest cannot see it.**
6703
- Nine `ui-node` rows and a 902-line Playwright suite, citable only in prose.
6704
- *So what is planned is (1) and the `pin` fix, and (2) and (3) explicitly are
6705
- not.* (1) is a paragraph in the example, costs no engine surface, and is the
6706
- one thing that makes a second table comparable to the first — without it two
6707
- tables are two vocabularies and the seam cannot be read either way. (2) and (3)
6708
- are both new schema, and blocker **(1)** of the proposal above — one adapter —
6709
- is not answered by a report that is the second adapter's first hour. Two hours
6710
- of one agent is not two adapters. **The `*.judge.ts` schema stays undecided**,
6711
- and when it moves it will move on a second table that somebody has maintained
6712
- rather than built, which is a different measurement from the one that arrived.
6713
-
6714
6751
  ## Under consideration
6715
6752
 
6716
6753
  Candidates with **no decision yet**, between the two sections either side of it
@@ -6939,6 +6976,31 @@ have to gather again.
6939
6976
  *What would move it:* a red `verify` on an adopter's tree whose cause is not a
6940
6977
  timeout. Every instance anybody has is this one.
6941
6978
 
6979
+ **Blocker (3)'s two cheap answers are built under `[1.1.0]`, and the
6980
+ entry stays open on the other two.** `ATX-88` has `tests-red` name the timeout
6981
+ that applied and how many scenarios reached it, and the workflow document now
6982
+ says the isolated run drops your `testTimeout` along with the rest of your
6983
+ config. Neither touches (1) or (2): no field was added, no `SCHEMA_VERSION`
6984
+ question was asked, and no assertion text from the child crossed into an
6985
+ `Issue`. What the entry proposes — passing the child's failures through — is
6986
+ unchanged and undecided.
6987
+ *Two things the re-measure corrected, and one is the second cheap answer's
6988
+ whole shape.* The workflow-document line was filed as content to write, and it
6989
+ was already written: `tests-red`'s troubleshooting section carries it, worked
6990
+ example included. What was missing was a pointer from the workflow document's
6991
+ own bullet about why a green direct run does not predict the gate, which lists
6992
+ aliases, transforms and a DOM and stops before the clock. And the first
6993
+ answer's phrasing — "the timeout it **imposed**" — was wrong about who imposes
6994
+ it: Attest sets no timeout, it inherits Vitest's default by isolating, so the
6995
+ number has to be read off the resolved child config rather than stated. That
6996
+ distinction is what makes the answer correct under `--vitest-config` too,
6997
+ where the ceiling is the adopter's.
6998
+ *What this does not do, stated so the entry is not read as smaller than it
6999
+ is.* It answers whether the clock was involved. It does not name the scenario,
7000
+ the file or the assertion, so a red `verify` whose cause is not a timeout is
7001
+ exactly as opaque as it was — which is the case this entry is still waiting on
7002
+ an instance of.
7003
+
6942
7004
  - **Changing the value of an existing param passes no gate, and for seven of
6943
7005
  them there is no record either.** Split out of the tautology entry that
6944
7006
  headed this section on 2026-08-18, and which is now under *Considered and
@@ -7138,6 +7200,124 @@ have to gather again.
7138
7200
  yet followed a stale instruction and paid for it, and the rejection below is
7139
7201
  entitled to that until somebody has.
7140
7202
 
7203
+ **Priced on 2026-09-02, per `How an entry is re-measured`, and the three
7204
+ questions have answers: `check`, `INFO`, and no — the chore is the exit code
7205
+ rather than the line.**
7206
+ *The re-measure corrected the entry's framing of the detector first.* "A diff
7207
+ against the installed build is the only detector the file's own design
7208
+ permits" reproduces exactly, and what it makes reachable is the opposite of
7209
+ how it reads: the comparison needs no version stamp, because `skillContent()`
7210
+ is already inside the process that would report. Measured against this
7211
+ repository's own copy — byte-equal and document-equal at 26,584 bytes both
7212
+ sides — and against the other two targets, which are **absent**, that being
7213
+ the population the notice has to be right about. Cost is **0.234 ms** per run
7214
+ for all three comparisons plus **4.19 ms** to import `targets.ts` and through
7215
+ it the 26 KB template in `skill.ts`, which `check` does not import today;
7216
+ `check self --json` is 814–1,183 ms over three runs, so the notice is **~0.5%**
7217
+ of the command it rides on.
7218
+ *Which command, and the objection to it is answered by precedent rather than
7219
+ by argument.* `check` is the only command an adopter runs unconditionally and
7220
+ is the one the workflow document tells the agent to run. `status` takes a
7221
+ `<change>`, so it is silent during exactly the brownfield adoption whose
7222
+ document is stalest; `cover` and `render` are narrower; `verify` and `archive`
7223
+ are the expensive verdicts, where a non-verdict line is noise at the worst
7224
+ available price. The objection is that this would be `check`'s first finding
7225
+ that is not about the project's intent layer — and it would not be:
7226
+ `compilerIssue()` is the first statement of `runCheck`, and
7227
+ `compiler-unsupported` is a finding about the *installed environment*, at
7228
+ ERROR. What ATX-16 scopes `check` to is executing nothing, never intent only.
7229
+ *What level, and `WARNING` is wrong for a reason the roster states.* All three
7230
+ existing WARNINGs — `rationale-placeholder`, `requirement-open`,
7231
+ `orphan-from-failed-registry` — are defects in the reader's own intent layer
7232
+ that the static half declines to fail on. A stale document is neither a defect
7233
+ nor theirs. `INFO` has exactly one member, `possible-drift`, and it is this
7234
+ shape precisely: something here may be drifting and nothing can prove it cost
7235
+ you anything — which is also what this entry's own unmeasured half says.
7236
+ `ERROR` is `init --check`, and that stays rejected.
7237
+ *The chore question dissolves at that level, on a fact about when the drift
7238
+ appears.* The rejection's standard is a gate that "turns red on a change the
7239
+ user did not make". Drift appears only when the installed version moves, which
7240
+ is a lockfile edit and is a change they made; a fresh clone at a pinned version
7241
+ compares equal and is silent, so the failure mode that forced `render --check`
7242
+ off byte comparison — calling a fresh clone stale — cannot occur here. What
7243
+ makes a thing a chore is the exit code, not the line, and at INFO there is
7244
+ nothing to clear: a reader who ignores it forever pays what they pay today.
7245
+ *The repair makes one thing reachable that the entry does not contain, which
7246
+ is the question the method exists to ask.* `runInit` writes with `writeAtomic`
7247
+ unconditionally — no merge, no backup — and the notice cannot tell a stale file
7248
+ from an edited one. Today that overwrite happens only when the user chooses to
7249
+ run `init`; a notice prompts them into it on the tool's schedule, so the fix
7250
+ hint has to say that `init` rewrites the file rather than merely naming the
7251
+ command. `targets.ts` rule 1 makes that defensible — Attest owns the path, and
7252
+ that is the whole reason it may overwrite — but defensible and unstated are
7253
+ different things.
7254
+ *And the obvious implementation is wrong twice, both times by copying
7255
+ `staleIssue`, which is the reference implementation this entry points at and
7256
+ therefore the one nobody re-reads.* **(a)** It emits `missing-spec-doc` for an
7257
+ absent file. Here an absent file is a project that never ran `init` — two of
7258
+ this repository's own three targets — and firing on it would be the tool
7259
+ scattering findings about editors nobody uses, which is the argument
7260
+ `DEFAULT_TARGET` already makes. Absent must be silent, so this takes one code
7261
+ and not two. **(b)** `sameDocument` normalises CRLF and has to here, for the
7262
+ reason `render --check` needs it: Attest does not own an adopter's
7263
+ `.gitattributes`. That is the **opposite** of ATX-84, which compares this
7264
+ repository's copy byte-for-byte, and whose rationale already argues why the two
7265
+ must differ. The comparisons are deliberately not the same one.
7266
+ *What the re-measure found that belongs to no proposal: ATX-84's rationale
7267
+ carries the standing version of this argument, and one clause of it is now
7268
+ false.* It records that a gate here does not reverse `skill.ts`'s decision
7269
+ against shipping one, on the grounds that "every mistake an old document can
7270
+ cause is a diagnostic carrying a fix hint the agent corrects itself from".
7271
+ That is the clause the `mine-capablanca` report falsified — the withdrawn
7272
+ `never-red` ordering produces unnecessary work rather than bad input, and
7273
+ nothing fires at any severity. **Withdrawn under `[1.1.0]` on 2026-09-02,
7274
+ in all three places that carried it**, since the correction was owed whether or not
7275
+ the notice ships; what the withdrawal leaves standing is the diff-nobody-asked-for
7276
+ objection alone, which is the argument this entry now has to beat.
7277
+ *One cost the entry did not price: this repository cannot dogfood it.* `check`
7278
+ reads the project root, which here is `self/`, and the document is at the repo
7279
+ root — so `check self` would find the file absent and be correctly silent.
7280
+ ATX-84's scenario reaches two directories up because it is a test; a command
7281
+ cannot. The scenario would land on a fixture, and `self/` would give the notice
7282
+ none of the continuous exercise ATX-84's rationale calls the only detector this
7283
+ document has.
7284
+ *What it does not need, priced because the entries around it spend most of
7285
+ their length on exactly this:* no `SCHEMA_VERSION` bump — the field exists, the
7286
+ value is new, and `ok` is untouched because INFO is below `hasError`, the same
7287
+ reading that has held the version at 1 across all 41 codes. One roster entry
7288
+ and one troubleshooting section per language, both made mandatory by
7289
+ `docs-consistency.spec.ts` rather than by remembering.
7290
+ *The one form still worth arguing is the cheapest one, and it loses on the
7291
+ exposure's own shape.* A dim line after the report — `init`'s own "Re-run
7292
+ `attest init` after upgrading", moved to where it is actionable — needs no
7293
+ code, no roster entry and no section in two languages. It is also invisible
7294
+ under `--json`, and the drift in that report was found by a reader writing a
7295
+ script against `dist/`. A detector a machine cannot read reproduces the
7296
+ invisibility it exists to remove.
7297
+
7298
+ **Built under `[1.1.0]` on 2026-09-02, and the entry stays here because
7299
+ what was built is not what it names.** The priced form shipped exactly as
7300
+ priced — `check`, INFO, one code, silent on an absent file, document rather
7301
+ than bytes — as `ATX-89`, `ATX-90` and `ATX-91`. The re-measure corrected none
7302
+ of the three answers, which is worth recording against the two cases in `How
7303
+ an entry is re-measured` where it corrected an entry outright.
7304
+ *What the build added that the pricing did not have is the argument the
7305
+ approver gave, and it is better than the one written here.* This entry argues
7306
+ the level from the negative — a gate is a chore, so do not gate. The reason
7307
+ actually adopted is positive and is an **asymmetry of obligation**: keeping
7308
+ this document current is Attest's obligation, because Attest is what changes
7309
+ it, and taking the update is nobody else's. A finding whose subject the reader
7310
+ owes nothing about is exactly the finding that may only inform. That reaches
7311
+ the same level by a route that does not depend on calling anything a chore,
7312
+ and it is what `ATX-89`'s rationale carries.
7313
+ *What is left of this entry is the gate alone, and it is unchanged.* A notice
7314
+ is not a weaker gate, it is a different artifact: it can be ignored, and by a
7315
+ CI-only adopter who never reads `check` output it certainly will be. That is
7316
+ the cost of the choice rather than an oversight — the trade taken was "never
7317
+ blocks anyone" against "guaranteed to be seen". The thing that would move the
7318
+ gate is what it always was, and the notice does not supply it: an adopter who
7319
+ followed a stale instruction and paid for it. Nobody has.
7320
+
7141
7321
  The rejection, kept as it was written:
7142
7322
 
7143
7323
  `render --check` is the obvious precedent and is the opposite case. A rendering
@@ -7260,6 +7440,231 @@ have to gather again.
7260
7440
  committed. If that count is zero the discipline was already working and the hook
7261
7441
  is overhead; if it is not, the number is the case for `init` writing one.
7262
7442
 
7443
+ - **An outward-facing form of the judgement tables, so an adopter can run this
7444
+ counter-pressure on their own registry.**
7445
+ `tests/intent-rule-candidates.spec.ts` maintains four hand-judged tables over
7446
+ this repo's registry and enforces their completeness. Design §11 records the
7447
+ properties they defend and that nothing enforces any of them; what the tables
7448
+ add is a counter-pressure holding for **one** registry whose authors agreed to
7449
+ maintain it. An adopter got none of it, and the reason was sharper
7450
+ than "nobody wrote it for them": the package exported `requirement`,
7451
+ `scenario`, `defineRequirements`, `delta` and types, and nothing else.
7452
+ `parseSpecs`, `findFiles` and `readRegistrySource` were internal, so an
7453
+ adopter **could not enumerate their own scenario names** — the table's central
7454
+ rot check, that a claimed scenario is one a spec really declares, was not
7455
+ merely unwritten downstream but unreachable. **That half shipped under
7456
+ `[0.9.2]` as `@am_shork/attest/inspect`** and is recorded at the foot of
7457
+ this entry; what stays here is the schema built on top of it, and the three
7458
+ blockers below are untouched by the export.
7459
+ *The shape that makes it shippable at all is a split, not a feature.* The
7460
+ engine can compute which ids exist, which statements match the universal
7461
+ quantifier regex, which params are list-valued, and whether a named scenario
7462
+ is declared — all four already, all statically. It cannot compute how many
7463
+ obligations a requirement carries, what a quantifier ranges over, or whether a
7464
+ scenario iterates a param. So the shippable object is **the enforcement, never
7465
+ the judgement**: an opt-in `*.judge.ts` read by the same static reader and
7466
+ under the same literal-only rule the registry is (it is metadata about
7467
+ requirements under review, so `check` must execute nothing to read it), whose
7468
+ contents are entirely the adopter's. Opting in is creating the file; once it
7469
+ exists, `requirement-unjudged`, `orphan-judgement`, `unbound-site` and
7470
+ `quantifier-unjudged` make it complete and keep it from rotting. The
7471
+ asymmetry is the only way this can exist without Attest asserting a ground
7472
+ truth it refuses to compute (§0), and it is the same asymmetry `never-red`
7473
+ already runs on: the archive gate would owe judgement only for ids a change
7474
+ ADDs, so a brownfield adoption is not blocked wholesale.
7475
+ *One part of the existing table cannot ship in any form.* `DOMAINS.pin` comes
7476
+ in two kinds — a scenario name, checkable against the AST, and a **source-text
7477
+ fragment** grepped out of the spec file. The second is asserting on the
7478
+ wording of somebody's test, which is the `Issue.message` category error under
7479
+ another name. Only the scenario-name kind is exportable, and what that costs
7480
+ is real: the grep is what caught a deleted pin that `toContain` had passed.
7481
+ *Why no detector ships with it, and this is the part that is already
7482
+ measured.* Three rule families were built and scored against the whole corpus
7483
+ (104 entries as of 1.1.0, counting a fixture id once per registry file) before any was
7484
+ written into the engine, which is the order `divergent-param` was originally
7485
+ designed in the wrong way round. Every figure below is a quotation of the
7486
+ suite's inline snapshots; the snapshot is the fresh copy, and this paragraph
7487
+ is only ever as fresh as its last edit.
7488
+ **(1) `compound-requirement`** — 42 requirements carry more than one
7489
+ obligation, it flags 18, is **wrong about none**, and misses 24. Never wrong
7490
+ and nearly blind, and the 24 are the form the coverage incentive actually
7491
+ rewards: extra clauses under a single keyword. **(2) The widened variant** —
7492
+ 36 correct, but 14 wrong and still 6 missed, and it is silenced by deleting an
7493
+ "and". A rule whose cheapest fix degrades the artifact it protects does not
7494
+ ship, and its precision falls as the registry grows. **(3) `divergent-param`**
7495
+ — 9 divergences on this repo, **all 9 judged wrong**, zero true positives ever
7496
+ recorded. The `code` group now spans 22 ids, and the failure is structural
7497
+ rather than a matter of naming: the key is built by discarding the evidence of
7498
+ relatedness, so the one genuinely paired divergence in the group is the one it
7499
+ cannot show, and the key has churned three times under `flag` on additions
7500
+ that changed no verdict, and once more under `code` at 1.1.0. `level` arriving
7501
+ there as a fifth param name is the second key whose two requirements genuinely
7502
+ *are* related — `ATX-60` and `ATX-89` are both about the severity ladder — and
7503
+ the rule still relates them by the one thing that is not the relation. A fourth candidate — the echo detector, scored at
7504
+ half precision — belongs to the tautology-detector entry now under
7505
+ *Considered and rejected* and is not re-quoted here, because a figure stated in two places goes stale in one. Taken together
7506
+ these are the measured range of what a token or single-body detector over
7507
+ prose can do here, and they are why the proposal is enforcement-only.
7508
+ *What blocks the decision — three things.* **(1) One adapter.** This
7509
+ repository's table is the only one that exists, and its author wrote both the
7510
+ schema and every answer in it. By this project's own rule a second table is
7511
+ what makes the seam real, and freezing a `Judgement` type now is deciding for
7512
+ everyone from 34 requirements written by one person. **(2) The breaking form
7513
+ of this was already rejected** — see *Countable obligations* below — and one
7514
+ of its three reasons survives the separate-file shape intact: **nothing can
7515
+ force the count to be true.** The other two do not survive, which is what is
7516
+ new here: a separate artifact touches no `--json` field, no delta apply and no
7517
+ `render` output, and completeness *across ids* is computable in a way
7518
+ completeness *within* an obligation array never was. So this is a re-proposal
7519
+ with one of three objections answered and the sharpest one standing.
7520
+ **(3) `quantifier-unjudged` reuses a rejected detector as its trigger.**
7521
+ Firing on the quantifier token was rejected below at 43% reach and 70% wrong;
7522
+ the claim here is that the same regex is acceptable when it asks a question an
7523
+ author discharges in one line (`over: 'values'`) rather than issuing a
7524
+ verdict. That claim is untested on anyone who did not also write the regex.
7525
+ *The half that is not blocked has shipped, under `[0.9.2]`.*
7526
+ `@am_shork/attest/inspect` is one function answering what a project declares —
7527
+ AST-only, no runner peer, landing on the existing static/executing boundary.
7528
+ It ships no schema and decides nothing, which is why it could go ahead of the
7529
+ three blockers rather than waiting on them. What it changes about this entry
7530
+ is the *kind* of thing that is now missing: the mechanism is no longer
7531
+ unreachable, only unwritten, so a second table is something an adopter can
7532
+ build rather than something only this repository could.
7533
+ *What would decide it:* one judgement table maintained by someone who did not
7534
+ design it. If it comes back the same shape, the seam is real and the schema
7535
+ follows; if it comes back different, `inspect` was the whole answer and the
7536
+ schema never needs to exist. *(One came back on 2026-08-31, and the disjunction
7537
+ above is too clean to survive it: partly the same shape, partly not, and the
7538
+ same half is the contaminated half. The close of this entry is where that is
7539
+ read.)*
7540
+ **Nothing currently makes that table arrive, which is a separate problem from
7541
+ the three blockers and the one that gates all of them.** Raised in a product
7542
+ review of `[0.9.1]`–`[0.9.3]` on 2026-08-19. The deciding evidence is an
7543
+ artifact only an adopter can produce, and the path to producing it is: a row in
7544
+ the README's document table, then a 214-line page, then a hand pass over four
7545
+ questions, then writing the two mechanical properties from a prose
7546
+ description, then choosing to report back. Five voluntary steps with no
7547
+ scaffold at any of them, against a decision this entry has been waiting on
7548
+ since `[0.9.2]`. An entry blocked on evidence that no shipped surface asks for
7549
+ is blocked indefinitely, and the wait is not itself evidence of anything.
7550
+ *The proposal is scaffolding the mechanism while still withholding the answer,*
7551
+ which is the same split `[0.9.2]` already made and not a retreat from it: an
7552
+ `examples/` directory holding the completeness and rot-binding assertions with
7553
+ **no columns** — the enumeration that forces a row, and the check that a named
7554
+ scenario is one `inspectProject` really declares — and the judgement columns
7555
+ left for the adopter to add. What the page withholds is this repository's four
7556
+ columns, and that stays withheld; what it currently also withholds is the two
7557
+ properties it names as *mechanical*, and those are the half where a copy is not
7558
+ an echo, because their content is fixed and the fourth finding does not apply
7559
+ to them.
7560
+ *What blocks it.* Whether a skeleton with no columns is still a shape that
7561
+ comes back as a copy — the page's argument is that a template makes the answer
7562
+ return its own question, and it is untested whether an adopter reads an empty
7563
+ frame as an invitation or as a form to fill in. And it is a second artifact
7564
+ claiming to be runnable consumer usage of the public API, which `pre-commit-check`
7565
+ §6 already records as the category no gate reaches: the page's three samples
7566
+ are unasserted today, and a fourth in a directory that looks executable would
7567
+ be worse than a fenced block, not better, unless something runs it.
7568
+ **Built under `[0.11.0]`, and the second of those two blockers was the
7569
+ buildable one.** `examples/judgement-table/` is in the default suite, the
7570
+ specifier is aliased rather than relativised so the file stays copyable
7571
+ verbatim, and both assertions were shown to fail before it was called done.
7572
+ So the category `pre-commit-check` §6 names now has exactly one member that a
7573
+ gate reaches, and the page's three prose samples are still not it.
7574
+ *Two things the build found that the proposal did not contain.* A third
7575
+ assertion — `issues` empty, before either of the other two — which is not a
7576
+ refinement but a hole: an unread registry file and a smaller project are the
7577
+ same value, and the two completeness directions pass over both. It was found
7578
+ by an invalid mutation during the bite test rather than by reasoning, which is
7579
+ `How an entry is re-measured`'s question about what a repair makes reachable,
7580
+ arriving from the other end. And a **type-level** forcing move the runtime
7581
+ assertion cannot make: a required field added to `Judgement` fails every
7582
+ existing row at compile time, so the schema-shaped half of "a row cannot
7583
+ arrive unjudged" costs one line and no engine rule. That is worth noting
7584
+ against blocker **(1)** above, which is about freezing a `Judgement` type:
7585
+ what shipped freezes nothing, because the type is in the adopter's copy.
7586
+ **What is untouched is the first blocker, and it is now the only thing between
7587
+ this entry and its evidence.** Whether an empty frame reads as an invitation
7588
+ or as a form to fill in is still untested, and cannot be tested here — the
7589
+ people who would answer it are the ones who have not arrived. What the build
7590
+ changes is only that the five voluntary steps are now four, and the one
7591
+ removed was the one with a fixed answer. If the next report is still nobody,
7592
+ the scaffold was not what was missing, and that is worth knowing too.
7593
+ **Decided 2026-08-31: build the counting rule, and not the schema.** The
7594
+ blocker was whether an empty frame reads as an invitation or as a form to fill
7595
+ in, it could not be tested here, and a 0.9.1 → 0.11.0 upgrade report from
7596
+ `mine-capablanca` tested it. The answer is **invitation**, and the report names
7597
+ the sentence that did it: not the empty `interface Judgement`, which reads as a
7598
+ stub and sent the reporter looking for what belonged in it, but the comment
7599
+ above it — *"It ships with no columns, and that is the finished state of this
7600
+ file rather than a gap in it."* Declaring the absence finished is what turns a
7601
+ blank into a question, and it is the cheapest sentence in the example.
7602
+ *The forcing move was one the build found rather than one it proposed.* The
7603
+ instruction to add the first column as a **required** field made all 80 rows
7604
+ stop compiling at once, and there was then no path back to green that did not
7605
+ go through having an opinion about all 80 — *"a form gets partially filled in;
7606
+ a type does not"*. That is the type-level forcing move described above,
7607
+ working on somebody who did not write it.
7608
+ *And the frame leaks in one place, which is a defect in the example rather
7609
+ than in the idea.* `pin` is documented as not a judgement column and is the
7610
+ only concrete field in the interface, so the reporter's first act was to
7611
+ populate it for all 80 rows mechanically — *"there is a field, populate it"*.
7612
+ Defensible, since it makes rot-binding bite for 80 rows instead of none, and
7613
+ exactly the form-filling behaviour the frame exists to prevent. Generating
7614
+ `pin` from `inspectProject` rather than leaving a field to be typed is the fix,
7615
+ and it belongs in the example.
7616
+ *What does **not** follow, and the report is why it does not.* Two of the three
7617
+ columns that came back are the same shape as two of this repository's —
7618
+ `clauses` is `OBLIGATIONS`, `quantifier` is a flattened `QUANTIFIED` — and it
7619
+ is tempting to read that as the seam being real. It cannot be read that way:
7620
+ the reporter names the `attest init` workflow document as prior influence, and
7621
+ that document discusses quantifiers and param pinning at length. So the
7622
+ convergence says **something** transmits those two ideas, and the document is
7623
+ at least as likely a channel as the frame. What is uncontaminated is the third
7624
+ column, `venue`, which has no counterpart here: nine of its rows record a
7625
+ promise held by a Playwright suite Attest's scope does not reach. A column this
7626
+ repository could not have written came back, which is a seam behaving like one.
7627
+ *What to build is smaller than a schema, and the three things the reporter
7628
+ wanted and did not get are its specification.* **(1) A counting rule for
7629
+ "obligation" precise enough that two people count the same.** The report
7630
+ distrusts its own `clauses` column above all the others and says why: two
7631
+ tables that count differently are not comparable, which defeats the point of
7632
+ there being a second one. A counting rule is content that does **not** depend
7633
+ on the adopter — the same category as the two mechanical assertions — and it is
7634
+ currently withheld along with the columns, which is the wrong side of the
7635
+ split. **(2) A way to bind a clause to a scenario rather than a row.**
7636
+ Rot-binding binds a row to *a* scenario; nothing binds the sixth obligation of
7637
+ a six-clause requirement to anything, so a six-clauses-three-scenarios row is a
7638
+ number a reader has to notice instead of an assertion that fails. **(3)
7639
+ Somewhere for a row to say the promise is held where Attest cannot see it.**
7640
+ Nine `ui-node` rows and a 902-line Playwright suite, citable only in prose.
7641
+ *So what is planned is (1) and the `pin` fix, and (2) and (3) explicitly are
7642
+ not.* (1) is a paragraph in the example, costs no engine surface, and is the
7643
+ one thing that makes a second table comparable to the first — without it two
7644
+ tables are two vocabularies and the seam cannot be read either way. (2) and (3)
7645
+ are both new schema, and blocker **(1)** of the proposal above — one adapter —
7646
+ is not answered by a report that is the second adapter's first hour. Two hours
7647
+ of one agent is not two adapters. **The `*.judge.ts` schema stays undecided**,
7648
+ and when it moves it will move on a second table that somebody has maintained
7649
+ rather than built, which is a different measurement from the one that arrived.
7650
+ **Both are built under `[1.1.0]`, and moving here on 2026-09-01 is what
7651
+ follows from that.** The counting rule and the anchor fix are the whole of what
7652
+ was decided on 2026-08-31, and they are recorded there. The re-measure before
7653
+ building corrected nothing this entry claims — the report's three asks
7654
+ reproduce as filed — and what it added is the finding the two cases in `How an
7655
+ entry is re-measured` would predict: the obvious implementation of "ship no
7656
+ fields" is an empty interface, and an empty interface admits `0` and `""`, so
7657
+ the frame would have gone on claiming an emptiness its own type did not hold.
7658
+ *The move is the entry's own state changing, not bookkeeping.* `Planned` means
7659
+ decided and unbuilt, and nothing left here is that: the schema, the
7660
+ clause-to-scenario binding and the out-of-scope `venue` column are all waiting
7661
+ on a second table somebody has maintained, which is the definition of this
7662
+ section rather than the one above. What it costs is that `Planned` is empty
7663
+ again — the state that moved `How an entry is re-measured` out of it, so that
7664
+ the method would not go unreachable whenever this happened — and that is the
7665
+ honest reading of where the project stands rather than a gap in the file:
7666
+ nothing is currently decided and unbuilt.
7667
+
7263
7668
  ## Considered and rejected
7264
7669
 
7265
7670
  Decisions **not** to build something, kept where they can be found before the
@@ -9079,6 +9484,7 @@ path the repair makes reachable; it is about **everything the repair would make
9079
9484
 
9080
9485
  ## Releases
9081
9486
 
9487
+ [1.1.0]: https://gitlab.com/Pseudorca/attest/-/tags/v1.1.0
9082
9488
  [1.0.0]: https://gitlab.com/Pseudorca/attest/-/tags/v1.0.0
9083
9489
  [0.11.0]: https://gitlab.com/Pseudorca/attest/-/tags/v0.11.0
9084
9490
  [0.10.0]: https://gitlab.com/Pseudorca/attest/-/tags/v0.10.0