@tiphys/kernel 0.0.0 → 0.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.
Files changed (181) hide show
  1. package/AGENTS.md +611 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +278 -0
  4. package/checklists/clean-room.yaml +325 -0
  5. package/checklists/env-failure-diagnosis.yaml +68 -0
  6. package/checklists/flake-playbook.yaml +68 -0
  7. package/checklists/hazard-review.yaml +144 -0
  8. package/checklists/plan-review.yaml +103 -0
  9. package/dist/bin/tiphys.d.ts +2 -0
  10. package/dist/bin/tiphys.js +14 -0
  11. package/dist/src/brief.d.ts +32 -0
  12. package/dist/src/brief.js +59 -0
  13. package/dist/src/checklists.d.ts +169 -0
  14. package/dist/src/checklists.js +310 -0
  15. package/dist/src/checks.d.ts +828 -0
  16. package/dist/src/checks.js +3314 -0
  17. package/dist/src/cli.d.ts +9 -0
  18. package/dist/src/cli.js +57 -0
  19. package/dist/src/commands/brief.d.ts +92 -0
  20. package/dist/src/commands/brief.js +320 -0
  21. package/dist/src/commands/checklist.d.ts +42 -0
  22. package/dist/src/commands/checklist.js +168 -0
  23. package/dist/src/commands/doctor.d.ts +35 -0
  24. package/dist/src/commands/doctor.js +605 -0
  25. package/dist/src/commands/gates.d.ts +9 -0
  26. package/dist/src/commands/gates.js +360 -0
  27. package/dist/src/commands/init.d.ts +30 -0
  28. package/dist/src/commands/init.js +128 -0
  29. package/dist/src/commands/lock.d.ts +1 -0
  30. package/dist/src/commands/lock.js +229 -0
  31. package/dist/src/commands/mode.d.ts +40 -0
  32. package/dist/src/commands/mode.js +134 -0
  33. package/dist/src/commands/plan.d.ts +20 -0
  34. package/dist/src/commands/plan.js +105 -0
  35. package/dist/src/commands/pool.d.ts +1 -0
  36. package/dist/src/commands/pool.js +128 -0
  37. package/dist/src/commands/spawn.d.ts +1 -0
  38. package/dist/src/commands/spawn.js +146 -0
  39. package/dist/src/commands/status.d.ts +19 -0
  40. package/dist/src/commands/status.js +120 -0
  41. package/dist/src/commands/teardown.d.ts +1 -0
  42. package/dist/src/commands/teardown.js +79 -0
  43. package/dist/src/commands/tuition.d.ts +21 -0
  44. package/dist/src/commands/tuition.js +218 -0
  45. package/dist/src/commands/validate.d.ts +78 -0
  46. package/dist/src/commands/validate.js +360 -0
  47. package/dist/src/commands/watch.d.ts +1 -0
  48. package/dist/src/commands/watch.js +172 -0
  49. package/dist/src/exec/env.d.ts +128 -0
  50. package/dist/src/exec/env.js +190 -0
  51. package/dist/src/fleet.d.ts +51 -0
  52. package/dist/src/fleet.js +80 -0
  53. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  54. package/dist/src/gates/adapters/http-json.js +283 -0
  55. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  56. package/dist/src/gates/adapters/migrations-command.js +373 -0
  57. package/dist/src/gates/citations.d.ts +408 -0
  58. package/dist/src/gates/citations.js +1163 -0
  59. package/dist/src/gates/coverage.d.ts +284 -0
  60. package/dist/src/gates/coverage.js +701 -0
  61. package/dist/src/gates/credentials.d.ts +74 -0
  62. package/dist/src/gates/credentials.js +533 -0
  63. package/dist/src/gates/deploy.d.ts +1 -0
  64. package/dist/src/gates/deploy.js +33 -0
  65. package/dist/src/gates/manifest.d.ts +99 -0
  66. package/dist/src/gates/manifest.js +208 -0
  67. package/dist/src/gates/migrations.d.ts +1 -0
  68. package/dist/src/gates/migrations.js +36 -0
  69. package/dist/src/gates/pin.d.ts +114 -0
  70. package/dist/src/gates/pin.js +154 -0
  71. package/dist/src/gates/red-witness.d.ts +22 -0
  72. package/dist/src/gates/red-witness.js +390 -0
  73. package/dist/src/gates/release.d.ts +283 -0
  74. package/dist/src/gates/release.js +820 -0
  75. package/dist/src/gates/result.d.ts +116 -0
  76. package/dist/src/gates/result.js +91 -0
  77. package/dist/src/gates/run.d.ts +566 -0
  78. package/dist/src/gates/run.js +1536 -0
  79. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  80. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  81. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  82. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  83. package/dist/src/gates/schemas/phase-declaration.schema.json +42 -0
  84. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  85. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  86. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  87. package/dist/src/gates/scope.d.ts +131 -0
  88. package/dist/src/gates/scope.js +1018 -0
  89. package/dist/src/gates/suite.d.ts +217 -0
  90. package/dist/src/gates/suite.js +927 -0
  91. package/dist/src/gates/validate.d.ts +121 -0
  92. package/dist/src/gates/validate.js +414 -0
  93. package/dist/src/hooks.d.ts +32 -0
  94. package/dist/src/hooks.js +62 -0
  95. package/dist/src/liveness.d.ts +321 -0
  96. package/dist/src/liveness.js +396 -0
  97. package/dist/src/lock.d.ts +178 -0
  98. package/dist/src/lock.js +500 -0
  99. package/dist/src/modes.d.ts +149 -0
  100. package/dist/src/modes.js +258 -0
  101. package/dist/src/path-identity.d.ts +2 -0
  102. package/dist/src/path-identity.js +10 -0
  103. package/dist/src/plan.d.ts +73 -0
  104. package/dist/src/plan.js +153 -0
  105. package/dist/src/pool.d.ts +130 -0
  106. package/dist/src/pool.js +721 -0
  107. package/dist/src/roles.d.ts +430 -0
  108. package/dist/src/roles.js +734 -0
  109. package/dist/src/spawn.d.ts +177 -0
  110. package/dist/src/spawn.js +332 -0
  111. package/dist/src/status.d.ts +91 -0
  112. package/dist/src/status.js +119 -0
  113. package/dist/src/task.d.ts +264 -0
  114. package/dist/src/task.js +305 -0
  115. package/dist/src/teardown.d.ts +32 -0
  116. package/dist/src/teardown.js +314 -0
  117. package/dist/src/tuition.d.ts +159 -0
  118. package/dist/src/tuition.js +311 -0
  119. package/dist/src/validate.d.ts +230 -0
  120. package/dist/src/validate.js +732 -0
  121. package/dist/src/version.d.ts +3 -0
  122. package/dist/src/version.js +38 -0
  123. package/dist/src/watcher.d.ts +275 -0
  124. package/dist/src/watcher.js +859 -0
  125. package/dist/src/witness/run.d.ts +274 -0
  126. package/dist/src/witness/run.js +1327 -0
  127. package/dist/src/witness/spec.d.ts +102 -0
  128. package/dist/src/witness/spec.js +253 -0
  129. package/dist/tsconfig.src.tsbuildinfo +1 -0
  130. package/gate-registry.yaml +390 -0
  131. package/gates.manifest.json +195 -0
  132. package/package.json +57 -3
  133. package/role-model-config.yaml +88 -0
  134. package/roles/README.md +128 -0
  135. package/roles/_shared-dispatch-contract.md +87 -0
  136. package/roles/adversarial-plan-reviewer.md +80 -0
  137. package/roles/clean-room-reviewer.md +140 -0
  138. package/roles/implementer.md +460 -0
  139. package/roles/investigator.md +138 -0
  140. package/roles/plan-writer.md +95 -0
  141. package/schemas/README.md +81 -0
  142. package/schemas/assurance-modes.schema.json +264 -0
  143. package/schemas/charter.schema.json +166 -0
  144. package/schemas/checklist.schema.json +114 -0
  145. package/schemas/decision-record.schema.json +88 -0
  146. package/schemas/final-report.schema.json +90 -0
  147. package/schemas/finding.schema.json +106 -0
  148. package/schemas/gate-registry.schema.json +260 -0
  149. package/schemas/mechanism-index.schema.json +94 -0
  150. package/schemas/plan.schema.json +300 -0
  151. package/schemas/report.schema.json +579 -0
  152. package/schemas/role-brief.schema.json +105 -0
  153. package/schemas/role-model-config.schema.json +90 -0
  154. package/schemas/status-line.schema.json +40 -0
  155. package/schemas/tuition.schema.json +191 -0
  156. package/schemas/verdict.schema.json +289 -0
  157. package/schemas/work-history.schema.json +183 -0
  158. package/templates/charter.example.yaml +54 -0
  159. package/templates/decision-record.example.yaml +27 -0
  160. package/templates/final-report.example.yaml +80 -0
  161. package/templates/plan.example.yaml +87 -0
  162. package/templates/report.example.yaml +236 -0
  163. package/templates/warnings.md +74 -0
  164. package/templates/work-history.example.yaml +185 -0
  165. package/tuition/README.md +76 -0
  166. package/tuition/T-001.yaml +48 -0
  167. package/tuition/T-002.yaml +51 -0
  168. package/tuition/T-003.yaml +100 -0
  169. package/tuition/T-004.yaml +52 -0
  170. package/tuition/T-005.yaml +72 -0
  171. package/tuition/T-006.yaml +81 -0
  172. package/tuition/T-007.yaml +56 -0
  173. package/tuition/T-008.yaml +111 -0
  174. package/tuition/T-009.yaml +50 -0
  175. package/tuition/T-015.yaml +36 -0
  176. package/tuition/T-016.yaml +36 -0
  177. package/tuition/T-017.yaml +46 -0
  178. package/tuition/T-018.yaml +84 -0
  179. package/tuition/T-021.yaml +40 -0
  180. package/tuition/T-022.yaml +36 -0
  181. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,579 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://tiphys.dev/schemas/report.schema.json",
