@tiphys/kernel 0.1.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.
- package/AGENTS.md +56 -4
- package/assurance-modes.yaml +23 -2
- package/dist/bin/tiphys.js +86 -8
- package/dist/src/adapters/load.d.ts +202 -0
- package/dist/src/adapters/load.js +440 -0
- package/dist/src/brief.js +27 -20
- package/dist/src/checks.d.ts +720 -9
- package/dist/src/checks.js +1874 -163
- package/dist/src/cli.js +11 -0
- package/dist/src/commands/brief.js +27 -4
- package/dist/src/commands/cutover.d.ts +35 -0
- package/dist/src/commands/cutover.js +448 -0
- package/dist/src/commands/doctor.d.ts +229 -0
- package/dist/src/commands/doctor.js +968 -27
- package/dist/src/commands/init.d.ts +3 -3
- package/dist/src/commands/init.js +57 -8
- package/dist/src/commands/lock.d.ts +33 -0
- package/dist/src/commands/lock.js +117 -6
- package/dist/src/commands/next.d.ts +130 -0
- package/dist/src/commands/next.js +597 -0
- package/dist/src/commands/pool.js +12 -1
- package/dist/src/commands/resume.d.ts +1 -0
- package/dist/src/commands/resume.js +88 -0
- package/dist/src/commands/spawn.js +51 -2
- package/dist/src/commands/status.d.ts +6 -4
- package/dist/src/commands/status.js +6 -4
- package/dist/src/commands/sync.d.ts +47 -0
- package/dist/src/commands/sync.js +341 -0
- package/dist/src/commands/teardown.js +10 -2
- package/dist/src/commands/validate.js +70 -0
- package/dist/src/cutover.d.ts +584 -0
- package/dist/src/cutover.js +1444 -0
- package/dist/src/exclusion.d.ts +389 -0
- package/dist/src/exclusion.js +843 -0
- package/dist/src/exec/env.d.ts +152 -2
- package/dist/src/exec/env.js +146 -2
- package/dist/src/fleet.d.ts +172 -0
- package/dist/src/fleet.js +219 -1
- package/dist/src/gates/citations.js +7 -1
- package/dist/src/gates/coverage.d.ts +113 -22
- package/dist/src/gates/coverage.js +166 -31
- package/dist/src/gates/credentials.d.ts +159 -0
- package/dist/src/gates/credentials.js +221 -2
- package/dist/src/gates/gate-classes.d.ts +56 -0
- package/dist/src/gates/gate-classes.js +633 -0
- package/dist/src/gates/merge-preconditions.d.ts +319 -0
- package/dist/src/gates/merge-preconditions.js +932 -0
- package/dist/src/gates/red-witness.js +105 -13
- package/dist/src/gates/run.d.ts +49 -1
- package/dist/src/gates/run.js +83 -5
- package/dist/src/gates/schemas/phase-declaration.schema.json +45 -0
- package/dist/src/gates/suite.js +48 -7
- package/dist/src/hooks.d.ts +55 -3
- package/dist/src/hooks.js +69 -6
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.js +30 -0
- package/dist/src/lock.d.ts +82 -4
- package/dist/src/lock.js +314 -22
- package/dist/src/model-resolution.d.ts +159 -0
- package/dist/src/model-resolution.js +307 -0
- package/dist/src/path-identity.d.ts +32 -0
- package/dist/src/path-identity.js +38 -0
- package/dist/src/pool.d.ts +197 -1
- package/dist/src/pool.js +289 -22
- package/dist/src/roles.d.ts +31 -0
- package/dist/src/roles.js +42 -0
- package/dist/src/spawn.d.ts +307 -2
- package/dist/src/spawn.js +690 -19
- package/dist/src/status.d.ts +27 -2
- package/dist/src/status.js +34 -5
- package/dist/src/task.d.ts +295 -55
- package/dist/src/task.js +125 -123
- package/dist/src/teardown.d.ts +7 -0
- package/dist/src/teardown.js +120 -12
- package/dist/src/validate.d.ts +44 -11
- package/dist/src/validate.js +44 -34
- package/dist/src/watcher.js +1 -11
- package/dist/src/witness/run.d.ts +32 -7
- package/dist/src/witness/run.js +76 -30
- package/dist/src/witness/spec.d.ts +168 -0
- package/dist/src/witness/spec.js +240 -18
- package/dist/tsconfig.src.tsbuildinfo +1 -1
- package/gate-registry.yaml +136 -0
- package/gates.manifest.json +63 -1
- package/package.json +18 -3
- package/roles/implementer.md +3 -0
- package/schemas/README.md +1 -0
- package/schemas/assurance-modes.schema.json +1 -1
- package/schemas/charter.schema.json +19 -0
- package/schemas/cutover-state.schema.json +64 -0
- package/schemas/executor-record.schema.json +36 -0
- package/schemas/model-resolution.schema.json +362 -0
- package/schemas/verdict.schema.json +9 -3
- package/schemas/write-bypass.schema.json +69 -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
|
+
}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"required": [
|
|
10
10
|
"kind",
|
|
11
11
|
"phase",
|
|
12
|
+
"head",
|
|
12
13
|
"verdict",
|
|
13
14
|
"produced-by",
|
|
14
15
|
"framing",
|
|
@@ -29,6 +30,11 @@
|
|
|
29
30
|
"minLength": 1,
|
|
30
31
|
"pattern": "\\S"
|
|
31
32
|
},
|
|
33
|
+
"head": {
|
|
34
|
+
"$comment": "M4-P10, discharging DR-0012 condition 1's head clause (delivery/decisions/DR-0012-delegated-merge-authority.md:22): two independent clean-room reviews exist FOR THE CURRENT HEAD. WHY IT IS REQUIRED RATHER THAN OPTIONAL, decided here because the plan left the choice open and the migration cost is not zero. Before this field the only thing scoping a set of verdicts to one head was the DIRECTORY the operator pointed the gate at, a convention recorded as a declared reading in delivery/work-history/m3-p9.md and enforced by nothing. An OPTIONAL head leaves that convention in place for every document that omits it, which is the whole existing population, so the gate would keep grouping by directory and the clause would stay asserted by a human while reading as checked. Required is the only spelling that removes the convention. WHY FORTY LOWERCASE HEX AND NOT AN ABBREVIATION. A short sha and a full sha are two spellings of ONE fact, and no canonical form reconciles them: nothing here can know that a seven-character value is a prefix of a forty-character sibling without resolving both against a repository this schema is never given. Two spellings of one head would then form two groups of one, which is the fail-open direction, because a group of one is never compared to anything. The pattern refuses the abbreviation at the document boundary instead. Case is refused here and ALSO folded downstream by the derived check's `establishField`, so the two layers agree rather than one relying on the other. WHAT IT DOES NOT BUY, stated rather than left to be found. Nothing here checks that the value names a commit that exists, or that it is the commit the reviewer actually read; the field makes the CLAIM explicit and comparable between two documents, it does not make it true. And a verdict cannot carry the sha of the commit that CONTAINS it, because committing the verdict changes the tree and therefore the sha. `head` names the commit that was REVIEWED, which is an ancestor of the commit carrying the verdict.",
|
|
35
|
+
"type": "string",
|
|
36
|
+
"pattern": "^[0-9a-f]{40}$"
|
|
37
|
+
},
|
|
32
38
|
"verdict": {
|
|
33
39
|
"$comment": "R-060's closed vocabulary, exactly two values. A third value such as 'approve with comments' is the shape that lets a fix round be skipped by wording, so the enum is the whole point of the field.",
|
|
34
40
|
"type": "string",
|
|
@@ -82,7 +88,7 @@
|
|
|
82
88
|
}
|
|
83
89
|
},
|
|
84
90
|
"if": {
|
|
85
|
-
"$comment": "THE DANGEROUS INSTANCE, criterion 4(a), inverted into the closed vocabulary's reach. The rule wanted is APPROVE implies no high finding, and the direct spelling needs `not`, which schemas/README.md does not permit. `contains` matches the NON-empty case, which is the polarity that works here: a set that CONTAINS a finding the review itself ranked high or critical is a set whose verdict must be FIX-ROUND-NEEDED. `type: array` is restated inside because ajv 8.20.0 under `strict: true` refuses `contains` in a subschema that has not declared its type at the same level.",
|
|
91
|
+
"$comment": "WIDENED TO `medium` BY M4-P10, AND THAT WIDENING IS THE POINT OF THE FIELD LIST BELOW. As shipped by M3-P7 this enum read [high, critical] while DR-0012 condition 2 (delivery/decisions/DR-0012-delegated-merge-authority.md:23) bars an unresolved finding at high OR MEDIUM. So a verdict reading APPROVE beside a finding the review itself ranked `medium` validated at exit 0, and the merge precondition the schema exists to carry was one severity wider than the schema. `low` is deliberately still mergeable, which is what that same line of DR-0012 says. THE DANGEROUS INSTANCE, criterion 4(a), inverted into the closed vocabulary's reach. The rule wanted is APPROVE implies no high finding, and the direct spelling needs `not`, which schemas/README.md does not permit. `contains` matches the NON-empty case, which is the polarity that works here: a set that CONTAINS a finding the review itself ranked high or critical is a set whose verdict must be FIX-ROUND-NEEDED. `type: array` is restated inside because ajv 8.20.0 under `strict: true` refuses `contains` in a subschema that has not declared its type at the same level.",
|
|
86
92
|
"properties": {
|
|
87
93
|
"findings": {
|
|
88
94
|
"type": "array",
|
|
@@ -92,7 +98,7 @@
|
|
|
92
98
|
"properties": {
|
|
93
99
|
"severity": {
|
|
94
100
|
"type": "string",
|
|
95
|
-
"enum": ["high", "critical"]
|
|
101
|
+
"enum": ["medium", "high", "critical"]
|
|
96
102
|
}
|
|
97
103
|
}
|
|
98
104
|
}
|
|
@@ -100,7 +106,7 @@
|
|
|
100
106
|
}
|
|
101
107
|
},
|
|
102
108
|
"then": {
|
|
103
|
-
"$comment": "So APPROVE beside a high or critical finding fails HERE, naming the verdict field. Restated in `properties` at this level because a bare `enum` under a name ajv cannot see declared is a compilation error under strictTypes.",
|
|
109
|
+
"$comment": "So APPROVE beside a medium, high or critical finding fails HERE, naming the verdict field (widened from high by M4-P10). Restated in `properties` at this level because a bare `enum` under a name ajv cannot see declared is a compilation error under strictTypes.",
|
|
104
110
|
"properties": {
|
|
105
111
|
"verdict": {
|
|
106
112
|
"type": "string",
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://tiphys.dev/schemas/write-bypass.schema.json",
|
|
4
|
+
"title": "Tiphys write-bypass declaration",
|
|
5
|
+
"$comment": "Kernel plan M4, M4-P9 (delivery/plan/kernel-plan-m4.md:1501), discharging M4-D-26. THE DOCUMENT IS THE CURRENT STATE AND IS REWRITTEN IN PLACE; the append-only evidence log beside it is a separate file that NO DECISION EVER READS, which is constraint C-1 made structural rather than promised. Reading the bypass state from the tail of that log is exactly what C-1 forbids, and a schema cannot forbid it, so the split is enforced by the decision function having no log parameter at all (plugin/src/hooks/project-write-block.ts). WHAT THIS DOCUMENT DOES REACH is the hazard the plan names first: a bypass with no expiry, so that the first infrastructure hotfix disables the project-write block permanently. `expiresAt` is REQUIRED and is an ABSOLUTE INSTANT, never a duration, so a bypass that never expires is unrepresentable rather than discouraged. WHAT IT CANNOT REACH, each with its instrument: (a) whether the expiry is in the future, which is a comparison against a clock and belongs to the reader (decideWrite takes `now` as a parameter for exactly that reason); (b) whether the listed paths lie inside the declared project, which compares sibling fields and is Kind B; (c) whether the declaration was written by an orchestrator rather than by an agent, which no document can establish about itself.",
|
|
6
|
+
"description": "What the orchestrator writes to <fleet>/write-bypass.json to permit a named, time-boxed set of working-tree writes in one project clone that the project-write block would otherwise refuse. A bypass is a FIRST-CLASS DECLARED ACT: it names the project, lists the paths explicitly, gives a reason, and expires at an instant it states.",
|
|
7
|
+
"type": "object",
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"required": [
|
|
10
|
+
"kind",
|
|
11
|
+
"contractVersion",
|
|
12
|
+
"project",
|
|
13
|
+
"paths",
|
|
14
|
+
"reason",
|
|
15
|
+
"expiresAt",
|
|
16
|
+
"declaredBy",
|
|
17
|
+
"declaredAt"
|
|
18
|
+
],
|
|
19
|
+
"properties": {
|
|
20
|
+
"kind": {
|
|
21
|
+
"$comment": "The artifact type, so `tiphys validate --type auto` resolves this document without being told, and so the ONE row in src/commands/validate.ts's TYPE_TABLE serves the `--type` table and the auto resolver at once (M3R-001).",
|
|
22
|
+
"type": "string",
|
|
23
|
+
"const": "write-bypass"
|
|
24
|
+
},
|
|
25
|
+
"contractVersion": {
|
|
26
|
+
"$comment": "A closed set rather than a minimum, for the reason every other shipped schema records: the authoring vocabulary has no `minimum` keyword.",
|
|
27
|
+
"type": "string",
|
|
28
|
+
"enum": ["1"]
|
|
29
|
+
},
|
|
30
|
+
"project": {
|
|
31
|
+
"$comment": "The ABSOLUTE path of the one project working tree this declaration is about. One declaration, one project: a bypass covering several projects at once is the shape that quietly becomes permanent, because the reason that justified it for the first stops being read for the rest.",
|
|
32
|
+
"type": "string",
|
|
33
|
+
"minLength": 1,
|
|
34
|
+
"pattern": "^/"
|
|
35
|
+
},
|
|
36
|
+
"paths": {
|
|
37
|
+
"$comment": "THE EXPLICIT PATH LIST, and `minItems: 1` is what makes it explicit. An empty list would be a declaration that names a project and permits nothing, which reads as an off switch waiting for an edit. Each entry is absolute; the reader additionally requires each to lie inside `project`, which is a sibling-field comparison no keyword here reaches.",
|
|
38
|
+
"type": "array",
|
|
39
|
+
"minItems": 1,
|
|
40
|
+
"items": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1,
|
|
43
|
+
"pattern": "^/"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"reason": {
|
|
47
|
+
"$comment": "Why the hotfix could not go through the ordinary pipeline. Required because a bypass with no stated reason is indistinguishable, a week later, from a block that was simply switched off.",
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1,
|
|
50
|
+
"pattern": "\\S"
|
|
51
|
+
},
|
|
52
|
+
"expiresAt": {
|
|
53
|
+
"$comment": "THE ABSOLUTE EXPIRY INSTANT, and it is the single most load-bearing field in this document. The plan's hazard class opens with 'a bypass with no expiry, so the first hotfix disables the block permanently'; making this required is how that state stops being representable. It is an instant and never a duration, because a duration has to be added to something and the thing it would be added to is a clock this document has no way to read.",
|
|
54
|
+
"type": "string",
|
|
55
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]+)?Z$"
|
|
56
|
+
},
|
|
57
|
+
"declaredBy": {
|
|
58
|
+
"$comment": "Who declared it, verbatim. A bypass is an act by a role, and the evidence log records the writes it permitted; this is the only field that says who is answerable for them.",
|
|
59
|
+
"type": "string",
|
|
60
|
+
"minLength": 1,
|
|
61
|
+
"pattern": "\\S"
|
|
62
|
+
},
|
|
63
|
+
"declaredAt": {
|
|
64
|
+
"$comment": "When it was declared. Paired with `expiresAt` it gives the window a reviewer reads, and it is what every refusal names when the declaration did not apply.",
|
|
65
|
+
"type": "string",
|
|
66
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}([.][0-9]+)?Z$"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|