@tiphys/kernel 0.0.0 → 0.2.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 (207) hide show
  1. package/AGENTS.md +663 -0
  2. package/LICENSE +202 -0
  3. package/assurance-modes.yaml +299 -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 +92 -0
  11. package/dist/src/adapters/load.d.ts +202 -0
  12. package/dist/src/adapters/load.js +440 -0
  13. package/dist/src/brief.d.ts +32 -0
  14. package/dist/src/brief.js +66 -0
  15. package/dist/src/checklists.d.ts +169 -0
  16. package/dist/src/checklists.js +310 -0
  17. package/dist/src/checks.d.ts +1539 -0
  18. package/dist/src/checks.js +5025 -0
  19. package/dist/src/cli.d.ts +9 -0
  20. package/dist/src/cli.js +68 -0
  21. package/dist/src/commands/brief.d.ts +92 -0
  22. package/dist/src/commands/brief.js +343 -0
  23. package/dist/src/commands/checklist.d.ts +42 -0
  24. package/dist/src/commands/checklist.js +168 -0
  25. package/dist/src/commands/cutover.d.ts +35 -0
  26. package/dist/src/commands/cutover.js +448 -0
  27. package/dist/src/commands/doctor.d.ts +264 -0
  28. package/dist/src/commands/doctor.js +1546 -0
  29. package/dist/src/commands/gates.d.ts +9 -0
  30. package/dist/src/commands/gates.js +360 -0
  31. package/dist/src/commands/init.d.ts +30 -0
  32. package/dist/src/commands/init.js +177 -0
  33. package/dist/src/commands/lock.d.ts +34 -0
  34. package/dist/src/commands/lock.js +340 -0
  35. package/dist/src/commands/mode.d.ts +40 -0
  36. package/dist/src/commands/mode.js +134 -0
  37. package/dist/src/commands/next.d.ts +130 -0
  38. package/dist/src/commands/next.js +597 -0
  39. package/dist/src/commands/plan.d.ts +20 -0
  40. package/dist/src/commands/plan.js +105 -0
  41. package/dist/src/commands/pool.d.ts +1 -0
  42. package/dist/src/commands/pool.js +139 -0
  43. package/dist/src/commands/resume.d.ts +1 -0
  44. package/dist/src/commands/resume.js +88 -0
  45. package/dist/src/commands/spawn.d.ts +1 -0
  46. package/dist/src/commands/spawn.js +195 -0
  47. package/dist/src/commands/status.d.ts +21 -0
  48. package/dist/src/commands/status.js +122 -0
  49. package/dist/src/commands/sync.d.ts +47 -0
  50. package/dist/src/commands/sync.js +341 -0
  51. package/dist/src/commands/teardown.d.ts +1 -0
  52. package/dist/src/commands/teardown.js +87 -0
  53. package/dist/src/commands/tuition.d.ts +21 -0
  54. package/dist/src/commands/tuition.js +218 -0
  55. package/dist/src/commands/validate.d.ts +78 -0
  56. package/dist/src/commands/validate.js +430 -0
  57. package/dist/src/commands/watch.d.ts +1 -0
  58. package/dist/src/commands/watch.js +172 -0
  59. package/dist/src/cutover.d.ts +584 -0
  60. package/dist/src/cutover.js +1444 -0
  61. package/dist/src/exclusion.d.ts +389 -0
  62. package/dist/src/exclusion.js +843 -0
  63. package/dist/src/exec/env.d.ts +278 -0
  64. package/dist/src/exec/env.js +334 -0
  65. package/dist/src/fleet.d.ts +223 -0
  66. package/dist/src/fleet.js +298 -0
  67. package/dist/src/gates/adapters/http-json.d.ts +5 -0
  68. package/dist/src/gates/adapters/http-json.js +283 -0
  69. package/dist/src/gates/adapters/migrations-command.d.ts +1 -0
  70. package/dist/src/gates/adapters/migrations-command.js +373 -0
  71. package/dist/src/gates/citations.d.ts +408 -0
  72. package/dist/src/gates/citations.js +1169 -0
  73. package/dist/src/gates/coverage.d.ts +375 -0
  74. package/dist/src/gates/coverage.js +836 -0
  75. package/dist/src/gates/credentials.d.ts +233 -0
  76. package/dist/src/gates/credentials.js +752 -0
  77. package/dist/src/gates/deploy.d.ts +1 -0
  78. package/dist/src/gates/deploy.js +33 -0
  79. package/dist/src/gates/gate-classes.d.ts +56 -0
  80. package/dist/src/gates/gate-classes.js +633 -0
  81. package/dist/src/gates/manifest.d.ts +99 -0
  82. package/dist/src/gates/manifest.js +208 -0
  83. package/dist/src/gates/merge-preconditions.d.ts +319 -0
  84. package/dist/src/gates/merge-preconditions.js +932 -0
  85. package/dist/src/gates/migrations.d.ts +1 -0
  86. package/dist/src/gates/migrations.js +36 -0
  87. package/dist/src/gates/pin.d.ts +114 -0
  88. package/dist/src/gates/pin.js +154 -0
  89. package/dist/src/gates/red-witness.d.ts +22 -0
  90. package/dist/src/gates/red-witness.js +482 -0
  91. package/dist/src/gates/release.d.ts +283 -0
  92. package/dist/src/gates/release.js +820 -0
  93. package/dist/src/gates/result.d.ts +116 -0
  94. package/dist/src/gates/result.js +91 -0
  95. package/dist/src/gates/run.d.ts +614 -0
  96. package/dist/src/gates/run.js +1614 -0
  97. package/dist/src/gates/schemas/citation-config.schema.json +59 -0
  98. package/dist/src/gates/schemas/coverage-config.schema.json +77 -0
  99. package/dist/src/gates/schemas/gate-manifest.schema.json +125 -0
  100. package/dist/src/gates/schemas/gate-result.schema.json +160 -0
  101. package/dist/src/gates/schemas/phase-declaration.schema.json +87 -0
  102. package/dist/src/gates/schemas/release-record.schema.json +119 -0
  103. package/dist/src/gates/schemas/verifier-config.schema.json +101 -0
  104. package/dist/src/gates/schemas/witness-spec.schema.json +110 -0
  105. package/dist/src/gates/scope.d.ts +131 -0
  106. package/dist/src/gates/scope.js +1018 -0
  107. package/dist/src/gates/suite.d.ts +217 -0
  108. package/dist/src/gates/suite.js +968 -0
  109. package/dist/src/gates/validate.d.ts +121 -0
  110. package/dist/src/gates/validate.js +414 -0
  111. package/dist/src/hooks.d.ts +84 -0
  112. package/dist/src/hooks.js +125 -0
  113. package/dist/src/index.d.ts +31 -0
  114. package/dist/src/index.js +30 -0
  115. package/dist/src/liveness.d.ts +321 -0
  116. package/dist/src/liveness.js +396 -0
  117. package/dist/src/lock.d.ts +256 -0
  118. package/dist/src/lock.js +792 -0
  119. package/dist/src/model-resolution.d.ts +159 -0
  120. package/dist/src/model-resolution.js +307 -0
  121. package/dist/src/modes.d.ts +149 -0
  122. package/dist/src/modes.js +258 -0
  123. package/dist/src/path-identity.d.ts +34 -0
  124. package/dist/src/path-identity.js +48 -0
  125. package/dist/src/plan.d.ts +73 -0
  126. package/dist/src/plan.js +153 -0
  127. package/dist/src/pool.d.ts +326 -0
  128. package/dist/src/pool.js +988 -0
  129. package/dist/src/roles.d.ts +461 -0
  130. package/dist/src/roles.js +776 -0
  131. package/dist/src/spawn.d.ts +482 -0
  132. package/dist/src/spawn.js +1003 -0
  133. package/dist/src/status.d.ts +116 -0
  134. package/dist/src/status.js +148 -0
  135. package/dist/src/task.d.ts +504 -0
  136. package/dist/src/task.js +307 -0
  137. package/dist/src/teardown.d.ts +39 -0
  138. package/dist/src/teardown.js +422 -0
  139. package/dist/src/tuition.d.ts +159 -0
  140. package/dist/src/tuition.js +311 -0
  141. package/dist/src/validate.d.ts +263 -0
  142. package/dist/src/validate.js +742 -0
  143. package/dist/src/version.d.ts +3 -0
  144. package/dist/src/version.js +38 -0
  145. package/dist/src/watcher.d.ts +275 -0
  146. package/dist/src/watcher.js +849 -0
  147. package/dist/src/witness/run.d.ts +299 -0
  148. package/dist/src/witness/run.js +1373 -0
  149. package/dist/src/witness/spec.d.ts +270 -0
  150. package/dist/src/witness/spec.js +475 -0
  151. package/dist/tsconfig.src.tsbuildinfo +1 -0
  152. package/gate-registry.yaml +526 -0
  153. package/gates.manifest.json +257 -0
  154. package/package.json +72 -3
  155. package/role-model-config.yaml +88 -0
  156. package/roles/README.md +128 -0
  157. package/roles/_shared-dispatch-contract.md +87 -0
  158. package/roles/adversarial-plan-reviewer.md +80 -0
  159. package/roles/clean-room-reviewer.md +140 -0
  160. package/roles/implementer.md +463 -0
  161. package/roles/investigator.md +138 -0
  162. package/roles/plan-writer.md +95 -0
  163. package/schemas/README.md +82 -0
  164. package/schemas/assurance-modes.schema.json +264 -0
  165. package/schemas/charter.schema.json +185 -0
  166. package/schemas/checklist.schema.json +114 -0
  167. package/schemas/cutover-state.schema.json +64 -0
  168. package/schemas/decision-record.schema.json +88 -0
  169. package/schemas/executor-record.schema.json +36 -0
  170. package/schemas/final-report.schema.json +90 -0
  171. package/schemas/finding.schema.json +106 -0
  172. package/schemas/gate-registry.schema.json +260 -0
  173. package/schemas/mechanism-index.schema.json +94 -0
  174. package/schemas/model-resolution.schema.json +362 -0
  175. package/schemas/plan.schema.json +300 -0
  176. package/schemas/report.schema.json +579 -0
  177. package/schemas/role-brief.schema.json +105 -0
  178. package/schemas/role-model-config.schema.json +90 -0
  179. package/schemas/status-line.schema.json +40 -0
  180. package/schemas/tuition.schema.json +191 -0
  181. package/schemas/verdict.schema.json +295 -0
  182. package/schemas/work-history.schema.json +183 -0
  183. package/schemas/write-bypass.schema.json +69 -0
  184. package/templates/charter.example.yaml +54 -0
  185. package/templates/decision-record.example.yaml +27 -0
  186. package/templates/final-report.example.yaml +80 -0
  187. package/templates/plan.example.yaml +87 -0
  188. package/templates/report.example.yaml +236 -0
  189. package/templates/warnings.md +74 -0
  190. package/templates/work-history.example.yaml +185 -0
  191. package/tuition/README.md +76 -0
  192. package/tuition/T-001.yaml +48 -0
  193. package/tuition/T-002.yaml +51 -0
  194. package/tuition/T-003.yaml +100 -0
  195. package/tuition/T-004.yaml +52 -0
  196. package/tuition/T-005.yaml +72 -0
  197. package/tuition/T-006.yaml +81 -0
  198. package/tuition/T-007.yaml +56 -0
  199. package/tuition/T-008.yaml +111 -0
  200. package/tuition/T-009.yaml +50 -0
  201. package/tuition/T-015.yaml +36 -0
  202. package/tuition/T-016.yaml +36 -0
  203. package/tuition/T-017.yaml +46 -0
  204. package/tuition/T-018.yaml +84 -0
  205. package/tuition/T-021.yaml +40 -0
  206. package/tuition/T-022.yaml +36 -0
  207. package/tuition/mechanism-index.yaml +256 -0