4
+ "title": "Tiphys role report",
5
+ "$comment": "R-049, R-057a, R-085, R-086, R-088, T-003, T-004, T-006. Kernel plan M3, M3-P4 steps 1, 1b and 1c. THE HAZARD CLASS OF THIS DOCUMENT IS THAT IT DECIDES WHAT A FALSE CLAIM IS ALLOWED TO LOOK LIKE, in a milestone whose own evidence is written in it. Four things this schema deliberately CANNOT do, stated here rather than left to be discovered. (1) NO SCHEMA CAN TELL A MECHANISM FROM A FINDING: `fix-round.mechanism` is a required non-empty string and nothing more, and the judgement is M3-P7's `fix-round-mechanism-named` probe (M3-P4 criterion 2d(a)). (2) NO KEYWORD CAN TELL FULL CAPTURED OUTPUT FROM A SUMMARY OF IT: `fix-round.derivation.output` carries minLength and a non-whitespace pattern, which reaches absent and empty and reaches nothing else; the multi-line property is asserted of the SHIPPED TEMPLATE by a registered test (criterion 2d(d)), never of an arbitrary instance. (3) NO CRITERION REACHES THE AUTHORING-COST RATIO between an honest record and a dishonest one, which is a property of two costs and not of any instance (section 2.6 reason 1). (4) A NON-EMPTY FIELD CAN STILL BE MEANINGLESS: `honest-failures[].exposure-window` cannot be empty or whitespace here, and whether it says anything is M3-P7's `honest-failure-substance` probe. (5) A `$ref` SHARES KEYWORDS AND DOES NOT SHARE DERIVED CHECKS: three `$defs` in this document are reached by schemas/work-history.schema.json, and a Kind B rule written against one of them runs on THIS type only until it declares `alsoTypes` in src/checks.ts. That asymmetry is invisible at the definition site and it is why a comment here once claimed a check applied where it did not (M3-P4 round-1 finding CR-001); the enumeration in test/report-contract.test.ts is the mechanism that stops the claim going stale, and each shared `$def` below states whether a derived check guards it. WHAT THIS SCHEMA DOES BUY: a green gate result cannot be recorded without the wrapper's exit code and its counts on the SAME object; an environmental excuse cannot be recorded without evidence; an incident cannot be recorded without its exposure window and structural fix; a claim cannot be filed under a kind this schema does not question; and every field whose VALUE is the point rather than its presence carries minLength 1 AND pattern \\S, because `required` alone is satisfied by the empty string (criterion 2e).",
6
+ "description": "One document per role output. What was asked, what was found, what each finding rests on, what was claimed and how each claim was settled, which gates ran and what the wrapper said, and every departure from the plan's letter.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "kind",
11
+ "role",
12
+ "task",
13
+ "findings",
14
+ "claims",
15
+ "deviations",
16
+ "honest-failures",
17
+ "environmental-claims",
18
+ "gate-results"
19
+ ],
20
+ "properties": {
21
+ "kind": {
22
+ "description": "The artifact type, so `tiphys validate --type auto` resolves this document without being told.",
23
+ "type": "string",
24
+ "const": "report"
25
+ },
26
+ "role": {
27
+ "description": "The role brief this report answers.",
28
+ "type": "string",
29
+ "minLength": 1,
30
+ "pattern": "\\S"
31
+ },
32
+ "task": {
33
+ "description": "What the role was asked to do, in the words it was asked in.",
34
+ "type": "string",
35
+ "minLength": 1,
36
+ "pattern": "\\S"
37
+ },
38
+ "verdict": {
39
+ "$comment": "OPTIONAL because plan step 1 says `verdict` where applicable: an investigation report has no verdict and a review report does. A verdict that IS present must say something, which is the criterion 2e rule applied here. M3-P5 CORRECTION, made here rather than in a new comment because a reader arriving at this field must not be told the old thing: the sentence `an investigation report has no verdict` is not true of the investigator role as M3-P5 shipped it. roles/investigator.md:31 states under clause R-004 that the role's output IS a root-cause verdict, roles/investigator.md:17 lists `report` as its only output, and roles/investigator.md:10 puts `declares a cause it did not reproduce without saying so` on the role's `never` list, so an investigator report that reaches a conclusion carries it HERE. The field stays optional, because the investigator that has not concluded is exactly the report M3-P4 had in mind and it must stay writable; what changed is that the presence of a verdict on an investigator report now costs a `repro`, by the top-level if/then this document adds for M3-P5 criterion 6.",
40
+ "type": "string",
41
+ "minLength": 1,
42
+ "pattern": "\\S"
43
+ },
44
+ "repro": {
45
+ "$comment": "R-015a and R-092, M3-P5 criterion 6, and the field the top-level if/then requires. THE POINT OF THE FIELD IS THAT R-015a STOPS BEING PROSE: the requirement row (delivery/requirements/migration-table.md:39) reads `investigator must produce a runnable repro that is red on current code, not just an explanation`, and until this document carried a field for it the only enforcement was a clause in a brief, which is a sentence an agent reads and not a thing a validator refuses. OPTIONAL at the top level and REQUIRED BY THE CONDITIONAL, which is not the same as required: a report that owes no verdict owes no repro, and pinning it here instead would have made every review report unwritable. THE SHAPE IS A ONEOF OVER TWO REAL OUTCOMES AND NOT OVER ONE, and that is the whole design: see `#/$defs/repro`.",
46
+ "$ref": "#/$defs/repro"
47
+ },
48
+ "no-findings-statement": {
49
+ "$comment": "Criterion 2e's TOP-LEVEL SCALAR member. NO KEYWORD CAN REQUIRE THIS FIELD WHEN `findings` IS EMPTY: `maxItems` is outside the declared authoring vocabulary (schemas/README.md), `minItems` says the opposite, and `contains` asks about a member that exists, so the emptiness of a sibling array is not a keyword property. The KEYWORDS here therefore reach only the empty and whitespace-only statement. THE MISSING ONE IS REACHED BY A DERIVED CHECK, `report-no-findings-statement` in src/checks.ts, added by M3-P4 fix round 2 on the orchestrator's arbitration, which amended section 2.3's table to three rows for this phase (D-M3-22 is satisfied by that amendment, not by this comment). It closes BOTH directions: an empty `findings` with no statement, and a statement sitting beside real findings. IT IS REGISTERED FOR `report` ALONE AND THAT IS CORRECT, because `findings` and `no-findings-statement` are report-local properties and no other document type reaches them. FIX ROUND 2 CLAIMED HERE THAT THE ENUMERATION IN test/report-contract.test.ts MADE THAT `a checked fact rather than an assertion`, AND IT DID NOT (finding DV-004): that enumeration walks `$defs` reached by `$ref` and skips any check declaring no `guards`, and this check declares none, so it is skipped on every pointer and these two are top-level properties that could never appear there anyway. The round's own work history said the registration was asserted by hand, so the shipped artifact and the work history disagreed and the shipped one is the one a later author reads. The registration is still correct; what was wrong was the account of HOW IT IS KNOWN, and it is known by reading this schema's `properties` block, where `findings` and `no-findings-statement` appear and no other shipped schema names either. RESIDUE: whether the statement SAYS anything is M3-P7's `contract-avoidance` probe, not a schema property.",
50
+ "type": "string",
51
+ "minLength": 1,
52
+ "pattern": "\\S"
53
+ },
54
+ "findings": {
55
+ "type": "array",
56
+ "items": { "$ref": "#/$defs/finding" }
57
+ },
58
+ "claims": {
59
+ "$comment": "T-006, D-M3-30. SHARED WITH schemas/work-history.schema.json BY $ref INTO THIS DOCUMENT, so there is one definition of the honesty contract rather than two that can drift.",
60
+ "type": "array",
61
+ "items": { "$ref": "#/$defs/claim" }
62
+ },
63
+ "fix-round": {
64
+ "$comment": "CLAUDE.md's fix-round contract, D-M3-30. OPTIONAL, and the residue is stated: nothing in this document declares that a report IS a fix-round report, so the schema cannot require the object for the reports that need it. What it does is make a fix round unreportable without its mechanism, its full derivation and its non-coverage once the object is present at all.",
65
+ "$ref": "#/$defs/fixRound"
66
+ },
67
+ "deviations": {
68
+ "$comment": "R-057a: every departure from the plan's letter is declared.",
69
+ "type": "array",
70
+ "items": { "$ref": "#/$defs/deviation" }
71
+ },
72
+ "honest-failures": {
73
+ "$comment": "R-088: an incident cannot be recorded without the three things that make it useful.",
74
+ "type": "array",
75
+ "items": { "$ref": "#/$defs/honestFailure" }
76
+ },
77
+ "environmental-claims": {
78
+ "$comment": "R-085: an environmental excuse without evidence is unrepresentable.",
79
+ "type": "array",
80
+ "items": { "$ref": "#/$defs/environmentalClaim" }
81
+ },
82
+ "gate-results": {
83
+ "$comment": "R-049, R-086. SHARED WITH schemas/work-history.schema.json BY $ref INTO THIS DOCUMENT.",
84
+ "type": "array",
85
+ "items": { "$ref": "#/$defs/gateResult" }
86
+ }
87
+ },
88
+ "if": {
89
+ "$comment": "M3-P5 CRITERION 6, R-015a. Kind A, both sides on the SAME document, and the TOP-LEVEL if/then slot was free before this (M3-P4 spent its conditionals inside $defs). THE CONDITION IS TWO PROPERTIES AND EACH IS DERIVED RATHER THAN CHOSEN. (1) THE ROLE. `role` is documented above as `the role brief this report answers`, and the briefs' own ids are a CLOSED enum of six in schemas/role-brief.schema.json:22. Measured against that enum, exactly ONE of the six matches this pattern: `orchestrator` no, `investigator` YES, `plan-writer` no, `adversarial-plan-reviewer` no, `implementer` no, `clean-room-reviewer` no. So the pattern cannot over-trigger on any role this process defines, and it is a PATTERN rather than a `const` so that `Investigator` and `investigator, round 2` are inside the rule instead of outside it; every letter is a two-member character class for the reason `$defs/universalQuantifier` gives, that ECMA-262 patterns here carry no flags and there is no `i` to set. (2) THE VERDICT. Presence, not wording. A ROOT-CAUSE-VERDICT PATTERN WAS THE OBVIOUS ALTERNATIVE AND IT IS WORSE HERE, which is a statement about this role and not about verdicts generally: roles/investigator.md:31 makes the root-cause verdict the role's ONLY output, so for an investigator report `has a verdict` and `has a root-cause verdict` name the same reports, while a token list would add a second escape (word the verdict without the token) and buy nothing. WHY THE ROLE IS IN THE CONDITION AT ALL, measured rather than argued: dropping it and keying on `verdict` alone rejects the repository's own shipped example, templates/report.example.yaml:13, which is `role: implementer` with a verdict at templates/report.example.yaml:19 and no repro, and which validates today. The unscoped rule was built and run against that file; its diagnostic is recorded in delivery/work-history/m3-p5.md. TWO RESIDUES, NEITHER OF THEM REACHED BY ANY KEYWORD AND BOTH MEASURED. FIRST: an investigator who states the cause somewhere OTHER than `verdict`, in `findings[].analysis` for instance, carries no `verdict`, so this rule does not fire and no repro is owed. Closing it would mean requiring a repro of every report carrying findings, which is every review report this process produces, and that makes the honest record unwritable, which is this contract's worst hazard. SECOND: a report whose `role` is outside the six, or misspelt past this pattern, escapes; that report has misdeclared which brief it answers, and comparing a record to the run that produced it is M3-P7's clean-room checklist throughout this document.",
90
+ "type": "object",
91
+ "required": ["role", "verdict"],
92
+ "properties": {
93
+ "role": { "$ref": "#/$defs/investigatorRole" },
94
+ "verdict": {
95
+ "$comment": "PRESENCE, and deliberately nothing else: `type: string` is the widest subschema that still satisfies the validator's strict policy, which refuses a `required` name this block does not define (Ajv strictRequired, and every `if` in this document declares its own required names for the same reason). Narrowing it here would make the condition depend on the verdict's WORDING, which the top-level comment gives the derivation against. The empty and whitespace-only verdict is already refused by the `verdict` property above, so widening it here costs nothing.",
96
+ "type": "string"
97
+ }
98
+ }
99
+ },
100
+ "then": {
101
+ "$comment": "The obligation, and `required` is the guarding keyword the witness removes and restores. The diagnostic names `repro`, which is why this is an if/then and not a bare oneOf: a oneOf alone would report only that no alternative matched, which is the reason M3-P4 gives for the same shape at `$defs/claim`.",
102
+ "type": "object",
103
+ "required": ["repro"],
104
+ "properties": {
105
+ "repro": { "$ref": "#/$defs/repro" }
106
+ }
107
+ },
108
+ "$defs": {
109
+ "investigatorRole": {
110
+ "$comment": "See the top-level `if` for the derivation against schemas/role-brief.schema.json:22's closed six. Kept as a named definition so the pattern has one home rather than being restated wherever a later phase needs it.",
111
+ "type": "string",
112
+ "pattern": "[Ii][Nn][Vv][Ee][Ss][Tt][Ii][Gg][Aa][Tt][Oo][Rr]"
113
+ },
114
+ "repro": {
115
+ "$comment": "R-015a AND R-092 TOGETHER, AND THE TWO BRANCHES ARE WHY. R-015a (delivery/requirements/migration-table.md:39) demands a runnable repro that is red on current code. R-092 (delivery/requirements/migration-table.md:165) says what to do when it will not reproduce: ship the harness and say so, and the migration table's own note calls R-092 the complement of R-015a. A ONE-BRANCH `repro` WOULD HAVE ENFORCED THE FIRST ROW BY MAKING THE SECOND ROW'S HONEST RECORD UNWRITABLE, and an investigator who genuinely could not force the arm would then have had two options, both bad: fabricate a repro, which validates and is invisible, or leave the verdict out, which loses the investigation. That is the authoring-cost hazard this document's own header names, arriving through a new field, so it is priced here instead of being discovered later. IT IS ALSO THE SHAPE M3-P4 PAID FOUR ROUNDS FOR at `$defs/claim`: round 2 shipped a pattern that refused the honest open question, and round 4 had to add a fourth branch for the records a truthful author writes. This definition is built with that already known, which is the only reason it does not repeat it. WHAT NEITHER BRANCH REACHES, and the list is what was examined rather than the size of the class. (1) NO KEYWORD HERE MAKES `exit-code` NONZERO, so a repro recording `exit-code: 0` validates while claiming to be red. The closed authoring vocabulary (schemas/README.md, src/validate.ts:111) has no `not` and no numeric bound, so this is not a thing that could have been written and left out; `const: 0` would say the opposite and an enum of every nonzero integer is not a finite object. (2) NOTHING RELATES THE `command` TO THE `exit-code` RECORDED BESIDE IT, which is the third member of the class M3-P4 names at `$defs/fixRound/properties/derivation/properties/output`: relating an output to the command that produced it needs the command to be RUN. (3) NOTHING HERE CAN TELL A TRUE `red-against` OR `not-covered` FROM A FALSE ONE. All three are the same bound this document states everywhere else, and all three are M3-P7's clean-room checklist rather than a keyword.",
116
+ "oneOf": [
117
+ {
118
+ "$comment": "BRANCH 1 OF TWO: IT REPRODUCED. Every required field here is a sentence in the clause it discharges, and none was invented for shape. `command` is roles/investigator.md:67's `a command someone else can run, which fails now`. `exit-code` is what makes the command a measurement instead of a description, and it is the field residue (1) above cannot constrain. `red-against` is roles/investigator.md:75, the red-witness rule's strong form applied to the repro: the repro must be red against the DANGEROUS STATE and not merely against an absent feature, and the brief asks in so many words which state the investigator drove the system into and how they know it got there. `not-covered` is roles/investigator.md:80, and it is required for exactly the reason `$defs/fixRound`'s field of the same name is required: a repro whose scope is wrong returns a clean result indistinguishable from an absence of the defect, and this project has been bitten by that shape three times.",
119
+ "type": "object",
120
+ "additionalProperties": false,
121
+ "required": ["command", "exit-code", "red-against", "not-covered"],
122
+ "properties": {
123
+ "command": { "type": "string", "minLength": 1, "pattern": "\\S" },
124
+ "exit-code": { "type": "integer" },
125
+ "red-against": { "type": "string", "minLength": 1, "pattern": "\\S" },
126
+ "not-covered": { "type": "string", "minLength": 1, "pattern": "\\S" }
127
+ }
128
+ },
129
+ {
130
+ "$comment": "BRANCH 2 OF TWO: IT DID NOT REPRODUCE, AND THE HARNESS IS THE DELIVERABLE (R-092). roles/investigator.md:92 is the clause: hand over the scaffolding, the exact commands, the states driven into, and the arms that stayed green. `harness` is that scaffolding, `arms-that-stayed-green` is that list, and `minItems: 1` is on it because a non-reproduction that names no arm it tried has reported nothing; an author who has tried nothing has an investigation to finish, not a record to file. `did-not-reproduce` is the honest sentence itself. THE PATTERN THAT IS DELIBERATELY NOT ON `did-not-reproduce`: the impossibility-token refusal at `$defs/claim`'s third `oneOf` branch is the obvious thing to copy here, since roles/investigator.md:94 tells the investigator to write `I did not find a way to force this arm` and not `this arm cannot be forced`, which is the same distinction. It is not copied, and the reason is measured rather than aesthetic: M3-P4 fix round 4 found four honest records that the identical pattern refused outright at the sibling site, one of them `I did not check every call site, so this is still open`, and had to add a whole branch to make them writable again. Reproducing a defect that cost four members and a fix round, in a field whose entire purpose is that the honest non-reproduction stays cheap, is not a trade this branch makes. WHAT THAT LEAVES OPEN, stated because the alternative is a reader assuming it was closed: an investigator can write `this arm cannot be forced` here and it validates. The brief refuses that sentence in prose (roles/investigator.md:95) and M3-P7's checklist is what reads it; what this branch buys is that the non-reproduction is DECLARED, with a harness beside it, instead of being a verdict with nothing behind it.",
131
+ "type": "object",
132
+ "additionalProperties": false,
133
+ "required": ["did-not-reproduce", "harness", "arms-that-stayed-green"],
134
+ "properties": {
135
+ "did-not-reproduce": { "type": "string", "minLength": 1, "pattern": "\\S" },
136
+ "harness": { "type": "string", "minLength": 1, "pattern": "\\S" },
137
+ "arms-that-stayed-green": {
138
+ "type": "array",
139
+ "minItems": 1,
140
+ "items": { "type": "string", "minLength": 1, "pattern": "\\S" }
141
+ }
142
+ }
143
+ }
144
+ ]
145
+ },
146
+ "nonEmptyText": {
147
+ "$comment": "CRITERION 2e, THE EMPTY-STRING SATISFACTION CLASS. `required` alone is satisfied by \"\", so every field whose VALUE is the point carries BOTH keywords: minLength 1 reaches the empty string and pattern \\S reaches the whitespace-only block scalar, which minLength alone does not catch. This definition is NOT used by $ref at every site, because a $ref would hide the two keywords from the removing-and-restoring witness section 2.3 rule 2 requires; the keywords are written out at each field and this definition records the rule once.",
148
+ "type": "string",
149
+ "minLength": 1,
150
+ "pattern": "\\S"
151
+ },
152
+ "universalQuantifier": {
153
+ "$comment": "T-003 lesson 3, named there as PARTIALLY LINTABLE and no more than that. The alternation is flat and each branch is a literal, so the pattern is linear in the subject length and carries no nested quantifier; a timing measurement on a long near-miss is recorded in delivery/work-history/m3-p4.md. EVERY LETTER IS A TWO-MEMBER CHARACTER CLASS because the first shipped version admitted only the lowercase and sentence-initial forms, and this repository writes its emphatic universals in CAPITALS: `NEVER` and `ALWAYS` and `EVERY` passed while lowercase `never` was correctly rejected, measured across 83 occurrences in 39 tracked files (M3-P4 round-1 finding CR-005). JSON Schema patterns are ECMA-262 regular expressions with no flags, so there is no `i` to set and the case-insensitivity is written out. WHAT IT DOES NOT REACH, stated because a pattern that looks like a guard invites the reader to assume it is one: a universal claim written without any of these five tokens passes, and the two examples worth naming are `there is no path that` and `guaranteed`. `in every case` is NOT one of them and was wrongly listed here until fix round 2: it IS caught, by the `every` branch under a word boundary (M3-P4 round-1 finding CR-006). T-006 records that the three false claims M1-P5 actually produced were not universal at all, which is why `claims[]` exists beside this rule rather than instead of it.",
154
+ "type": "string",
155
+ "pattern": "\\b([Aa][Ll][Ww][Aa][Yy][Ss]|[Nn][Ee][Vv][Ee][Rr]|[Ee][Vv][Ee][Rr][Yy]|[Aa][Ll][Ll] [Cc][Aa][Ss][Ee][Ss]|[Ii][Nn] [Aa][Ll][Ll])\\b"
156
+ },
157
+ "finding": {
158
+ "type": "object",
159
+ "additionalProperties": false,
160
+ "required": ["id", "severity", "evidence", "analysis", "outcome", "source-pinned"],
161
+ "properties": {
162
+ "id": {
163
+ "type": "string",
164
+ "minLength": 1,
165
+ "pattern": "^[A-Za-z][A-Za-z0-9]*-[0-9]+[a-z]?$"
166
+ },
167
+ "severity": {
168
+ "type": "string",
169
+ "enum": ["high", "medium", "low", "informational"]
170
+ },
171
+ "evidence": {
172
+ "type": "array",
173
+ "minItems": 1,
174
+ "items": { "$ref": "#/$defs/evidence" }
175
+ },
176
+ "analysis": {
177
+ "type": "string",
178
+ "minLength": 1,
179
+ "pattern": "\\S"
180
+ },
181
+ "outcome": {
182
+ "type": "string",
183
+ "minLength": 1,
184
+ "pattern": "\\S"
185
+ },
186
+ "source-pinned": {
187
+ "$comment": "T-004. A finding produced by a run that cannot pin the source it ran against is LABELLED rather than inherited as fact. The boolean is required so the question is answered rather than skipped, and `true` drags `pinned-evidence` with it.",
188
+ "type": "boolean"
189
+ },
190
+ "pinned-evidence": {
191
+ "type": "string",
192
+ "minLength": 1,
193
+ "pattern": "\\S"
194
+ },
195
+ "counter-experiment": {
196
+ "$comment": "T-003 lesson 3. Required by the if/then below when `analysis` carries a universal quantifier: a universal claim is settled by someone trying to falsify it, and this field is where that attempt is named.",
197
+ "type": "string",
198
+ "minLength": 1,
199
+ "pattern": "\\S"
200
+ }
201
+ },
202
+ "if": {
203
+ "$comment": "T-004. Kind A, both sides on the SAME object, so the coupling cannot be satisfied by a pin recorded somewhere else in the document.",
204
+ "type": "object",
205
+ "required": ["source-pinned"],
206
+ "properties": { "source-pinned": { "const": true } }
207
+ },
208
+ "then": {
209
+ "type": "object",
210
+ "required": ["pinned-evidence"],
211
+ "properties": {
212
+ "pinned-evidence": { "type": "string", "minLength": 1, "pattern": "\\S" }
213
+ }
214
+ },
215
+ "oneOf": [
216
+ {
217
+ "$comment": "THE UNIVERSAL-CLAIM RULE (T-003 lesson 3). DECLARED DEVIATION from plan criterion 2(e)'s letter, which calls for an `if`/`then` over a `pattern` on the same object: this object already spends its ONE `if`/`then` slot on T-004's source-pinned coupling, `allOf` is outside the declared authoring vocabulary (schemas/README.md), and two independent conditional rules cannot share one slot. It is still Kind A (schema keywords over one document) and its guarding keyword is `oneOf`, which is what the witness removes and restores. Branch 1: the analysis carries no universal quantifier, so no counter-experiment is owed.",
218
+ "type": "object",
219
+ "required": ["analysis"],
220
+ "properties": {
221
+ "analysis": {
222
+ "$comment": "THE EXACT COMPLEMENT of `$defs/universalQuantifier`'s pattern, wrapped in a negative lookahead. The two are written out separately because JSON Schema has no way to negate a `$ref`, so they can DRIFT, and a drift here opens a gap (a subject matching neither branch) or an overlap (one matching both), either of which breaks the `oneOf`. `test/report-contract.test.ts` derives this string from the shared definition's pattern and fails when the two stop being complements.",
223
+ "type": "string",
224
+ "pattern": "^(?:(?!\\b([Aa][Ll][Ww][Aa][Yy][Ss]|[Nn][Ee][Vv][Ee][Rr]|[Ee][Vv][Ee][Rr][Yy]|[Aa][Ll][Ll] [Cc][Aa][Ss][Ee][Ss]|[Ii][Nn] [Aa][Ll][Ll])\\b)[\\s\\S])*$"
225
+ }
226
+ }
227
+ },
228
+ {
229
+ "$comment": "Branch 2: the analysis carries one, and a counter-experiment reference is required beside it.",
230
+ "type": "object",
231
+ "required": ["analysis", "counter-experiment"],
232
+ "properties": {
233
+ "analysis": { "$ref": "#/$defs/universalQuantifier" },
234
+ "counter-experiment": { "type": "string", "minLength": 1, "pattern": "\\S" }
235
+ }
236
+ }
237
+ ]
238
+ },
239
+ "evidence": {
240
+ "$comment": "Plan step 1: an object with `path` and EITHER `lines` OR `command` plus `exit-code`. The two shapes are a oneOf rather than an optional-everything object, so `path` alone (a citation with nothing behind it) is a rejection.",
241
+ "type": "object",
242
+ "oneOf": [
243
+ {
244
+ "type": "object",
245
+ "additionalProperties": false,
246
+ "required": ["path", "lines"],
247
+ "properties": {
248
+ "path": { "type": "string", "minLength": 1, "pattern": "\\S" },
249
+ "lines": { "type": "string", "pattern": "^[0-9]+(-[0-9]+)?$" },
250
+ "note": { "type": "string", "minLength": 1, "pattern": "\\S" },
251
+ "counter-experiment": { "type": "string", "minLength": 1, "pattern": "\\S" }
252
+ }
253
+ },
254
+ {
255
+ "type": "object",
256
+ "additionalProperties": false,
257
+ "required": ["path", "command", "exit-code"],
258
+ "properties": {
259
+ "path": { "type": "string", "minLength": 1, "pattern": "\\S" },
260
+ "command": { "type": "string", "minLength": 1, "pattern": "\\S" },
261
+ "exit-code": { "type": "integer" },
262
+ "note": { "type": "string", "minLength": 1, "pattern": "\\S" },
263
+ "counter-experiment": { "type": "string", "minLength": 1, "pattern": "\\S" }
264
+ }
265
+ }
266
+ ],
267
+ "if": {
268
+ "$comment": "T-003 lesson 3 applied to `evidence[].note`, which plan step 1 names beside `analysis`.",
269
+ "type": "object",
270
+ "required": ["note"],
271
+ "properties": { "note": { "$ref": "#/$defs/universalQuantifier" } }
272
+ },
273
+ "then": {
274
+ "type": "object",
275
+ "required": ["counter-experiment"],
276
+ "properties": {
277
+ "counter-experiment": { "type": "string", "minLength": 1, "pattern": "\\S" }
278
+ }
279
+ }
280
+ },
281
+ "claim": {
282
+ "$comment": "T-006, D-M3-30, plan step 1b. SHARED BY $ref WITH schemas/work-history.schema.json. WHY A FIELD AND NOT A GREP: CLAUDE.md's claim grep scans prose for candidate sentences and works; T-006 records that its own pattern survived being documented as a norm and was reproduced by the agent who filed it on the same day. A declared section gives the check a FIELD, so the honest restatement is a first-class value rather than an omission. THE ENUM IS CLOSED ON PURPOSE: an open `kind` lets an impossibility claim be filed as a note and skip its construction requirement, which is the hazard this section exists for. THE SPLIT THAT MATTERS: a UNIVERSAL claim is settled by someone trying to FALSIFY it, and an impossibility, coverage or remedy claim is settled by someone trying to BUILD the thing, which is why the three carry an executed construction rather than a counter-experiment. `open-question` is the honest restatement and is deliberately CHEAP: 'I did not find a way to force this arm' needs nothing, and 'this arm cannot be forced here' needs a construction. THAT LAST SENTENCE WAS FALSE AS FIRST SHIPPED and is true now, which is worth saying rather than quietly correcting: until M3-P4 fix round 2 the two sentences differed only in which `kind` the author typed, so the second one filed as `open-question` also needed nothing (finding CR-002). The open-question branch's `statement` now carries a pattern that refuses an asserted settlement; see that branch's own comment for the token list and for what it still does not reach. NO DERIVED CHECK GUARDS THIS DEFINITION, and that is a statement about the mechanism rather than an omission: everything this contract asks for is expressible in keywords, and KEYWORDS TRAVEL THROUGH A `$ref` while derived checks do not, so the whole of the claim contract is shared with schemas/work-history.schema.json automatically. A Kind B rule added here later would NOT be, and would owe `alsoTypes` and `guards` in src/checks.ts.",
283
+ "type": "object",
284
+ "additionalProperties": false,
285
+ "required": ["id", "kind", "statement"],
286
+ "properties": {
287
+ "id": {
288
+ "type": "string",
289
+ "minLength": 1,
290
+ "pattern": "\\S"
291
+ },
292
+ "kind": {
293
+ "$comment": "CLOSED. A value outside this set is reported as `value <x> is not one of the permitted values ...`, which names the vocabulary rather than leaving the author to guess (criterion 2c(d)).",
294
+ "type": "string",
295
+ "enum": ["universal", "impossibility", "coverage", "remedy", "open-question"]
296
+ },
297
+ "statement": {
298
+ "type": "string",
299
+ "minLength": 1,
300
+ "pattern": "\\S"
301
+ },
302
+ "still-open-because": {
303
+ "$comment": "M3-P4 FIX ROUND 4. Declared here as well as in the branch that requires it, because this object sets `additionalProperties: false` and a field absent from THIS block is refused before any branch is reached. Only the fourth `oneOf` branch below permits it, so writing it on a claim of any other kind is a rejection.",
304
+ "type": "string",
305
+ "minLength": 1,
306
+ "pattern": "\\S"
307
+ },
308
+ "settled-by": { "$ref": "#/$defs/settledBy" }
309
+ },
310
+ "if": {
311
+ "$comment": "Every kind EXCEPT open-question owes a settlement. This if/then exists beside the oneOf below so that the diagnostic NAMES `settled-by`; the oneOf alone would report only that no alternative matched.",
312
+ "type": "object",
313
+ "required": ["kind"],
314
+ "properties": {
315
+ "kind": {
316
+ "type": "string",
317
+ "enum": ["universal", "impossibility", "coverage", "remedy"]
318
+ }
319
+ }
320
+ },
321
+ "then": {
322
+ "type": "object",
323
+ "required": ["settled-by"],
324
+ "properties": { "settled-by": { "$ref": "#/$defs/settledBy" } }
325
+ },
326
+ "oneOf": [
327
+ {
328
+ "$comment": "A universal claim is settled by a counter-experiment (T-003). THE SECOND MEMBER OF CR-002'S CLASS IS CLOSED HERE, and it is a different member rather than the same one twice: `open-question` requires NOTHING, so it escapes everything, while `universal` requires a counter-experiment, which is a SENTENCE, so filing an impossibility here downgrades an executed construction to a sentence. `It is impossible to force the ENOENT arm here` filed as `universal` and settled by `I thought about it and concluded there is nothing to try` validated before fix round 2. The statement pattern here refuses the SEVEN IMPOSSIBILITY TOKENS ONLY, and deliberately not the universal ones: `the lease is never held twice` is exactly what this branch is for and must stay cheap. M1-P5's `needs a stat or readdir failure that is neither ENOENT nor a permission bit, and this suite runs as root` is the measured reason the two settlements are not interchangeable: it was disproved in minutes by `symlinkSync(p, p)` raising ELOOP.",
329
+ "type": "object",
330
+ "additionalProperties": false,
331
+ "required": ["id", "kind", "statement", "settled-by"],
332
+ "properties": {
333
+ "id": { "type": "string", "minLength": 1, "pattern": "\\S" },
334
+ "kind": { "type": "string", "const": "universal" },
335
+ "statement": {
336
+ "type": "string",
337
+ "minLength": 1,
338
+ "pattern": "^(?:(?!\\b([Cc][Aa][Nn][Nn][Oo][Tt] [Bb][Ee]|[Cc][Aa][Nn] [Nn][Oo][Tt] [Bb][Ee]|[Ii][Mm][Pp][Oo][Ss][Ss][Ii][Bb][Ll][Ee]|[Ii][Mm][Pp][Oo][Ss][Ss][Ii][Bb][Ii][Ll][Ii][Tt][Yy]|[Nn][Oo] [Ww][Aa][Yy] [Tt][Oo]|[Tt][Hh][Ee][Rr][Ee] [Ii][Ss] [Nn][Oo] [Ww][Aa][Yy]|[Tt][Hh][Ee][Rr][Ee] [Ii][Ss] [Nn][Oo] [Pp][Aa][Tt][Hh])\\b)[\\s\\S])*$"
339
+ },
340
+ "settled-by": { "$ref": "#/$defs/settledByCounterExperiment" }
341
+ }
342
+ },
343
+ {
344
+ "$comment": "An impossibility, coverage or remedy claim is settled by an EXECUTED CONSTRUCTION: the attempt, with its command, its exit code and its output, rather than the reasoning that predicted the attempt would fail. M1-P5's 'needs a stat or readdir failure that is neither ENOENT nor a permission bit, and this suite runs as root' was disproved in minutes by symlinkSync(p, p) raising ELOOP, needing no privileges.",
345
+ "type": "object",
346
+ "additionalProperties": false,
347
+ "required": ["id", "kind", "statement", "settled-by"],
348
+ "properties": {
349
+ "id": { "type": "string", "minLength": 1, "pattern": "\\S" },
350
+ "kind": {
351
+ "type": "string",
352
+ "enum": ["impossibility", "coverage", "remedy"]
353
+ },
354
+ "statement": { "type": "string", "minLength": 1, "pattern": "\\S" },
355
+ "settled-by": { "$ref": "#/$defs/settledByConstruction" }
356
+ }
357
+ },
358
+ {
359
+ "$comment": "THE HONEST RESTATEMENT, and it is first-class rather than an omission. `additionalProperties: false` with `settled-by` absent from this branch's properties is what makes a SETTLED question filed as OPEN a rejection: it is the opposite misdeclaration and is equally a misdeclaration (criterion 2c(e)). THE STATEMENT PATTERN IS FIX ROUND 2's ANSWER TO CR-002, and the mechanism it closes is worth stating at the site: AN ENUM BRANCH THAT REQUIRES NOTHING MAKES EVERY SIBLING BRANCH THAT REQUIRES SOMETHING OPTIONAL, BECAUSE THE AUTHOR PICKS THE BRANCH. Closing the enum shut the route through an INVENTED kind and left the route through the cheapest DECLARED one wide open: the same impossibility filed here exited 0, while filed as `impossibility` it owed an executed construction. The remedy is inside the declared authoring vocabulary (`pattern`, schemas/README.md), which is what makes this a fix rather than a vocabulary expansion. WHAT IT REFUSES: a statement asserting a SETTLEMENT while claiming to be open, by the five universal tokens plus eight impossibility tokens (`cannot be`, `can not be`, `impossible`, `impossibility`, `no way to`, `there is no way`, `there is no path`, `guaranteed`), each written case-insensitively for the same reason as `$defs/universalQuantifier`. WHAT IT KEEPS CHEAP, and the sentence that used to stand here was a universal that round 4 broke: fix round 2 wrote that the pattern keeps the honest open question cheap, offering `I did not find a way to force this arm` as its evidence, and one instance is not a class. That instance does still cost nothing, and four others that a truthful author writes were refused outright until round 4 added the fourth branch below; they are named in that branch's comment and derived with exit codes in delivery/work-history/m3-p4.md. So the honest statement free of these thirteen tokens costs nothing HERE, and the honest statement carrying one costs a `still-open-because` THERE. `this arm cannot be forced here` is refused at both, and must be filed as an `impossibility` with a construction. THE TOKENS DELIBERATELY LEFT OUT: CLAUDE.md's claim grep also scans for `needs a`, `is covered`, `catches`, `would catch`, `recovers` and `anyway`, and none of them is here, because each is ordinary in an honest open question (`this needs a real runner`) and pricing them would make the honest record expensive, which is this phase's own worst hazard. RESIDUE: an assertion written in none of these thirteen tokens still passes, exactly as `$defs/universalQuantifier` still passes an unlisted universal. The pattern narrows the escape; it does not seal it.",
360
+ "type": "object",
361
+ "additionalProperties": false,
362
+ "required": ["id", "kind", "statement"],
363
+ "properties": {
364
+ "id": { "type": "string", "minLength": 1, "pattern": "\\S" },
365
+ "kind": { "type": "string", "const": "open-question" },
366
+ "statement": {
367
+ "type": "string",
368
+ "minLength": 1,
369
+ "pattern": "^(?:(?!\\b([Aa][Ll][Ww][Aa][Yy][Ss]|[Nn][Ee][Vv][Ee][Rr]|[Ee][Vv][Ee][Rr][Yy]|[Aa][Ll][Ll] [Cc][Aa][Ss][Ee][Ss]|[Ii][Nn] [Aa][Ll][Ll]|[Cc][Aa][Nn][Nn][Oo][Tt] [Bb][Ee]|[Cc][Aa][Nn] [Nn][Oo][Tt] [Bb][Ee]|[Ii][Mm][Pp][Oo][Ss][Ss][Ii][Bb][Ll][Ee]|[Ii][Mm][Pp][Oo][Ss][Ss][Ii][Bb][Ii][Ll][Ii][Tt][Yy]|[Nn][Oo] [Ww][Aa][Yy] [Tt][Oo]|[Tt][Hh][Ee][Rr][Ee] [Ii][Ss] [Nn][Oo] [Ww][Aa][Yy]|[Tt][Hh][Ee][Rr][Ee] [Ii][Ss] [Nn][Oo] [Pp][Aa][Tt][Hh]|[Gg][Uu][Aa][Rr][Aa][Nn][Tt][Ee][Ee][Dd])\\b)[\\s\\S])*$"
370
+ }
371
+ }
372
+ },
373
+ {
374
+ "$comment": "M3-P4 FIX ROUND 4. THE OPEN QUESTION WHOSE PROSE CARRIES A GUARDED TOKEN, WITH THE AUTHOR SAYING WHY IT IS STILL OPEN. NO REVIEWER FOUND THIS SITE: it was derived by round 4's enumeration, which took the sibling branch's claim that the pattern `KEEPS CHEAP` the honest open question and tried to break it instead of believing it. It broke on the first try and on four members, each of which the round validated against the shipped template and recorded with its exit code in delivery/work-history/m3-p4.md. The one that matters most is `I did not check every call site, so this is still open`, because CLAUDE.md's fix-round contract asks in so many words for a statement of what a derivation did NOT cover, and that sentence carries `every`. The others: an open question DENYING an impossibility (`I could not show that this is impossible`), one QUOTING somebody else's assertion, and one naming the universal it did not test (`whether the lease is never held twice is open`). Each was refused by the sibling branch, and `impossibility` was no escape because that kind owes an executed construction the author does not have, so the honest record was unwritable rather than merely expensive. WHY A DECLARED FIELD RATHER THAN A CLEVERER PATTERN: the tokens are refused wherever they appear, and English negates, quotes and hedges them in open-endedly many ways, so any list of permitted negations would be the same underived universal one level up. That is the mechanism the round-3 arbitration ruled on and it is not repeated here. `still-open-because` moves the discrimination to the one party that knows the answer. WHAT IT DOES NOT REACH, all three measured with exit codes in delivery/work-history/m3-p4.md rather than argued. ONE: an author who wants to smuggle a settlement in can write a sentence here too, and the round wrote `This arm cannot be forced here` with a declaration and measured exit 0. It is no longer free and no longer invisible; whether the sentence is true is M3-P7's clean-room checklist, not a keyword. TWO: a REDUNDANT declaration validates here, unlike the two sibling repairs in this round. A statement free of all thirteen tokens carrying a `still-open-because` matches this branch alone (the branch above sets `additionalProperties: false` and does not name the field), so `oneOf`'s exactly-one rule does not bite, where at `#/$defs/gateResult` and at `schemas/work-history.schema.json`'s `$defs/verificationFirst` the equivalent redundancy IS a rejection. Closing it means giving this branch's `statement` the POSITIVE complement of the sibling's pattern, which is a third hand-maintained copy of the same alternation and the drift hazard `#/$defs/finding/oneOf/0/properties/analysis` already documents; a redundant declaration hides nothing, so the asymmetry is declared rather than paid for. THREE: the residue the sibling branch names is untouched, since a statement carrying none of the thirteen tokens still passes there.",
375
+ "type": "object",
376
+ "additionalProperties": false,
377
+ "required": ["id", "kind", "statement", "still-open-because"],
378
+ "properties": {
379
+ "id": { "type": "string", "minLength": 1, "pattern": "\\S" },
380
+ "kind": { "type": "string", "const": "open-question" },
381
+ "statement": { "type": "string", "minLength": 1, "pattern": "\\S" },
382
+ "still-open-because": { "type": "string", "minLength": 1, "pattern": "\\S" }
383
+ }
384
+ }
385
+ ]
386
+ },
387
+ "settledBy": {
388
+ "$comment": "The base shape, so the claim object can name the property. Which of the two forms applies is decided by the claim's kind, in the oneOf above.",
389
+ "oneOf": [
390
+ { "$ref": "#/$defs/settledByCounterExperiment" },
391
+ { "$ref": "#/$defs/settledByConstruction" }
392
+ ]
393
+ },
394
+ "settledByCounterExperiment": {
395
+ "type": "object",
396
+ "additionalProperties": false,
397
+ "required": ["counter-experiment"],
398
+ "properties": {
399
+ "counter-experiment": { "type": "string", "minLength": 1, "pattern": "\\S" }
400
+ }
401
+ },
402
+ "settledByConstruction": {
403
+ "type": "object",
404
+ "additionalProperties": false,
405
+ "required": ["executed-construction"],
406
+ "properties": {
407
+ "executed-construction": {
408
+ "type": "object",
409
+ "additionalProperties": false,
410
+ "required": ["command", "exit-code", "output"],
411
+ "properties": {
412
+ "command": { "type": "string", "minLength": 1, "pattern": "\\S" },
413
+ "exit-code": { "type": "integer" },
414
+ "output": { "type": "string", "minLength": 1, "pattern": "\\S" }
415
+ }
416
+ }
417
+ }
418
+ },
419
+ "fixRound": {
420
+ "$comment": "CLAUDE.md's fix-round contract, D-M3-30, plan step 1c. SHARED BY $ref WITH schemas/work-history.schema.json. THE MEASURED EVIDENCE, recorded here so a future reader finds the reason rather than the rule: a throughput analysis of M1 measured sixteen completed fix rounds, thirteen were re-reviewed, and TWELVE of those thirteen produced a new finding attributable to the round itself; the dominant shape, roughly a third of the milestone's elapsed time, is that the fix addressed the INSTANCE the reviewer named when the defect was the MECHANISM (M1-P3 chained four rounds that way, M1-P5 four, M1-P6 two). `not-covered` is REQUIRED rather than optional because a search whose scope is wrong returns an empty result indistinguishable from an absence of defects, and this project was bitten by that three times: `state/session.lock` probed when the lease is `state/orchestrator.lock`; an inventory scoped to `tasks/`, `state/` and `worktrees/` while the missed path sat at the fleet root; a usage error read as a clean result. M3-P7's clean-room checklist makes `not-covered` the reviewer's FIRST probe, which is the other half of the same rule. NO DERIVED CHECK GUARDS THIS DEFINITION EITHER, for the same reason given at `$defs/claim`: its contract is entirely keywords, and keywords travel through the `$ref` into schemas/work-history.schema.json while derived checks do not.",
421
+ "type": "object",
422
+ "additionalProperties": false,
423
+ "required": ["mechanism", "derivation", "not-covered"],
424
+ "properties": {
425
+ "mechanism": {
426
+ "$comment": "The MECHANISM, not the finding. 'A FIFO at the beacon hangs the guard' is a finding; 'reading a path whose type has not been established' is the mechanism, and the round fixes the second. NO SCHEMA CAN TELL THE TWO APART and this field does not pretend to: the judgement is M3-P7's `fix-round-mechanism-named` probe.",
427
+ "type": "string",
428
+ "minLength": 1,
429
+ "pattern": "\\S"
430
+ },
431
+ "derivation": {
432
+ "type": "object",
433
+ "additionalProperties": false,
434
+ "required": ["command", "output"],
435
+ "properties": {
436
+ "command": {
437
+ "$comment": "The exact command that enumerates every call site of the mechanism.",
438
+ "type": "string",
439
+ "minLength": 1,
440
+ "pattern": "\\S"
441
+ },
442
+ "output": {
443
+ "$comment": "The FULL output of that command, not a summary of it. minLength and the non-whitespace pattern reach ABSENT and EMPTY and reach nothing else. THREE MEMBERS OF THE CLASS THEY DO NOT REACH, named because listing one invites a reader to think the other two were considered and closed (M3-P4 round-1 finding CR-004): a one-line SUMMARY (`11 call sites found, all reviewed and safe.`); a REAL BUT TRUNCATED capture ending `... (remaining 10 sites elided for brevity)`; and a real capture OF A DIFFERENT COMMAND than the one recorded beside it. All three satisfy both keywords, and the third is not reachable by any keyword at all, since relating an output to the command that produced it needs the command to be RUN. The multi-line property is asserted of the SHIPPED TEMPLATE by a registered test (criterion 2d(d)), which is a claim about this repository's example and not about an arbitrary instance.",
444
+ "type": "string",
445
+ "minLength": 1,
446
+ "pattern": "\\S"
447
+ }
448
+ }
449
+ },
450
+ "not-covered": {
451
+ "$comment": "The regions the derivation EXCLUDED, and why. Criterion 2e's array-element member when this object sits inside work-history's fix-round[].",
452
+ "type": "string",
453
+ "minLength": 1,
454
+ "pattern": "\\S"
455
+ }
456
+ }
457
+ },
458
+ "deviation": {
459
+ "$comment": "R-057a.",
460
+ "type": "object",
461
+ "additionalProperties": false,
462
+ "required": ["plan-clause", "why"],
463
+ "properties": {
464
+ "plan-clause": { "type": "string", "minLength": 1, "pattern": "\\S" },
465
+ "why": { "type": "string", "minLength": 1, "pattern": "\\S" }
466
+ }
467
+ },
468
+ "honestFailure": {
469
+ "$comment": "R-088. An incident cannot be recorded without the three things that make it useful: what caused it, how long the exposure lasted, and what structural change stops it recurring.",
470
+ "type": "object",
471
+ "additionalProperties": false,
472
+ "required": ["cause", "exposure-window", "structural-fix"],
473
+ "properties": {
474
+ "cause": { "type": "string", "minLength": 1, "pattern": "\\S" },
475
+ "exposure-window": {
476
+ "$comment": "Criterion 2e's WHITESPACE-ONLY BLOCK SCALAR member: a YAML block scalar holding a newline and two spaces satisfies minLength and is caught by the pattern, which is why both keywords are here and not one.",
477
+ "type": "string",
478
+ "minLength": 1,
479
+ "pattern": "\\S"
480
+ },
481
+ "structural-fix": { "type": "string", "minLength": 1, "pattern": "\\S" }
482
+ }
483
+ },
484
+ "environmentalClaim": {
485
+ "$comment": "R-085: an environmental excuse without evidence is unrepresentable. `minItems: 1` is the guard; an empty evidence array is the dangerous instance criterion 2(b) witnesses.",
486
+ "type": "object",
487
+ "additionalProperties": false,
488
+ "required": ["claim", "evidence"],
489
+ "properties": {
490
+ "claim": { "type": "string", "minLength": 1, "pattern": "\\S" },
491
+ "evidence": {
492
+ "type": "array",
493
+ "minItems": 1,
494
+ "items": { "$ref": "#/$defs/evidence" }
495
+ }
496
+ }
497
+ },
498
+ "gateResult": {
499
+ "$comment": "R-049, R-086, R-048. SHARED BY $ref WITH schemas/work-history.schema.json, which reaches this definition under the DIFFERENT KEY `gate-evidence`. 'ALL GREEN' ONLY EVER MEANS THE WRAPPER'S EXIT CODE, expressed as a schema rule rather than as an instruction an agent can forget, and the coupling is an if/then on the SAME OBJECT so a `green` cannot be constructed while the exit code lives somewhere else in the document. A GREEN ALSO CANNOT REPORT A FAILURE: `failed` is pinned to 0 by the same if/then, because the coupling that was built first bound green to the exit code and left the record free to say something else two lines below, and a green carrying 400 failures balanced the arithmetic and validated (M3-P4 round-1 finding CR-003). THE COUNTS TRAVEL WITH IT rather than instead of it (plan section 1.7): the M2-P3 wrapper binds the child's exit code to the parsed counts in both directions, and R-048 is the silently-dropped-tests case, so `discovered` is checked against the five buckets by the derived check `report-parity-arithmetic` (Kind B: no keyword computes arithmetic over sibling fields). THAT CHECK RUNS ON BOTH TYPES, and saying so is only safe because it is now true: a derived check is registered PER TYPE and reads a TYPE-SPECIFIC KEY while these `$defs` are SHARED ACROSS TYPES, so sharing a definition does NOT share its check, and until M3-P4 fix round 2 this sentence was false for every work history (finding CR-001). The check declares `guards: [report.schema.json#/$defs/gateResult]` and `alsoTypes: [work-history]`, and test/report-contract.test.ts enumerates every cross-document `$ref` in schemas/ and fails when a guarded definition is reachable from a type the check does not list, so this sentence cannot go stale silently again. THE SIXTH BUCKET, `todo`, IS NOW A FIELD: the M2-P3 wrapper's identity is `pass + fail + skipped + todo + did-not-run == reported` and the plan named five counts, so a run reporting `todo > 0` could not be recorded at all without breaking parity; the orchestrator's arbitration of M3-P4 round 1 amended the plan's field list to six rather than leaving a contract that refuses a legitimate run. ONE RESIDUE, STILL DELIBERATELY OPEN AND NOT TO BE CONFUSED WITH THE CR-003 CASE ABOVE: a result that is NOT green may carry `wrapper-exit-code: 0`, because a wrapper can exit 0 while the author judges the run not green, which is exactly the R-048 shape, and closing that direction would make the honest record unwritable. A SECOND RESIDUE: a green carrying `did-not-run > 0` or `skipped > 0` is still accepted, because a skipped test is a legitimate part of a green run in this repository (the floor-gated tests) and a cancelled one may be reported beside a green wrapper exit; only `failed` is pinned, which is the direction the finding named and the only one where the contradiction is unambiguous. A THIRD RESIDUE, FOUND BY THE FIX ROUND 2 DERIVATION RATHER THAN BY A REVIEWER, and it is CR-002's mechanism wearing this definition's clothes: the `if` keys off a VALUE THE AUTHOR CHOOSES, so the whole obligation (the exit code, the pinned `failed`, the six counts) was escaped by writing `result: red` or `amber` or `error`, each of which owed NOTHING. FIX ROUND 2 WROTE THAT THIS `cannot be closed here` AND THAT SENTENCE WAS HALF FALSE, which is corrected rather than quietly deleted because the round's reason for it was a class argument that did not hold across the class (finding DV-003, and DV-002 at the sibling site in schemas/work-history.schema.json). THE COUNTS HALF STANDS: a red run frequently has no counts to give and demanding them would make the honest failure unwritable, so nothing here requires them. THE EXIT CODE HALF DID NOT STAND, and the `oneOf` below now prices it: a non-green result carries the exit code or says why there is none. ROUND 3 PRICED IT THE WRONG WAY AND ROUND 4 CORRECTED THAT rather than deleting the history of it: round 3 required the exit code of `red`, `amber` and `error` alike, on the argument that a gate which RAN has one by construction, and that argument was a universal over a class nobody had enumerated (finding DV3-001). Round 4 enumerated the class from the RUNNER, which is what produces these records, and found four members with no exit code to give; they are listed at `no-wrapper-exit-code` above and derived in full in delivery/work-history/m3-p4.md. FOUR IS WHAT ROUND 4 EXAMINED AND NOT THE SIZE OF THE CLASS, corrected in fix round 5 on clean-room finding CR-C-1: eight of the seventeen `errorResult` call sites in src/gates/run.ts went undiscussed in a grep the round itself pasted, four of them have since been forced through the real runner, and four more are named and unforced. The bound is stated in full at `no-wrapper-exit-code` below, and it does not weaken the branch, because the author declares the member rather than the schema guessing it. WHAT REMAINS OPEN AT THIS SITE AFTER THAT, stated so the next reader does not have to rediscover it: the obligation is one short field either way, so it does not equalise the branches, and an author who does not want to write the six counts can still record `red`. What closes THAT is a reviewer comparing the record to the run, which is M3-P7's territory rather than this schema's.",
500
+ "type": "object",
501
+ "additionalProperties": false,
502
+ "required": ["gate", "result"],
503
+ "properties": {
504
+ "gate": { "type": "string", "minLength": 1, "pattern": "\\S" },
505
+ "result": {
506
+ "type": "string",
507
+ "enum": ["green", "red", "amber", "not-applicable", "error"]
508
+ },
509
+ "wrapper-exit-code": { "type": "integer" },
510
+ "no-wrapper-exit-code": {
511
+ "$comment": "M3-P4 FIX ROUND 4, FINDING DV3-001. THE AUTHOR DECLARES WHICH MEMBER THE RECORD IS, because the schema cannot see it. A non-green result either gives the wrapper's exit code or says here why there is none; it may not do both and it may not do neither. Round 3 guessed the member from the status word and the guess was wrong for three DERIVED members, each captured in delivery/work-history/m3-p4.md rather than argued: a gate the runner refuses before any child is spawned (src/gates/run.ts:844, the missing-parameter branch, seven such call sites sit above the spawn at src/gates/run.ts:943); a spawn that fails outright so no process ever exists (src/gates/run.ts:992); and a child terminated by a signal, where `child.status` is null and there is no exit code to report (src/gates/run.ts:999). `amber` is a fourth: it is in this enum and it is not one of the runner's four statuses (src/gates/result.ts:46), so no producer defines an exit code for it at all. WHAT THAT ENUMERATION DID NOT COVER, ADDED IN FIX ROUND 5 ON CLEAN-ROOM FINDING CR-C-1, because the count above was published as though it were the class and it is not: `errorResult` has SEVENTEEN call sites in src/gates/run.ts, SEVEN above the spawn at src/gates/run.ts:943 and TEN below it, and round 4's prose examined TWO of the ten (src/gates/run.ts:992 and src/gates/run.ts:999). The other eight below-spawn sites were present in round 4's own pasted grep, and a search of that round's prose for each of the ten line numbers outside the paste finds mentions for exactly two of them, so the eight are unexamined rather than examined and dismissed. A clean-room reviewer then forced four of them through the real runner (a gate that runs and writes no record, src/gates/run.ts:1015; a record naming a different gate, src/gates/run.ts:1051; a record failing its own schema, src/gates/run.ts:1043; an exit code disagreeing with the recorded status, src/gates/run.ts:1060), each a real `status: error` record with no exit-code field, and four more (src/gates/run.ts:989, src/gates/run.ts:1009, src/gates/run.ts:1023, src/gates/run.ts:1035) are named and NOT forced. Fix round 5 reproduced the src/gates/run.ts:1015 member itself through `tiphys gates run` rather than restating the review. SO THE NUMBER IS A FLOOR AND NOT A TOTAL, AND THE BRANCH BELOW DOES NOT DEPEND ON IT: the repair is that the AUTHOR DECLARES which member the record is, which covers members nobody has enumerated, including the three named-and-unforced ones and any a later change to the runner adds. That is why the correction here is to the claim and not to the schema. TWO RESIDUES, MEASURED IN ROUND 4 RATHER THAN REASONED ABOUT, both with their exit codes in delivery/work-history/m3-p4.md. FIRST: a `green` or a `not-applicable` carrying this field validates, because the third `oneOf` branch constrains only `result` there; the field is meaningless on those two and nothing refuses it. SECOND: this field is prose, so a FALSE reason validates exactly as a fabricated integer did, and the round wrote the false one and measured exit 0 rather than asserting the point. What this buys is that the omission is no longer silent and the substitute is a sentence a reviewer reads.",
512
+ "type": "string",
513
+ "minLength": 1,
514
+ "pattern": "\\S"
515
+ },
516
+ "discovered": { "type": "integer" },
517
+ "passed": { "type": "integer" },
518
+ "failed": { "type": "integer" },
519
+ "skipped": { "type": "integer" },
520
+ "todo": { "type": "integer" },
521
+ "did-not-run": { "type": "integer" }
522
+ },
523
+ "if": {
524
+ "type": "object",
525
+ "required": ["result"],
526
+ "properties": { "result": { "const": "green" } }
527
+ },
528
+ "then": {
529
+ "type": "object",
530
+ "required": [
531
+ "wrapper-exit-code",
532
+ "discovered",
533
+ "passed",
534
+ "failed",
535
+ "skipped",
536
+ "todo",
537
+ "did-not-run"
538
+ ],
539
+ "properties": {
540
+ "wrapper-exit-code": { "type": "integer", "const": 0 },
541
+ "discovered": { "type": "integer" },
542
+ "passed": { "type": "integer" },
543
+ "failed": { "type": "integer", "const": 0 },
544
+ "skipped": { "type": "integer" },
545
+ "todo": { "type": "integer" },
546
+ "did-not-run": { "type": "integer" }
547
+ }
548
+ },
549
+ "oneOf": [
550
+ {
551
+ "$comment": "BRANCH 1 OF THREE, THE EXIT CODE IS GIVEN. Round 3 (finding DV-003) closed the exit-code half of the third residue with a TWO-branch split keyed on the status word, justified by `a gate that RAN and did not pass has a wrapper exit code by construction, so requiring its PRESENCE refuses no honest record`. That sentence quantified over a class no round enumerated, and round 4 enumerated it from the PRODUCER instead of from the schema: the derivation and its full output are in delivery/work-history/m3-p4.md, and it names four members for which no exit code exists (finding DV3-001). ROUND 5 CORRECTION (clean-room finding CR-C-1): those four are the members round 4 EXAMINED, not the class. The class is larger and its enumeration is open, which is stated with its call sites at the sibling `$comment` on `no-wrapper-exit-code`. The split below survives that correction because it stopped guessing: what replaced round 3's status-word guess is a DECLARATION by the author, and a declaration does not need the class to have been counted. So the split is no longer a guess about the class; the author declares which member the record is, by carrying either `wrapper-exit-code` or `no-wrapper-exit-code`. THIS BRANCH STILL CONSTRAINS PRESENCE AND NEVER VALUE, so the declared-open residue above (a non-green result carrying `wrapper-exit-code: 0`, the R-048 shape) is still accepted and a test asserts that rather than assuming it. CARRYING BOTH FIELDS IS A REJECTION, by the exactly-one rule of `oneOf`, and that is the same discipline `schemas/final-report.schema.json`'s `$defs/enumerableSection` applies to `none: true` over real entries: an exception marker declared where the exception does not apply is a misdeclaration. This `oneOf` sits BESIDE the `if`/`then` above rather than replacing it: branches 1 and 2 are disjoint from branch 3 on `result`, so the green obligations are untouched and each witness registered against them still names the keyword that does the work.",
552
+ "type": "object",
553
+ "required": ["result", "wrapper-exit-code"],
554
+ "properties": {
555
+ "result": { "type": "string", "enum": ["red", "amber", "error"] },
556
+ "wrapper-exit-code": { "type": "integer" }
557
+ }
558
+ },
559
+ {
560
+ "$comment": "BRANCH 2 OF THREE, THERE IS NO EXIT CODE AND THE RECORD SAYS WHY. This is the branch that four derived members need, and each of them is a real record this repository can produce rather than a hypothetical. IT IS NOT ONLY THOSE FOUR, and saying so is the whole point of fix round 5's correction of clean-room finding CR-C-1: the four are the members round 4 examined, at least four more have since been forced through the real runner and four more are named and unforced, and the full bound with its call sites is at the `$comment` on this `oneOf` above. THE BRANCH IS INDIFFERENT TO THE COUNT, which is why the correction is to the prose and not to the keyword: the author declares that there is no exit code and says why, so a member nobody has enumerated is writable on the day it first occurs. A claim of exhaustiveness here would have to be re-derived every time src/gates/run.ts changes, and a stale one reassures instead of guarding. WHAT IT COSTS THE HONEST AUTHOR: one short string, where round 3's shape cost them the record entirely. WHAT IT COSTS THE DISHONEST ONE: the silent omission that round 2 left free is still refused, so DV-003 does not reopen; what replaces the fabricated exit code is a sentence a reviewer reads. WHAT IT DOES NOT REACH, and this is a bound rather than a claim about every author: nothing here can tell a true reason from a false one, so `no-wrapper-exit-code: the dog ate it` validates, exactly as a fabricated integer validated before. That comparison is M3-P7's clean-room checklist, which is where this schema has put every question of the form `is the recorded thing the thing that happened`.",
561
+ "type": "object",
562
+ "required": ["result", "no-wrapper-exit-code"],
563
+ "properties": {
564
+ "result": { "type": "string", "enum": ["red", "amber", "error"] },
565
+ "no-wrapper-exit-code": { "type": "string", "minLength": 1, "pattern": "\\S" }
566
+ }
567
+ },
568
+ {
569
+ "$comment": "BRANCH 3 OF THREE, GREEN AND NOT-APPLICABLE. Green's obligations are the `if`/`then` above, which is why this branch adds nothing to it; putting them here as well would have made the green diagnostics report two failures for one defect. `not-applicable` owes nothing on purpose. ONE MEMBER DERIVED IN ROUND 4 AND DELIBERATELY NOT CLOSED HERE: a gate whose registry row declares `verified-by: clean-room-checklist` is never executed by the runner, which says so on its own stdout, so a reviewer judging one GREEN has no wrapper exit code either and the `if`/`then` above refuses that record. R-049 binds green to the wrapper's exit code and is the plan's letter, so weakening it is not an implementer's call; the open question of where a checklist gate's verdict belongs is handed back in delivery/work-history/m3-p4.md rather than answered by a schema change nobody asked for.",
570
+ "type": "object",
571
+ "required": ["result"],
572
+ "properties": {
573
+ "result": { "type": "string", "enum": ["green", "not-applicable"] }
574
+ }
575
+ }
576
+ ]
577
+ }
578
+ }
579
+ }