@event4u/agent-config 14.14.0 → 14.15.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +63 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/pr/create.md +16 -1
- package/dist/agent-src/contexts/execution/terminal-states.md +25 -4
- package/dist/agent-src/rules/missing-skill-recovery.md +2 -2
- package/dist/agent-src/rules/token-budget-discipline.md +2 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli-delegate/{chunk-ILMUPLGW.js → chunk-7RXIKF62.js} +159 -86
- package/dist/cli-delegate/cmd_conformance.js +513 -17
- package/dist/cli-delegate/cmd_doctor.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +5 -5
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1043 -390
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +703 -115
- package/package.json +1 -1
- package/src/config/assurance-capability-registry.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +62 -2
- package/src/config/gate-reachability-exemptions.json +1 -0
- package/src/config/gate-violation-baselines.json +4 -3
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/config/web-launch-readiness.json +4 -2
- package/src/scripts/_cli/_doctor_emit.ts +71 -0
- package/src/scripts/_cli/_doctor_strict.ts +163 -0
- package/src/scripts/_cli/cmd_doctor.ts +16 -37
- package/src/scripts/_dispatch.bash +11 -0
- package/src/scripts/_lib/branch_convergence.ts +186 -0
- package/src/scripts/_lib/context_observation.ts +132 -0
- package/src/scripts/_lib/continuation_ladder.ts +188 -0
- package/src/scripts/_lib/experiment_binding.ts +95 -0
- package/src/scripts/_lib/ignored_blocker.ts +8 -1
- package/src/scripts/_lib/outcome_envelope.ts +6 -1
- package/src/scripts/_lib/outcome_vocabularies.ts +135 -5
- package/src/scripts/_lib/release_highlights.ts +75 -10
- package/src/scripts/_lib/repeated_failure.ts +19 -2
- package/src/scripts/_lib/runtime_journal.ts +11 -4
- package/src/scripts/check_agent_artifact_location.ts +337 -0
- package/src/scripts/check_artefact_count_messaging.ts +74 -8
- package/src/scripts/check_enforcement_coverage.ts +40 -1
- package/src/scripts/check_release_highlights.ts +17 -1
- package/src/scripts/check_web_launch_readiness.ts +310 -20
- package/src/scripts/evolution_lab.ts +53 -1
- package/src/scripts/generate_capability_matrix.ts +16 -9
- package/src/scripts/git_authorization_hook.ts +476 -6
- package/src/scripts/grade_target_readiness.ts +221 -2
- package/src/scripts/hook_effect_doctor.ts +47 -4
- package/src/scripts/hooks/block_unauthorized_git.ts +238 -9
- package/src/scripts/hooks/merge_impact.ts +207 -0
- package/src/scripts/hooks/run_continuation_hook.ts +78 -88
- package/src/scripts/hooks/session_eol_hook.ts +12 -1
- package/src/scripts/install-hooks.sh +18 -0
- package/src/scripts/lint_code_comments.ts +106 -1
- package/src/scripts/release.ts +5 -17
- package/src/scripts/release_drill.ts +76 -1
- package/src/scripts/release_env.ts +31 -0
- package/src/scripts/release_publication.ts +125 -1
- package/src/scripts/roadmap_context.ts +15 -0
- package/src/scripts/skill_scout.ts +633 -0
- package/src/scripts/sync_pr_branch.ts +521 -42
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* |---|---|---|---|
|
|
14
14
|
* | phase | one audit-log line — a `/work` PHASE | here, consumed by `orchestration_record` | 4 |
|
|
15
15
|
* | step | one work-engine STEP | `work_engine/delivery_state.ts` (template tree) | 3 |
|
|
16
|
-
* | run | one command or gate RUN | here, consumed by `outcome_envelope` | 6
|
|
16
|
+
* | run | one command or gate RUN | here, consumed by `outcome_envelope` | 7 | code-comment-allow report-comment -- one row of a pre-existing three-row table documenting the three vocabularies; the digit changed from 6 to 7 with the vocabulary, and the table is the registry's whole point
|
|
17
17
|
*
|
|
18
18
|
* All three are declared in code AND emitted today. `skipped` and `error` are
|
|
19
19
|
* not aspirational: `envelopeOutcome` in `orchestration_record.ts` returns
|
|
@@ -33,16 +33,42 @@
|
|
|
33
33
|
* `partial`. So: separate vocabularies, one registry, mappings only where
|
|
34
34
|
* execution actually crosses.
|
|
35
35
|
*
|
|
36
|
-
* **Mappings are not written for symmetry.**
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* **Mappings are not written for symmetry.** Two crossings exist, and both are
|
|
37
|
+
* places where execution actually translates: `DispatchOutcome` -> phase in
|
|
38
|
+
* `orchestration_record.ts`, and `LadderAction` -> run in
|
|
39
|
+
* `continuation_ladder.ts` (added 2026-09-03 with the seventh run state, so the
|
|
40
|
+
* state has a producer rather than being a word nothing emits). A total
|
|
41
|
+
* phase<->step<->run mapping would be nine relations of which two are real, and
|
|
42
|
+
* the seven invented ones would read as contract.
|
|
40
43
|
*
|
|
41
44
|
* `revisit-if`: a producer needs a value its vocabulary cannot express, or a
|
|
42
45
|
* second real crossing appears, or producer analysis shows two of the three
|
|
43
46
|
* subjects share identical terminal semantics with a lossless mapping — at
|
|
44
47
|
* which point unifying those two becomes the cheaper answer.
|
|
45
48
|
*
|
|
49
|
+
* **The run vocabulary grew to seven on 2026-09-03** — exactly the first branch
|
|
50
|
+
* of the reopening condition above: the continuation ladder needed a word for a
|
|
51
|
+
* run whose plan premise moved, and had none. AI council (anthropic/claude-sonnet-4-5 +
|
|
52
|
+
* openai/codex-default, three rounds, blind chairman) resolved it unanimously
|
|
53
|
+
* as option (a), extend rather than overload `blocked` with a reason field. The
|
|
54
|
+
* seats attached the same prerequisites, discharged here: a versioned value
|
|
55
|
+
* domain ({@link RUN_TERMINAL_VOCABULARY_VERSION}), a tolerant read
|
|
56
|
+
* ({@link readRunTerminalState}), a declared downgrade
|
|
57
|
+
* ({@link RUN_TERMINAL_STATE_DOWNGRADE}) and the rollback trigger below.
|
|
58
|
+
*
|
|
59
|
+
* **Rollback trigger, stated rather than instrumented.** Withdraw
|
|
60
|
+
* `premise-invalidated` — remove it from {@link RUN_TERMINAL_STATES} and let the
|
|
61
|
+
* ladder rung report its declared downgrade `blocked` instead — on EITHER of:
|
|
62
|
+
* (1) any consumer is observed failing (a throw, a refused write, a dropped
|
|
63
|
+
* record) on encountering the value, or (2) `readRunTerminalState` returns
|
|
64
|
+
* `null` for a persisted value in normal operation, which would mean a writer
|
|
65
|
+
* emitted something this registry does not know. Both are single-occurrence
|
|
66
|
+
* triggers, not rates: the downgrade mapping already exists, so withdrawal
|
|
67
|
+
* costs one commit and no migration, and there is no reason to tolerate a
|
|
68
|
+
* budget of failures before paying that. No telemetry is built for this and
|
|
69
|
+
* none is claimed — condition (1) surfaces as a failing run and (2) as a null
|
|
70
|
+
* where a value was written.
|
|
71
|
+
*
|
|
46
72
|
* Type-only + literal arrays. No `node:` import, so any surface may read it.
|
|
47
73
|
*/
|
|
48
74
|
|
|
@@ -82,9 +108,99 @@ export const RUN_TERMINAL_STATES = [
|
|
|
82
108
|
'approval-required',
|
|
83
109
|
'exhausted',
|
|
84
110
|
'stagnated',
|
|
111
|
+
/**
|
|
112
|
+
* The plan premise this run was built on no longer holds — the situational
|
|
113
|
+
* -awareness fingerprint the run engaged under (`origin/main` plus every open
|
|
114
|
+
* PR head) was re-observed and differs.
|
|
115
|
+
*
|
|
116
|
+
* Added by `road-to-wired-instruments` Phase 2 under the AI-council decision
|
|
117
|
+
* of 2026-09-03 (option (a), unanimous). It is NOT `blocked` with a reason
|
|
118
|
+
* field: premise invalidation is operationally distinct from a missing
|
|
119
|
+
* precondition and from an exhausted budget, and folding it into either loses
|
|
120
|
+
* both aggregation by state and type-enforced exhaustive handling. The
|
|
121
|
+
* remedy differs too — re-probe and re-plan, where `blocked` says obtain the
|
|
122
|
+
* missing thing and `exhausted` says the budget was too small.
|
|
123
|
+
*/
|
|
124
|
+
'premise-invalidated',
|
|
85
125
|
] as const;
|
|
86
126
|
export type RunTerminalState = (typeof RUN_TERMINAL_STATES)[number];
|
|
87
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Version of the RUN vocabulary's VALUE DOMAIN — bumped when a member is added
|
|
130
|
+
* or removed, never for prose.
|
|
131
|
+
*
|
|
132
|
+
* Distinct from any table version: `runtime_journal.JOURNAL_SCHEMA_VERSION`
|
|
133
|
+
* covers tables and columns and is unaffected by a widening of what one column
|
|
134
|
+
* may contain, so a value-domain change needs its own number or it travels
|
|
135
|
+
* unversioned. A persisted shape carrying a `RunTerminalState` stamps THIS
|
|
136
|
+
* number beside the value, so a reader can tell a value it does not know from a
|
|
137
|
+
* value that is corrupt.
|
|
138
|
+
*
|
|
139
|
+
* v1 — the six original states.
|
|
140
|
+
* v2 — adds `premise-invalidated` (2026-09-03).
|
|
141
|
+
*/
|
|
142
|
+
export const RUN_TERMINAL_VOCABULARY_VERSION = 2;
|
|
143
|
+
|
|
144
|
+
/** The vocabulary version each member first appeared in. */
|
|
145
|
+
export const RUN_TERMINAL_STATE_SINCE: Readonly<Record<RunTerminalState, number>> = {
|
|
146
|
+
success: 1,
|
|
147
|
+
'clean-no-op': 1,
|
|
148
|
+
blocked: 1,
|
|
149
|
+
'approval-required': 1,
|
|
150
|
+
exhausted: 1,
|
|
151
|
+
stagnated: 1,
|
|
152
|
+
'premise-invalidated': 2,
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* What a consumer pinned to an older vocabulary version reports instead.
|
|
157
|
+
*
|
|
158
|
+
* `premise-invalidated` -> `blocked`: of the six v1 states it is the only one
|
|
159
|
+
* that is honest for a reader that cannot represent it — the run stopped on an
|
|
160
|
+
* external condition it cannot resolve by iterating. Mapping it to `exhausted`
|
|
161
|
+
* would invite the wrong remedy (raise the budget), which is the exact
|
|
162
|
+
* confusion the contract's own `exhausted`/`stagnated` split exists to prevent.
|
|
163
|
+
*
|
|
164
|
+
* Every member introduced after v1 MUST have a row here; `outcome_vocabularies`
|
|
165
|
+
* contract tests assert that, so a future eighth value cannot ship undowngradable.
|
|
166
|
+
*/
|
|
167
|
+
export const RUN_TERMINAL_STATE_DOWNGRADE: Readonly<Partial<Record<RunTerminalState, RunTerminalState>>> = {
|
|
168
|
+
'premise-invalidated': 'blocked',
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The state a consumer pinned at `toVersion` should report for `state`.
|
|
173
|
+
*
|
|
174
|
+
* Returns the state unchanged when it already existed at that version, the
|
|
175
|
+
* declared downgrade when it did not, and `null` for a string this vocabulary
|
|
176
|
+
* does not know at all — never a throw, because this is the function a reader
|
|
177
|
+
* of PERSISTED data calls, and persisted data outlives the code that wrote it.
|
|
178
|
+
*/
|
|
179
|
+
export function downgradeRunTerminalState(
|
|
180
|
+
state: string,
|
|
181
|
+
toVersion: number,
|
|
182
|
+
): RunTerminalState | null {
|
|
183
|
+
if (!isRunTerminalState(state)) return null;
|
|
184
|
+
if ((RUN_TERMINAL_STATE_SINCE[state] ?? 1) <= toVersion) return state;
|
|
185
|
+
return RUN_TERMINAL_STATE_DOWNGRADE[state] ?? null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Tolerant read of a persisted terminal state: a member returns itself,
|
|
190
|
+
* anything else returns `null`. Never throws.
|
|
191
|
+
*
|
|
192
|
+
* `null` rather than a fabricated fallback is the load-bearing choice. This
|
|
193
|
+
* vocabulary's own consumers already treat a null terminal state as "not
|
|
194
|
+
* recorded" (`repeated_failure` counts it under `unknown`), and that is exactly
|
|
195
|
+
* what an unrecognised value IS to a reader that cannot name it. Substituting
|
|
196
|
+
* `blocked` here would manufacture a measurement out of an absence — the
|
|
197
|
+
* downgrade above is for a value the reader KNOWS is newer, which is a
|
|
198
|
+
* different fact from a value it cannot place.
|
|
199
|
+
*/
|
|
200
|
+
export function readRunTerminalState(v: unknown): RunTerminalState | null {
|
|
201
|
+
return isRunTerminalState(v) ? v : null;
|
|
202
|
+
}
|
|
203
|
+
|
|
88
204
|
/**
|
|
89
205
|
* The crossings that exist. One row, because one crossing is real.
|
|
90
206
|
*
|
|
@@ -112,6 +228,20 @@ export const CROSS_DOMAIN_MAPPINGS = [
|
|
|
112
228
|
*/
|
|
113
229
|
semantics_version: 2,
|
|
114
230
|
},
|
|
231
|
+
{
|
|
232
|
+
source: 'LadderAction',
|
|
233
|
+
target: 'run',
|
|
234
|
+
at: 'src/scripts/_lib/continuation_ladder.ts',
|
|
235
|
+
fn: 'terminalStateFor',
|
|
236
|
+
/**
|
|
237
|
+
* Version 1, and the mapping is TOTAL over `LadderAction` by
|
|
238
|
+
* construction: `TERMINAL_STATE_BY_ACTION` is a `Record<LadderAction,
|
|
239
|
+
* RunTerminalState | null>`, so adding a rung without deciding its
|
|
240
|
+
* terminal state does not compile. `engage` maps to null because it is
|
|
241
|
+
* the one action that is not terminal.
|
|
242
|
+
*/
|
|
243
|
+
semantics_version: 1,
|
|
244
|
+
},
|
|
115
245
|
] as const;
|
|
116
246
|
|
|
117
247
|
/** Is this string a phase outcome? The guard the audit-log consumer was missing. */
|
|
@@ -279,9 +279,57 @@ export function derive_categories(commits: readonly SpanCommit[]): Record<string
|
|
|
279
279
|
*
|
|
280
280
|
* A label with no evidence is omitted, so the caller's `_none_` default still
|
|
281
281
|
* applies where `_none_` is the true answer. One line per label keeps the
|
|
282
|
-
* rendered head inside its cap;
|
|
283
|
-
*
|
|
282
|
+
* rendered head inside its cap; citations are capped at `DERIVED_SHA_CAP` with
|
|
283
|
+
* an explicit remainder count rather than a silent truncation.
|
|
284
|
+
*
|
|
285
|
+
* **PUBLISHABLE BY DEFAULT since 2026-09-03, and this is the third position
|
|
286
|
+
* this line has held.** It shipped `DERIVED_MARKER` plus a CATEGORY
|
|
287
|
+
* DESCRIPTION — "rule/schema diffs, breaking commits or removed public surface
|
|
288
|
+
* in 5a3b7c5". The 2026-09-01 flip then made that marker a hard refusal, and
|
|
289
|
+
* the combination had a consequence nobody priced: every release of this
|
|
290
|
+
* package touches `src/rules/` or `src/scripts/schemas/`, so **Behaviour
|
|
291
|
+
* changes** is always substantiated, so the generator always wrote a line the
|
|
292
|
+
* gate always refused. Every release halted, by construction. That was
|
|
293
|
+
* recorded as "the cadence the flip chose"; the maintainer's instruction of
|
|
294
|
+
* 2026-09-03 — *"fix the bug so this stops happening"* — is the authority for
|
|
295
|
+
* removing it.
|
|
296
|
+
*
|
|
297
|
+
* The fix is not a weaker gate, and it is not the marker moving somewhere
|
|
298
|
+
* cheaper (that was 2026-09-03's first attempt, `guard_release_curation`,
|
|
299
|
+
* which relocated the halt without removing it). It is the writer no longer
|
|
300
|
+
* emitting text that cannot ship. Two changes, and the first is what makes the
|
|
301
|
+
* second honest:
|
|
302
|
+
*
|
|
303
|
+
* 1. The line now states the COMMIT SUBJECTS behind each category, not the
|
|
304
|
+
* name of the category's own detection rule. "hooks:effect reports whether
|
|
305
|
+
* a bound concern fires (b0a03a7)" is a claim a reader can use; "rule/schema
|
|
306
|
+
* diffs in b0a03a7" is a restatement of why the classifier fired.
|
|
307
|
+
* 2. `DERIVED_MARKER` is gone from the emission, so nothing unpublishable is
|
|
308
|
+
* ever written and no release halts on the writer's own output.
|
|
309
|
+
*
|
|
310
|
+
* What is deliberately NOT relaxed: `highlight_contradictions` still refuses a
|
|
311
|
+
* human editing a substantiated line down to `_none_`, which is the failure the
|
|
312
|
+
* false 9.13.0 and 9.14.0 heads actually were; and the marker constant and its
|
|
313
|
+
* four guard sites stay, now covering only a marker somebody writes BY HAND.
|
|
314
|
+
* The honest cost of this change, stated rather than left to be discovered: the
|
|
315
|
+
* head is a categorised view of the span rather than curated prose, and it
|
|
316
|
+
* ships without a human having read it. An operator who wants better prose
|
|
317
|
+
* still edits it — they are no longer STOPPED until they do.
|
|
284
318
|
*/
|
|
319
|
+
/**
|
|
320
|
+
* A commit subject reduced to the claim inside it.
|
|
321
|
+
*
|
|
322
|
+
* Drops the conventional-commit `type(scope):` prefix, because "feat(hooks):"
|
|
323
|
+
* is metadata about the commit and the changelog's own commit lists already
|
|
324
|
+
* carry it — what a highlight line needs is the sentence after the colon. Keeps
|
|
325
|
+
* the subject otherwise verbatim: rewording it here would make the head a
|
|
326
|
+
* paraphrase of the span rather than a citation of it, and the whole point of
|
|
327
|
+
* this module is that the two cannot disagree.
|
|
328
|
+
*/
|
|
329
|
+
function _claim_text(subject: string): string {
|
|
330
|
+
return subject.replace(/^[a-z]+(?:\([^)]*\))?!?:\s*/u, '').trim();
|
|
331
|
+
}
|
|
332
|
+
|
|
285
333
|
export function render_derived_head_values(
|
|
286
334
|
hits: Readonly<Record<string, readonly CategoryHit[]>>,
|
|
287
335
|
): Record<string, string> {
|
|
@@ -292,11 +340,19 @@ export function render_derived_head_values(
|
|
|
292
340
|
if (!reason || ev.length === 0) {
|
|
293
341
|
continue;
|
|
294
342
|
}
|
|
295
|
-
const
|
|
296
|
-
const
|
|
297
|
-
const
|
|
298
|
-
const
|
|
299
|
-
|
|
343
|
+
const shown = ev.slice(0, DERIVED_SHA_CAP);
|
|
344
|
+
const remainder = ev.length - shown.length;
|
|
345
|
+
const more = remainder > 0 ? `; +${String(remainder)} more` : '';
|
|
346
|
+
const cited = shown
|
|
347
|
+
.map((h) => `${_claim_text(h.text)} (${h.sha.slice(0, 7)})`)
|
|
348
|
+
.join('; ');
|
|
349
|
+
// `reason` still names the classifier that fired, kept as the fallback
|
|
350
|
+
// for a hit whose subject renders empty after trimming — a subject that
|
|
351
|
+
// is nothing but a conventional-commit prefix. Without it the line
|
|
352
|
+
// would read "( b0a03a7)".
|
|
353
|
+
out[label] = cited ? `${cited}${more}.` : `${reason} in ${shown
|
|
354
|
+
.map((h) => h.sha.slice(0, 7))
|
|
355
|
+
.join(', ')}${more}.`;
|
|
300
356
|
}
|
|
301
357
|
return out;
|
|
302
358
|
}
|
|
@@ -331,14 +387,23 @@ export function render_derived_head_values(
|
|
|
331
387
|
* `CURATED_HEAD_INSTRUCTION` is a named sentinel imported from the module that
|
|
332
388
|
* defines it — never a shape match over comment prose, which would reject
|
|
333
389
|
* unrelated legitimate comments and miss this one after a reword.
|
|
390
|
+
*
|
|
391
|
+
* `where` names the branch the remedy edit belongs on, because the same
|
|
392
|
+
* blockers are now read at a FOURTH site that is not on `main`:
|
|
393
|
+
* `guard_release_branch_push` refuses the release branch's first push, where
|
|
394
|
+
* the file to edit is on `release/X.Y.Z`. One definition of the blockers with a
|
|
395
|
+
* parameterised remedy beats a second copy of the message that drifts.
|
|
334
396
|
*/
|
|
335
|
-
export function publication_blockers(
|
|
397
|
+
export function publication_blockers(
|
|
398
|
+
sectionBody: string,
|
|
399
|
+
version: string,
|
|
400
|
+
where = '`main`',
|
|
401
|
+
): string[] {
|
|
336
402
|
const out: string[] = [];
|
|
337
403
|
if (sectionBody.includes(DERIVED_MARKER)) {
|
|
338
404
|
out.push(
|
|
339
405
|
`the ${version} section still carries \`${DERIVED_MARKER}\` — the generator's ` +
|
|
340
|
-
|
|
341
|
-
`${'`main`'} and re-run.`,
|
|
406
|
+
`draft head, not a curated claim. Rewrite those line(s) on ${where} and re-run.`,
|
|
342
407
|
);
|
|
343
408
|
}
|
|
344
409
|
if (sectionBody.includes(CURATED_HEAD_INSTRUCTION)) {
|
|
@@ -39,9 +39,26 @@ export const SUCCESS_TERMINALS: ReadonlySet<RunTerminalState> = new Set([
|
|
|
39
39
|
'clean-no-op',
|
|
40
40
|
]);
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Neither a failure nor a success for this metric, and each excluded for its own
|
|
44
|
+
* reason rather than by omission.
|
|
45
|
+
*
|
|
46
|
+
* `approval-required` is the safety floor working; counting a floor doing its
|
|
47
|
+
* job as a failure would make the metric fall every time governance fires.
|
|
48
|
+
*
|
|
49
|
+
* `premise-invalidated` is the same shape one layer out: the run stopped because
|
|
50
|
+
* the world it planned against moved, which is the drift detector working, not
|
|
51
|
+
* the work failing. It is also the state whose base rate is set by OTHER people's
|
|
52
|
+
* pushes rather than by this run's quality — `origin/main` moves constantly — so
|
|
53
|
+
* counting it as a failure would make the repeated-failure rate track repository
|
|
54
|
+
* traffic. That is the concrete form of this roadmap's Risk 1, arriving through
|
|
55
|
+
* the metric instead of through the ladder.
|
|
56
|
+
*/
|
|
57
|
+
const EXCLUDED: ReadonlySet<RunTerminalState> = new Set(['approval-required', 'premise-invalidated']);
|
|
58
|
+
|
|
42
59
|
// Every terminal state is classified or deliberately excluded -- silence about
|
|
43
60
|
// one is how a metric quietly changes meaning when the vocabulary grows.
|
|
44
|
-
const CLASSIFIED = new Set<string>([...FAILURE_TERMINALS, ...SUCCESS_TERMINALS,
|
|
61
|
+
const CLASSIFIED = new Set<string>([...FAILURE_TERMINALS, ...SUCCESS_TERMINALS, ...EXCLUDED]);
|
|
45
62
|
const _unclassified = RUN_TERMINAL_STATES.filter((t) => !CLASSIFIED.has(t));
|
|
46
63
|
if (_unclassified.length > 0) {
|
|
47
64
|
throw new Error(
|
|
@@ -86,7 +103,7 @@ export function repeatedFailureRate(
|
|
|
86
103
|
// `ep.terminal_state` is already the AMENDED verdict: reconstructEpisode
|
|
87
104
|
// drops superseded rows and takes the LAST effective terminal state.
|
|
88
105
|
const t = ep.terminal_state;
|
|
89
|
-
if (t === null || !CLASSIFIED.has(t) || t
|
|
106
|
+
if (t === null || !CLASSIFIED.has(t) || EXCLUDED.has(t)) {
|
|
90
107
|
unknown += 1;
|
|
91
108
|
continue;
|
|
92
109
|
}
|
|
@@ -173,7 +173,7 @@ import type { DatabaseSync } from 'node:sqlite';
|
|
|
173
173
|
|
|
174
174
|
import { current_branch, git_common_dir, git_dir } from './git_common_dir.js';
|
|
175
175
|
import type { TerminalState } from './outcome_envelope.js';
|
|
176
|
-
import { RUN_TERMINAL_STATES } from './outcome_vocabularies.js';
|
|
176
|
+
import { readRunTerminalState, RUN_TERMINAL_STATES } from './outcome_vocabularies.js';
|
|
177
177
|
import {
|
|
178
178
|
isSqliteAvailableSync,
|
|
179
179
|
loadSqliteSync,
|
|
@@ -303,7 +303,7 @@ export const CONSUMPTION_STATES = ['consumed', 'partially-consumed', 'rejected-w
|
|
|
303
303
|
export type Consumption = (typeof CONSUMPTION_STATES)[number];
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
|
-
* The
|
|
306
|
+
* The run terminal states as a runtime array.
|
|
307
307
|
*
|
|
308
308
|
* Re-export of `RUN_TERMINAL_STATES` from `outcome_vocabularies.ts`. It used to
|
|
309
309
|
* be a second literal list here, guarded by two type-level assertions that
|
|
@@ -316,6 +316,12 @@ export type Consumption = (typeof CONSUMPTION_STATES)[number];
|
|
|
316
316
|
*
|
|
317
317
|
* The name is kept because it is exported and pinned by
|
|
318
318
|
* `tests/scripts/runtime_journal.test.ts:205,207`.
|
|
319
|
+
*
|
|
320
|
+
* Growing this list does NOT move {@link JOURNAL_SCHEMA_VERSION}: that covers
|
|
321
|
+
* tables and columns and a mismatch discards the store, while a widened value
|
|
322
|
+
* domain changes neither. The domain carries its own version in the registry
|
|
323
|
+
* (`RUN_TERMINAL_VOCABULARY_VERSION`), and the read path tolerates a value it
|
|
324
|
+
* does not know rather than trusting the column — see `toEvent`.
|
|
319
325
|
*/
|
|
320
326
|
export const TERMINAL_STATES = RUN_TERMINAL_STATES;
|
|
321
327
|
|
|
@@ -1019,7 +1025,7 @@ export function recordEvent(h: JournalHandle, input: RecordEventInput): JournalE
|
|
|
1019
1025
|
!(TERMINAL_STATES as readonly string[]).includes(input.terminal_state)
|
|
1020
1026
|
) {
|
|
1021
1027
|
throw new JournalContractError(
|
|
1022
|
-
`terminal_state ${JSON.stringify(input.terminal_state)} is not
|
|
1028
|
+
`terminal_state ${JSON.stringify(input.terminal_state)} is not in the run vocabulary.`,
|
|
1023
1029
|
);
|
|
1024
1030
|
}
|
|
1025
1031
|
if (
|
|
@@ -1105,7 +1111,8 @@ function toEvent(r: Record<string, unknown>): JournalEvent {
|
|
|
1105
1111
|
worktree_id: String(r['worktree_id']),
|
|
1106
1112
|
at: String(r['at']),
|
|
1107
1113
|
capability: String(r['capability']),
|
|
1108
|
-
|
|
1114
|
+
// Tolerant, not a cast: a value this build cannot place reads as null.
|
|
1115
|
+
terminal_state: readRunTerminalState(r['terminal_state']),
|
|
1109
1116
|
return_ref: (r['return_ref'] as string | null) ?? null,
|
|
1110
1117
|
verification_ref: (r['verification_ref'] as string | null) ?? null,
|
|
1111
1118
|
consumption: (r['consumption'] as Consumption | null) ?? null,
|