@@ -0,0 +1,362 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://tiphys.dev/schemas/model-resolution.schema.json",
4
+ "title": "Tiphys model-resolution record",
5
+ "$comment": "R-075, and this is the half role-model-config.yaml reserved for the harness adapter: that document ships a TIER and a charter-override permission and says the adapter resolves them at M4. Kernel plan M4, M4-P7 (delivery/plan/kernel-plan-m4.md:1018), discharging M4-D-06's four parts (delivery/plan/m4-intake.md:732). THE SHAPE IS THE RELEASE RECORD'S, DELIBERATELY: src/gates/schemas/release-record.schema.json:26 requires a verbatim subject echo as a misattribution guard, and the same guard is required here because a family token attributed to the wrong task makes check-dual-review's decorrelation assertion an assertion about nothing. WHAT THIS DOCUMENT CANNOT REACH, and each has its instrument. (a) Whether the subject echo AGREES with the resolution field by field: that compares sibling fields and is Kind B, the derived check model-resolution-subject-echo in src/checks.ts (DR-0013 clause 8). (b) Whether an observation agrees with the echoed request: also Kind B, and it is enforced by the kernel-side reader in src/model-resolution.ts rather than here, because a schema-only guard passes exactly the half of M4-P7 criterion 6 that matters. (c) Whether the record EXISTS at all: an absent record is the consumer's error and is src/model-resolution.ts's, mirroring src/gates/release.ts:609. NO VENDOR NAME APPEARS HERE and none may: the family vocabulary lives in the plugin (plugin/src/vocabulary.ts) and this document dereferences only its IDENTITY.",
6
+ "description": "What a harness adapter writes to tasks/<id>/model-resolution.json when a turn ENDS, recording which model tier was requested, which the charter resolved, and which model family actually served the turn. Written at turn end and never at launch: a launch-time record can only carry what was REQUESTED and is a restatement of the request wearing the word resolved.",
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": [
10
+ "kind",
11
+ "contractVersion",
12
+ "writer",
13
+ "writtenAt",
14
+ "turnEnd",
15
+ "subject",
16
+ "resolution",
17
+ "resolved"
18
+ ],
19
+ "properties": {
20
+ "kind": {
21
+ "$comment": "The artifact type, so `tiphys validate --type auto` resolves this document without being told. It is also what makes the ONE row in src/commands/validate.ts's TYPE_TABLE serve the `--type` table and the auto resolver at once.",
22
+ "type": "string",
23
+ "const": "model-resolution"
24
+ },
25
+ "contractVersion": {
26
+ "$comment": "A closed set rather than a minimum, for the same reason as every other shipped schema: the authoring vocabulary has no `minimum` keyword.",
27
+ "type": "string",
28
+ "enum": ["1"]
29
+ },
30
+ "writer": {
31
+ "$comment": "The adapter that wrote the record, verbatim. The only artifact that ever says which harness resolved the turn.",
32
+ "type": "string",
33
+ "minLength": 1,
34
+ "pattern": "\\S"
35
+ },
36
+ "writtenAt": {
37
+ "$comment": "M4-P7 criterion 2. The instant the record was written, which is AFTER the turn ended. The comparison against turnEnd.endedAt is a sibling-field comparison, so it is Kind B and lives in the derived check; what this document buys is that neither instant can be absent.",
38
+ "type": "string",
39
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]+)?Z$"
40
+ },
41
+ "turnEnd": {
42
+ "$ref": "#/$defs/turnEnd"
43
+ },
44
+ "subject": {
45
+ "$ref": "#/$defs/subject"
46
+ },
47
+ "resolution": {
48
+ "$ref": "#/$defs/resolution"
49
+ },
50
+ "resolved": {
51
+ "$ref": "#/$defs/resolved"
52
+ }
53
+ },
54
+ "$defs": {
55
+ "turnEnd": {
56
+ "$comment": "M4-P7 criterion 2, and REQUIRED is the whole of what it buys. This is the turn-end record the generated hook wrote (src/hooks.ts:38), echoed verbatim. A record written at LAUNCH cannot carry it, because the turn has not ended and the file does not exist, so requiring it makes the dangerous state unrepresentable rather than merely discouraged.",
57
+ "type": "object",
58
+ "additionalProperties": false,
59
+ "required": ["endedAt", "exitCode"],
60
+ "properties": {
61
+ "endedAt": {
62
+ "type": "string",
63
+ "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]+)?Z$"
64
+ },
65
+ "exitCode": {
66
+ "type": "integer"
67
+ }
68
+ }
69
+ },
70
+ "subject": {
71
+ "$comment": "THE VERBATIM ECHO OF THE LAUNCH REQUEST, and it is the misattribution guard (src/gates/schemas/release-record.schema.json:26). Every field is copied from the ExecutorRequest the adapter was handed, with no normalisation: requestedTier crosses this seam exactly as role-model-config.yaml declared it. The echo is compared field by field against `resolution` BEFORE anything reads `resolved`, which is the order src/gates/release.ts applies to a release record and the order src/model-resolution.ts applies here.",
72
+ "type": "object",
73
+ "additionalProperties": false,
74
+ "required": ["taskId", "role", "requestedTier"],
75
+ "properties": {
76
+ "taskId": {
77
+ "type": "string",
78
+ "minLength": 1,
79
+ "pattern": "\\S"
80
+ },
81
+ "role": {
82
+ "type": "string",
83
+ "minLength": 1,
84
+ "pattern": "\\S"
85
+ },
86
+ "requestedTier": {
87
+ "$comment": "NAMES A TIER, NEVER A MODEL, for the reason role-model-config.schema.json records: a model id in a versioned kernel artifact goes stale in weeks and puts a vendor's product name inside the package. The vocabulary is NOT an enum here: role-model-config.yaml owns the tier vocabulary and a second copy of it in this document is a second thing to drift.",
88
+ "type": "string",
89
+ "minLength": 1,
90
+ "pattern": "\\S"
91
+ }
92
+ }
93
+ },
94
+ "resolution": {
95
+ "$comment": "M4-P7 criterion 7: the charter override is RESOLVED, not assumed. `tier` is the tier the resolver actually consumed; when a charter overrode the request it is the CHARTER's tier and `charterPath` says which document said so. The override CONDITION is two file reads (the role's charter-override permission and the charter's own tier for the role), so it is genuinely OBSERVED and the observation is recorded rather than asserted.",
96
+ "type": "object",
97
+ "additionalProperties": false,
98
+ "required": ["role", "tier", "overrideApplied", "provenance", "observation"],
99
+ "properties": {
100
+ "role": {
101
+ "type": "string",
102
+ "minLength": 1,
103
+ "pattern": "\\S"
104
+ },
105
+ "tier": {
106
+ "type": "string",
107
+ "minLength": 1,
108
+ "pattern": "\\S"
109
+ },
110
+ "overrideApplied": {
111
+ "type": "boolean"
112
+ },
113
+ "charterPath": {
114
+ "$comment": "Required exactly when the override applied, through the `oneOf` below. Echoed rather than summarised, so a reader can go and look at the document that changed the tier.",
115
+ "type": "string",
116
+ "minLength": 1,
117
+ "pattern": "\\S"
118
+ },
119
+ "charterTier": {
120
+ "type": "string",
121
+ "minLength": 1,
122
+ "pattern": "\\S"
123
+ },
124
+ "provenance": {
125
+ "$comment": "The override condition is readable from files, so `observed` is the only honest value here and the enum says so. That is NOT true of the resolved model identity, which is why `resolved.provenance` carries a wider vocabulary.",
126
+ "type": "string",
127
+ "enum": ["observed"]
128
+ },
129
+ "observation": {
130
+ "$ref": "#/$defs/overrideObservation"
131
+ }
132
+ },
133
+ "oneOf": [
134
+ {
135
+ "$comment": "Branch A: the charter overrode the role's configured tier, so the document that did it and the tier it named are both required. `overrideApplied` is restated in a sibling `properties` because ajv 8.20.0 under strict mode fails COMPILATION when `required` names a property not declared in `properties` at the same level, which is the pattern schemas/verdict.schema.json's root `oneOf` records.",
136
+ "required": ["overrideApplied", "charterPath", "charterTier"],
137
+ "properties": {
138
+ "overrideApplied": {
139
+ "type": "boolean",
140
+ "const": true
141
+ },
142
+ "charterPath": {
143
+ "type": "string",
144
+ "minLength": 1,
145
+ "pattern": "\\S"
146
+ },
147
+ "charterTier": {
148
+ "type": "string",
149
+ "minLength": 1,
150
+ "pattern": "\\S"
151
+ }
152
+ }
153
+ },
154
+ {
155
+ "$comment": "Branch B: no override, so no charter document may be cited. A record that claims both is a contradiction and the reader cannot tell which it meant, which is exactly how schemas/verdict.schema.json's hazardClassAddressed branches are written.",
156
+ "required": ["overrideApplied"],
157
+ "properties": {
158
+ "overrideApplied": {
159
+ "type": "boolean",
160
+ "const": false
161
+ }
162
+ }
163
+ }
164
+ ]
165
+ },
166
+ "overrideObservation": {
167
+ "$comment": "WHAT WAS ACTUALLY READ to decide the override, named by path so the claim is checkable rather than asserted. `consulted` IS THE HONEST HALF AND IT IS REQUIRED. A harness that has no tier-policy document in front of it has not observed a permission, and a record that reported `configPermission: allowed` in that state would be inventing a reading of a document it never opened, which is the laundering this whole record exists against one field over. The branch below therefore admits `consulted: false` with a reason in `detail` and NO configuration citation at all.",
168
+ "type": "object",
169
+ "additionalProperties": false,
170
+ "required": ["consulted", "detail"],
171
+ "properties": {
172
+ "consulted": {
173
+ "type": "boolean"
174
+ },
175
+ "configPath": {
176
+ "type": "string",
177
+ "minLength": 1,
178
+ "pattern": "\\S"
179
+ },
180
+ "configPermission": {
181
+ "$comment": "The role's own `charter-override` value, from role-model-config.yaml's closed enum. It is a copy of that document's vocabulary rather than a new one, so a reader comparing the record against the configuration compares like with like.",
182
+ "type": "string",
183
+ "enum": ["allowed", "forbidden"]
184
+ },
185
+ "detail": {
186
+ "type": "string",
187
+ "minLength": 1,
188
+ "pattern": "\\S"
189
+ }
190
+ },
191
+ "oneOf": [
192
+ {
193
+ "$comment": "Branch A: a tier-policy document was read, so the record says which one and what it said. `consulted` is restated in a sibling `properties` for the strictRequired reason recorded on the `resolution` branches.",
194
+ "required": ["consulted", "configPath", "configPermission"],
195
+ "properties": {
196
+ "consulted": {
197
+ "type": "boolean",
198
+ "const": true
199
+ },
200
+ "configPath": {
201
+ "type": "string",
202
+ "minLength": 1,
203
+ "pattern": "\\S"
204
+ },
205
+ "configPermission": {
206
+ "type": "string",
207
+ "enum": ["allowed", "forbidden"]
208
+ }
209
+ }
210
+ },
211
+ {
212
+ "$comment": "Branch B: nothing was consulted, and the record says so in `detail`. This is the shipped adapter's state today and it is recorded rather than papered over. A record in this branch can never carry `overrideApplied: true`, because the derived check model-resolution-subject-echo requires an observed `configPermission` of `allowed` before an override may be applied, and this branch cannot carry one.",
213
+ "required": ["consulted"],
214
+ "properties": {
215
+ "consulted": {
216
+ "type": "boolean",
217
+ "const": false
218
+ }
219
+ }
220
+ }
221
+ ]
222
+ },
223
+ "vocabulary": {
224
+ "$comment": "M4-P7 criteria 3 and 4, AND IT IS AN IDENTITY RATHER THAN A CONTENT. The kernel compares two records' family tokens only when both name the SAME vocabulary id, and it never looks inside one: a tier-to-model or model-to-family mapping in src/ is what would close off every harness that is not the one it names. The mapping lives in plugin/src/vocabulary.ts, which is where a vendor name is allowed to be.",
225
+ "type": "object",
226
+ "additionalProperties": false,
227
+ "required": ["id", "version"],
228
+ "properties": {
229
+ "id": {
230
+ "type": "string",
231
+ "minLength": 1,
232
+ "pattern": "\\S"
233
+ },
234
+ "version": {
235
+ "type": "integer"
236
+ }
237
+ }
238
+ },
239
+ "observation": {
240
+ "$comment": "M4-P7 criterion 6. The captured evidence behind an `observed` identity. `taskId` and `model` are what the reader compares against the echoed request and the claimed identity: an observation naming another task, or naming a model other than the one claimed, is a self-report wearing the word observed and is REFUSED by src/model-resolution.ts. `source` names the channel, and M4-P1 measured that the only channel here is harness-written and neither concurrency-stable nor tamper-proof, so nothing in this vocabulary implies attestation.",
241
+ "type": "object",
242
+ "additionalProperties": false,
243
+ "required": ["source", "taskId", "model", "detail"],
244
+ "properties": {
245
+ "source": {
246
+ "type": "string",
247
+ "minLength": 1,
248
+ "pattern": "\\S"
249
+ },
250
+ "taskId": {
251
+ "type": "string",
252
+ "minLength": 1,
253
+ "pattern": "\\S"
254
+ },
255
+ "model": {
256
+ "type": "string",
257
+ "minLength": 1,
258
+ "pattern": "\\S"
259
+ },
260
+ "detail": {
261
+ "type": "string",
262
+ "minLength": 1,
263
+ "pattern": "\\S"
264
+ }
265
+ }
266
+ },
267
+ "resolved": {
268
+ "$comment": "M4-P7 criteria 1, 3, 4 and 6. THE IDENTITY, AND ITS PROVENANCE IS PART OF IT. M4-D-06 was written to survive either answer to 'is the served model readable after the fact', and M4-P1 measured that it IS, through a harness-written transcript, while also measuring that the transcript can be EMPTY at hook time under load and that a process at the agent's uid can rewrite it. So `observed` is a real level above `self-reported` and below any notion of attestation, and `unresolved` exists because a resolver that falls back to the self-report on an empty transcript is green whenever the race does not fire and silently accepts a forgeable value when it does.",
269
+ "type": "object",
270
+ "additionalProperties": false,
271
+ "required": ["vocabulary", "provenance"],
272
+ "properties": {
273
+ "vocabulary": {
274
+ "$ref": "#/$defs/vocabulary"
275
+ },
276
+ "family": {
277
+ "$comment": "The vocabulary's family token, verbatim. This is the value the closeout copies into a verdict's `produced-by` byte for byte (criterion 8).",
278
+ "type": "string",
279
+ "minLength": 1,
280
+ "pattern": "\\S"
281
+ },
282
+ "model": {
283
+ "type": "string",
284
+ "minLength": 1,
285
+ "pattern": "\\S"
286
+ },
287
+ "provenance": {
288
+ "type": "string",
289
+ "enum": ["observed", "self-reported", "unresolved"]
290
+ },
291
+ "observation": {
292
+ "$ref": "#/$defs/observation"
293
+ },
294
+ "reason": {
295
+ "type": "string",
296
+ "minLength": 1,
297
+ "pattern": "\\S"
298
+ }
299
+ },
300
+ "oneOf": [
301
+ {
302
+ "$comment": "Branch A, `observed`: M4-P7 criterion 6 member one, and it is a SCHEMA rule on purpose. A record claiming `observed` with no observation field is a self-report laundered into a measurement, and the laundering is exactly the thing that must be unrepresentable rather than merely reviewed. Member TWO, an observation that CONTRADICTS the echoed request, is present-but-inconsistent and no keyword reaches it; it is the reader's.",
303
+ "required": ["provenance", "observation", "family", "model"],
304
+ "properties": {
305
+ "provenance": {
306
+ "type": "string",
307
+ "const": "observed"
308
+ },
309
+ "observation": {
310
+ "$ref": "#/$defs/observation"
311
+ },
312
+ "family": {
313
+ "type": "string",
314
+ "minLength": 1,
315
+ "pattern": "\\S"
316
+ },
317
+ "model": {
318
+ "type": "string",
319
+ "minLength": 1,
320
+ "pattern": "\\S"
321
+ }
322
+ }
323
+ },
324
+ {
325
+ "$comment": "Branch B, `self-reported`: the writer says which model it asked for and does not claim to have seen it. STATED PRECISELY BECAUSE THE OBVIOUS READING IS WRONG: this branch does NOT forbid an observation field. A branch that merely declines to require a property still admits it, and no keyword in the authoring vocabulary expresses an absence, so a self-reported record carrying an observation validates here. Refusing it is the reader's, in src/model-resolution.ts, and that division is the same one criterion 6 draws: a missing field is the schema's and a present-but-inconsistent one is not.",
326
+ "required": ["provenance", "family", "model"],
327
+ "properties": {
328
+ "provenance": {
329
+ "type": "string",
330
+ "const": "self-reported"
331
+ },
332
+ "family": {
333
+ "type": "string",
334
+ "minLength": 1,
335
+ "pattern": "\\S"
336
+ },
337
+ "model": {
338
+ "type": "string",
339
+ "minLength": 1,
340
+ "pattern": "\\S"
341
+ }
342
+ }
343
+ },
344
+ {
345
+ "$comment": "Branch C, `unresolved`: the observation channel was consulted and answered nothing. M4-P1 measured that arm happening twice in twelve resolutions, so it is a mode rather than a defensive flourish, and it must be LOUD. A reason is required. Whether a family or a model is also claimed is NOT reachable from here, for the reason recorded on branch B, so the reader refuses an unresolved record that names either; between them that is what stops an empty transcript from becoming a quiet fallback to the forgeable value.",
346
+ "required": ["provenance", "reason"],
347
+ "properties": {
348
+ "provenance": {
349
+ "type": "string",
350
+ "const": "unresolved"
351
+ },
352
+ "reason": {
353
+ "type": "string",
354
+ "minLength": 1,
355
+ "pattern": "\\S"
356
+ }
357
+ }
358
+ }
359
+ ]
360
+ }
361
+ }
362
+ }
@@ -0,0 +1,300 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://tiphys.dev/schemas/plan.schema.json",
4
+ "title": "Tiphys delivery plan",
5
+ "description": "The plan artifact of the orchestrated delivery process, section 1c. Kernel plan M3, M3-P1 step 2. Every object level sets additionalProperties: false, so a misspelled property is a failure and never a silently ignored field (criterion 5).",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "kind",
10
+ "status",
11
+ "baseline-commit",
12
+ "binding-rule",
13
+ "process-summary",
14
+ "standing-context",
15
+ "report-code-disagreement",
16
+ "phases",
17
+ "decisions",
18
+ "open-questions",
19
+ "parked"
20
+ ],
21
+ "properties": {
22
+ "kind": {
23
+ "$comment": "R-016. The discriminator tiphys validate --type auto reads.",
24
+ "type": "string",
25
+ "const": "plan"
26
+ },
27
+ "status": {
28
+ "type": "string",
29
+ "enum": ["draft", "approved", "superseded"]
30
+ },
31
+ "baseline-commit": {
32
+ "type": "string",
33
+ "pattern": "^[0-9a-f]{7,40}$"
34
+ },
35
+ "binding-rule": {
36
+ "$comment": "R-017. A required const carrying the process document's exact sentence (delivery/intake/orchestrated-delivery-process.md section 1c), so a plan cannot ship a softened paraphrase of the rule that keeps ten agents from improvising.",
37
+ "type": "string",
38
+ "const": "If it is not written here, it is not being made. Unanswered questions go to the orchestrator."
39
+ },
40
+ "process-summary": {
41
+ "type": "string",
42
+ "minLength": 1
43
+ },
44
+ "standing-context": {
45
+ "$comment": "R-018. What previous runs bought, what is already fixed, what the deploy state is.",
46
+ "type": "string",
47
+ "minLength": 1
48
+ },
49
+ "report-code-disagreement": {
50
+ "$comment": "R-011, and the input to the derived check plan-verification-first-present. An entry with verified: false whose owning phase has no verification-first step makes the plan invalid; that is a foreign-key lookup across two arrays and is Kind B, in src/checks.ts.",
51
+ "type": "array",
52
+ "items": {
53
+ "type": "object",
54
+ "additionalProperties": false,
55
+ "required": ["phase", "claim", "verified"],
56
+ "properties": {
57
+ "phase": {
58
+ "type": "string",
59
+ "pattern": "^M[0-9]+-P[0-9]+$"
60
+ },
61
+ "claim": {
62
+ "type": "string",
63
+ "minLength": 1
64
+ },
65
+ "verified": {
66
+ "type": "boolean"
67
+ }
68
+ }
69
+ }
70
+ },
71
+ "phases": {
72
+ "type": "array",
73
+ "minItems": 1,
74
+ "items": {
75
+ "$ref": "#/$defs/phase"
76
+ }
77
+ },
78
+ "decisions": {
79
+ "$comment": "R-021. Every non-obvious call, numbered, so disagreement targets a number instead of re-litigating prose.",
80
+ "type": "array",
81
+ "items": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "required": ["id", "statement"],
85
+ "properties": {
86
+ "id": {
87
+ "type": "string",
88
+ "pattern": "^D-[0-9]+$"
89
+ },
90
+ "statement": {
91
+ "type": "string",
92
+ "minLength": 1
93
+ }
94
+ }
95
+ }
96
+ },
97
+ "open-questions": {
98
+ "$comment": "D-7 and SC-009: an open question IS a decision record with status open, so the plan carries references and never a second parallel register.",
99
+ "type": "array",
100
+ "items": {
101
+ "type": "string",
102
+ "pattern": "^DR-[0-9]{4}$"
103
+ }
104
+ },
105
+ "parked": {
106
+ "type": "array",
107
+ "items": {
108
+ "type": "object",
109
+ "additionalProperties": false,
110
+ "required": ["item", "reason"],
111
+ "properties": {
112
+ "item": {
113
+ "type": "string",
114
+ "minLength": 1
115
+ },
116
+ "reason": {
117
+ "type": "string",
118
+ "minLength": 1
119
+ }
120
+ }
121
+ }
122
+ }
123
+ },
124
+ "$defs": {
125
+ "phase": {
126
+ "$comment": "R-019 plus the blueprint section 5 superset (conflicts-with, parallelizable). A strict SUPERSET of the M2-P4 scope auditor's phase-declaration projection, which tiphys plan project emits (D-M3-18).",
127
+ "type": "object",
128
+ "additionalProperties": false,
129
+ "required": [
130
+ "id",
131
+ "branch",
132
+ "intent",
133
+ "grounding",
134
+ "severity",
135
+ "verified-root-cause",
136
+ "steps",
137
+ "files-to-touch",
138
+ "extras",
139
+ "acceptance",
140
+ "hazard-classes",
141
+ "migrations",
142
+ "conflicts-with",
143
+ "parallelizable",
144
+ "citations"
145
+ ],
146
+ "properties": {
147
+ "id": {
148
+ "type": "string",
149
+ "pattern": "^M[0-9]+-P[0-9]+$"
150
+ },
151
+ "branch": {
152
+ "type": "string",
153
+ "pattern": "^claude/m[0-9]+-p[0-9]+-.+$"
154
+ },
155
+ "intent": {
156
+ "type": "string",
157
+ "minLength": 1
158
+ },
159
+ "grounding": {
160
+ "type": "string",
161
+ "minLength": 1
162
+ },
163
+ "severity": {
164
+ "type": "string",
165
+ "enum": ["low", "medium", "high", "critical"]
166
+ },
167
+ "verified-root-cause": {
168
+ "type": "string",
169
+ "minLength": 1
170
+ },
171
+ "steps": {
172
+ "type": "array",
173
+ "minItems": 1,
174
+ "items": {
175
+ "type": "object",
176
+ "additionalProperties": false,
177
+ "required": ["text"],
178
+ "properties": {
179
+ "text": {
180
+ "type": "string",
181
+ "minLength": 1
182
+ },
183
+ "kind": {
184
+ "$comment": "R-012. A step that confirms a claim before building on it. The derived check plan-verification-first-present requires one in any phase named by an unverified report-code-disagreement entry.",
185
+ "type": "string",
186
+ "const": "verification-first"
187
+ }
188
+ }
189
+ }
190
+ },
191
+ "files-to-touch": {
192
+ "$comment": "M2R-016. Literal paths, or literal directories with a trailing slash. A parenthetical gloss is permitted HERE, because this document is authored for humans; tiphys plan project strips it, and that stripping is the one place the plan's vocabulary and the auditor's meet.",
193
+ "type": "array",
194
+ "minItems": 1,
195
+ "items": {
196
+ "type": "string",
197
+ "minLength": 1
198
+ }
199
+ },
200
+ "extras": {
201
+ "$comment": "Projected as declaredExtras. Required rather than optional because the scope auditor treats declaredExtras as required; the standing pre-authorized extras (test/behaviors.json and the phase work history) are added by the auditor itself and are never listed here.",
202
+ "type": "array",
203
+ "items": {
204
+ "type": "string",
205
+ "minLength": 1
206
+ }
207
+ },
208
+ "acceptance": {
209
+ "$comment": "R-019: falsifiable statements. minItems 1 because a phase with no acceptance criterion has declared nothing to walk.",
210
+ "type": "array",
211
+ "minItems": 1,
212
+ "items": {
213
+ "type": "object",
214
+ "additionalProperties": false,
215
+ "required": ["id", "criterion"],
216
+ "properties": {
217
+ "id": {
218
+ "type": "string",
219
+ "minLength": 1
220
+ },
221
+ "criterion": {
222
+ "type": "string",
223
+ "minLength": 1
224
+ }
225
+ }
226
+ }
227
+ },
228
+ "hazard-classes": {
229
+ "$comment": "T-007, D-M3-32 and D-M3-35. minItems 1 and NOT optional: an optional field is the version of this rule that did not survive. T-007 records a phase meeting fifteen of fifteen executed criteria while live-locking every supervision command, and the reviewer who found it differed in its BRIEF, not in its model. addressed-by is resolved by the derived check plan-hazard-classes-addressed-by-resolves (section 2.6).",
230
+ "type": "array",
231
+ "minItems": 1,
232
+ "items": {
233
+ "type": "object",
234
+ "additionalProperties": false,
235
+ "required": ["id", "statement", "addressed-by"],
236
+ "properties": {
237
+ "id": {
238
+ "type": "string",
239
+ "minLength": 1
240
+ },
241
+ "statement": {
242
+ "type": "string",
243
+ "minLength": 1
244
+ },
245
+ "addressed-by": {
246
+ "$comment": "Either `criterion <id>` naming a criterion in the SAME phase's acceptance list, or one of section 2.6's three admissible reasons with its named target. The pattern is the Kind A half; resolving the target is Kind B.",
247
+ "type": "string",
248
+ "pattern": "^(criterion .+|judgment-property-of-prose: .+|state-not-entered: .+|later-phase: M[0-9]+-P[0-9]+)$"
249
+ }
250
+ }
251
+ }
252
+ },
253
+ "migrations": {
254
+ "type": "string",
255
+ "minLength": 1
256
+ },
257
+ "conflicts-with": {
258
+ "type": "array",
259
+ "items": {
260
+ "type": "string",
261
+ "minLength": 1
262
+ }
263
+ },
264
+ "parallelizable": {
265
+ "type": "boolean"
266
+ },
267
+ "citations": {
268
+ "type": "array",
269
+ "items": {
270
+ "type": "string",
271
+ "minLength": 1
272
+ }
273
+ },
274
+ "fill-in": {
275
+ "$comment": "R-014. Present and unfilled means the phase is valid for REVIEW and invalid for DISPATCH. That is a computed property of the slots rather than of one field, so it is Kind B: the derived check plan-dispatchable reports it.",
276
+ "type": "object",
277
+ "additionalProperties": false,
278
+ "required": ["filled", "root-cause", "fix-shape", "files"],
279
+ "properties": {
280
+ "filled": {
281
+ "type": "boolean"
282
+ },
283
+ "root-cause": {
284
+ "type": "string"
285
+ },
286
+ "fix-shape": {
287
+ "type": "string"
288
+ },
289
+ "files": {
290
+ "type": "array",
291
+ "items": {
292
+ "type": "string"
293
+ }
294
+ }
295
+ }
296
+ }
297
+ }
298
+ }
299
+ }
300
+ }