@event4u/agent-config 9.31.0 → 9.33.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +146 -0
- package/dist/agent-src/commands/review/routing.md +3 -4
- package/dist/agent-src/contexts/communication/rules-auto/reply-close-mechanics.md +65 -0
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +32 -1
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +3 -0
- package/dist/agent-src/rules/delegation-policy.md +17 -1
- package/dist/agent-src/scripts/roadmap_gates.ts +107 -3
- package/dist/agent-src/skills/docker/SKILL.md +11 -0
- package/dist/agent-src/skills/laravel/SKILL.md +11 -0
- package/dist/agent-src/skills/review-routing/SKILL.md +23 -6
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/ownership-map.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/delivery_state.ts +6 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +332 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +106 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +50 -15
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +49 -15
- package/dist/agent-src/templates/scripts/work_engine/state.ts +74 -0
- package/dist/agent-src/templates/scripts/work_engine/state_io.ts +6 -0
- package/dist/cli/registry.js +4 -4
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-2WELHO3P.js +582 -0
- package/dist/cli-delegate/chunk-HTIU3W56.js +66 -0
- package/dist/cli-delegate/{chunk-ITIQSGOQ.js → chunk-MA4GFWDY.js} +7 -4
- package/dist/cli-delegate/{chunk-2WCLVDWC.js → chunk-PMQ6Z3T3.js} +1642 -165
- package/dist/cli-delegate/chunk-PZWRPRWP.js +38 -0
- package/dist/cli-delegate/{chunk-PFBV6XRT.js → chunk-Q26HT4QT.js} +1 -63
- package/dist/cli-delegate/{chunk-LVRYBKTI.js → chunk-YPAWXXXR.js} +158 -0
- package/dist/cli-delegate/cmd_conformance.js +308 -227
- package/dist/cli-delegate/cmd_doctor.js +4 -2
- package/dist/cli-delegate/cmd_export.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +160 -58
- package/dist/cli-delegate/cmd_prune.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +30 -5
- package/dist/cli-delegate/cmd_settings_get.js +4 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
- package/dist/cli-delegate/cmd_settings_set.js +5 -3
- package/dist/cli-delegate/cmd_sync.js +1 -1
- package/dist/cli-delegate/cmd_uninstall.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.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 +1136 -690
- package/dist/install/install.mjs +7 -4
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +3688 -1159
- package/package.json +1 -1
- package/src/agent-src/scripts/roadmap_gates.ts +107 -3
- package/src/agent-src/templates/scripts/ownership-map.example.yml +1 -1
- package/src/agent-src/templates/scripts/work_engine/delivery_state.ts +6 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +332 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/refine.ts +106 -1
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +50 -15
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +49 -15
- package/src/agent-src/templates/scripts/work_engine/state.ts +74 -0
- package/src/agent-src/templates/scripts/work_engine/state_io.ts +6 -0
- package/src/config/cost-parity-budget.json +167 -0
- package/src/config/gate-coverage.yml +44 -5
- package/src/config/quorum-attendance-budget.json +39 -0
- package/src/scripts/_cli/cmd_doctor.ts +69 -1
- package/src/scripts/_cli/cmd_session_recycle.ts +44 -3
- package/src/scripts/_cli/handoff_generate.ts +36 -1
- package/src/scripts/_cli/handoff_sessions.ts +140 -9
- package/src/scripts/_dispatch.bash +5 -3
- package/src/scripts/_lib/carrier_divergence.ts +98 -27
- package/src/scripts/_lib/envelope_grounding.ts +206 -0
- package/src/scripts/_lib/host_capability.ts +79 -0
- package/src/scripts/_lib/orchestration_record.ts +45 -0
- package/src/scripts/_lib/session_eol.ts +85 -0
- package/src/scripts/_lib/subagent_capsule.ts +256 -1
- package/src/scripts/ai_council/clients.ts +31 -0
- package/src/scripts/ai_council/events_log.ts +128 -6
- package/src/scripts/ai_council/orchestrator.ts +12 -3
- package/src/scripts/ai_council/pricing.ts +48 -1
- package/src/scripts/ai_council/quorum.ts +20 -0
- package/src/scripts/ai_council/session.ts +11 -0
- package/src/scripts/ask_transport.ts +20 -1
- package/src/scripts/cache_realization_report.ts +38 -0
- package/src/scripts/check_release_highlights.ts +7 -1
- package/src/scripts/check_secret_leak.test.ts +43 -0
- package/src/scripts/check_secret_leak.ts +12 -0
- package/src/scripts/conformance_scan.ts +105 -0
- package/src/scripts/cost/track.mjs +39 -0
- package/src/scripts/cost_summary.ts +104 -1
- package/src/scripts/council_cli.ts +131 -13
- package/src/scripts/dispatch_economy_report.ts +5 -2
- package/src/scripts/handoff_context_hook.ts +66 -18
- package/src/scripts/hooks/session_eol_hook.ts +12 -12
- package/src/scripts/install.ts +20 -6
- package/src/scripts/lint_budget_ownership.ts +81 -0
- package/src/scripts/lint_skill_frontmatter_safety.ts +55 -1
- package/src/scripts/lint_workflow_paths.ts +384 -0
- package/src/scripts/preamble_byte_census.ts +9 -1
- package/src/scripts/report_carrier_divergence.ts +104 -29
- package/src/scripts/report_conformance_funnel.ts +51 -4
- package/src/scripts/routing_doctor.ts +25 -2
- package/src/scripts/schemas/dod.schema.json +45 -0
- package/src/scripts/schemas/subagent.schema.json +3 -2
- package/dist/cli-delegate/chunk-RVGBH4XK.js +0 -142
|
@@ -17,10 +17,12 @@
|
|
|
17
17
|
* - Must be a dict.
|
|
18
18
|
* - Must carry a `verdict` key — one of `success`, `failed`,
|
|
19
19
|
* or `mixed` (targeted vs full-suite split). Anything else blocks.
|
|
20
|
-
* - `failed` or `mixed` verdicts
|
|
21
|
-
* the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* - `failed` or `mixed` verdicts enter the bounded self-fix loop
|
|
21
|
+
* (`./_self_fix.ts`): the first attempts are delegated by directive, and an
|
|
22
|
+
* exhausted or non-progressing loop exits `PARTIAL` with the verdict still
|
|
23
|
+
* on the surface so the user decides whether to continue or stop. This
|
|
24
|
+
* follows the `verify-before-complete` rule: a bad test outcome must never
|
|
25
|
+
* be silently skipped, and the loop cannot report one as success.
|
|
24
26
|
* - Optional keys (`targeted`, `full`, `duration_ms`,
|
|
25
27
|
* `followups`) feed the delivery report.
|
|
26
28
|
*/
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
import type { DeliveryState} from '../../delivery_state.js';
|
|
29
31
|
import { type Any, Outcome, StepResult, agent_directive } from '../../delivery_state.js';
|
|
30
32
|
import { resolve_policy } from '../../persona_policy.js';
|
|
33
|
+
import { decide, partial_exit, record_attempt, retry_halt, unmet_dod, verdict_signature } from './_self_fix.js';
|
|
31
34
|
|
|
32
35
|
const _ALLOWED_VERDICTS = ['success', 'failed', 'mixed'] as const;
|
|
33
36
|
|
|
@@ -53,7 +56,18 @@ export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
|
53
56
|
{
|
|
54
57
|
code: 'bad_test_verdict',
|
|
55
58
|
trigger: '`state.tests[\'verdict\']` is `failed` or `mixed`',
|
|
56
|
-
resolution:
|
|
59
|
+
resolution:
|
|
60
|
+
'bounded self-fix loop: agent directive `fix-failing-checks` ' +
|
|
61
|
+
'while attempts remain and the verdict signature keeps changing',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
code: 'self_fix_exhausted',
|
|
65
|
+
trigger:
|
|
66
|
+
'the self-fix budget for `test` is spent, or two consecutive ' +
|
|
67
|
+
'attempts produced an identical verdict signature',
|
|
68
|
+
resolution:
|
|
69
|
+
'PARTIAL honest exit — the red verdict stays on the surface, ' +
|
|
70
|
+
'the user decides whether to take over, override, or abort',
|
|
57
71
|
},
|
|
58
72
|
];
|
|
59
73
|
|
|
@@ -156,18 +170,39 @@ function _blocked_on_shape(state: DeliveryState, issues: string[]): StepResult {
|
|
|
156
170
|
});
|
|
157
171
|
}
|
|
158
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Route a red test verdict through the bounded self-fix loop.
|
|
175
|
+
*
|
|
176
|
+
* Before this loop existed the function below returned a directive-free
|
|
177
|
+
* BLOCKED — a user question block — so every red cost a user round-trip even
|
|
178
|
+
* when the failure was one the agent could fix unaided. Now the first attempts
|
|
179
|
+
* are delegated and only an exhausted or non-progressing loop reaches the
|
|
180
|
+
* user, as a PARTIAL that still carries the red verdict.
|
|
181
|
+
*/
|
|
159
182
|
function _blocked_on_bad_verdict(state: DeliveryState, verdict: Any): StepResult {
|
|
160
183
|
const ticketId = _ticketId(state);
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
184
|
+
const signature = verdict_signature('test', state.tests);
|
|
185
|
+
const decision = decide(state, 'test', signature);
|
|
186
|
+
|
|
187
|
+
if (decision.kind === 'retry') {
|
|
188
|
+
record_attempt(state, 'test', signature);
|
|
189
|
+
return retry_halt({
|
|
190
|
+
lane: 'test',
|
|
191
|
+
ticket_id: ticketId,
|
|
192
|
+
verdict,
|
|
193
|
+
decision,
|
|
194
|
+
fix_hint: 'read the failing assertions, fix the cause, re-run the same filter.',
|
|
195
|
+
rerun_directive: 'run-tests',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return partial_exit({
|
|
200
|
+
lane: 'test',
|
|
201
|
+
ticket_id: ticketId,
|
|
202
|
+
verdict,
|
|
203
|
+
decision,
|
|
204
|
+
unmet: unmet_dod(state),
|
|
205
|
+
rerun_directive: 'run-tests',
|
|
171
206
|
});
|
|
172
207
|
}
|
|
173
208
|
|
|
@@ -18,9 +18,12 @@
|
|
|
18
18
|
* `partial`. Matches the `Outcome` vocabulary used everywhere
|
|
19
19
|
* else in the flow.
|
|
20
20
|
* - A `success` verdict advances the flow to `report`.
|
|
21
|
-
* - A `blocked` or `partial` verdict
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* - A `blocked` or `partial` verdict enters the bounded self-fix loop
|
|
22
|
+
* (`./_self_fix.ts`): while attempts remain and the verdict signature
|
|
23
|
+
* keeps changing, the fix is delegated by directive. An exhausted or
|
|
24
|
+
* non-progressing loop exits `PARTIAL` with numbered options so the user
|
|
25
|
+
* decides whether to take over, override (rarely appropriate), or abort.
|
|
26
|
+
* The loop never turns a non-success verdict into `SUCCESS`.
|
|
24
27
|
* - Optional keys (`confidence`, `findings`, `followups`) feed
|
|
25
28
|
* the delivery report.
|
|
26
29
|
*/
|
|
@@ -28,6 +31,7 @@
|
|
|
28
31
|
import type { DeliveryState} from '../../delivery_state.js';
|
|
29
32
|
import { type Any, Outcome, StepResult, agent_directive } from '../../delivery_state.js';
|
|
30
33
|
import { resolve_policy } from '../../persona_policy.js';
|
|
34
|
+
import { decide, partial_exit, record_attempt, retry_halt, unmet_dod, verdict_signature } from './_self_fix.js';
|
|
31
35
|
|
|
32
36
|
const _ALLOWED_VERDICTS = ['success', 'blocked', 'partial'] as const;
|
|
33
37
|
|
|
@@ -52,7 +56,17 @@ export const AMBIGUITIES: ReadonlyArray<Record<string, string>> = [
|
|
|
52
56
|
code: 'bad_verify_verdict',
|
|
53
57
|
trigger: '`state.verify[\'verdict\']` is `blocked` or `partial`',
|
|
54
58
|
resolution:
|
|
55
|
-
'
|
|
59
|
+
'bounded self-fix loop: agent directive `fix-failing-checks` ' +
|
|
60
|
+
'while attempts remain — never bypass ' + '(see `verify-before-complete`)',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
code: 'self_fix_exhausted',
|
|
64
|
+
trigger:
|
|
65
|
+
'the self-fix budget for `verify` is spent, or two consecutive ' +
|
|
66
|
+
'attempts produced an identical verdict signature',
|
|
67
|
+
resolution:
|
|
68
|
+
'PARTIAL honest exit — the non-success verdict stays on the ' +
|
|
69
|
+
'surface; the user takes over, overrides, or aborts',
|
|
56
70
|
},
|
|
57
71
|
];
|
|
58
72
|
|
|
@@ -145,19 +159,39 @@ function _blocked_on_shape(state: DeliveryState, issues: string[]): StepResult {
|
|
|
145
159
|
});
|
|
146
160
|
}
|
|
147
161
|
|
|
162
|
+
/**
|
|
163
|
+
* Route a red review verdict through the bounded self-fix loop.
|
|
164
|
+
*
|
|
165
|
+
* Same shape as the `test` lane, separate counter: `autonomous-execution`
|
|
166
|
+
* resets its N=3 budget on a different validation target, and a review finding
|
|
167
|
+
* is not a failing test. `verify-before-complete` is unaffected — the loop
|
|
168
|
+
* cannot report completion, and its exhausted exit is PARTIAL with the
|
|
169
|
+
* non-success verdict still on the surface.
|
|
170
|
+
*/
|
|
148
171
|
function _blocked_on_bad_verdict(state: DeliveryState, verdict: Any): StepResult {
|
|
149
172
|
const ticketId = _ticketId(state);
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
173
|
+
const signature = verdict_signature('verify', state.verify);
|
|
174
|
+
const decision = decide(state, 'verify', signature);
|
|
175
|
+
|
|
176
|
+
if (decision.kind === 'retry') {
|
|
177
|
+
record_attempt(state, 'verify', signature);
|
|
178
|
+
return retry_halt({
|
|
179
|
+
lane: 'verify',
|
|
180
|
+
ticket_id: ticketId,
|
|
181
|
+
verdict,
|
|
182
|
+
decision,
|
|
183
|
+
fix_hint: 'address the recorded findings, then re-run the four-judge review.',
|
|
184
|
+
rerun_directive: 'review-changes',
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return partial_exit({
|
|
189
|
+
lane: 'verify',
|
|
190
|
+
ticket_id: ticketId,
|
|
191
|
+
verdict,
|
|
192
|
+
decision,
|
|
193
|
+
unmet: unmet_dod(state),
|
|
194
|
+
rerun_directive: 'review-changes',
|
|
161
195
|
});
|
|
162
196
|
}
|
|
163
197
|
|
|
@@ -220,6 +220,7 @@ export class WorkState {
|
|
|
220
220
|
ui_polish: Dict | null;
|
|
221
221
|
contract: Dict | null;
|
|
222
222
|
stitch: Dict | null;
|
|
223
|
+
self_fix: Dict | null;
|
|
223
224
|
halts: Dict[];
|
|
224
225
|
version: number;
|
|
225
226
|
persona: string;
|
|
@@ -245,6 +246,7 @@ export class WorkState {
|
|
|
245
246
|
ui_polish?: Dict | null;
|
|
246
247
|
contract?: Dict | null;
|
|
247
248
|
stitch?: Dict | null;
|
|
249
|
+
self_fix?: Dict | null;
|
|
248
250
|
halts?: Dict[];
|
|
249
251
|
version?: number;
|
|
250
252
|
persona?: string;
|
|
@@ -269,6 +271,7 @@ export class WorkState {
|
|
|
269
271
|
this.ui_polish = opts.ui_polish ?? null;
|
|
270
272
|
this.contract = opts.contract ?? null;
|
|
271
273
|
this.stitch = opts.stitch ?? null;
|
|
274
|
+
this.self_fix = opts.self_fix ?? null;
|
|
272
275
|
this.halts = opts.halts ?? [];
|
|
273
276
|
this.version = opts.version ?? SCHEMA_VERSION;
|
|
274
277
|
this.persona = opts.persona ?? 'senior-engineer';
|
|
@@ -309,6 +312,7 @@ export function to_dict(state: WorkState): Dict {
|
|
|
309
312
|
_validate_ui_polish(state.ui_polish);
|
|
310
313
|
_validate_contract(state.contract);
|
|
311
314
|
_validate_stitch(state.stitch);
|
|
315
|
+
_validate_self_fix(state.self_fix);
|
|
312
316
|
_validate_halts(state.halts);
|
|
313
317
|
return {
|
|
314
318
|
version: state.version,
|
|
@@ -324,6 +328,7 @@ export function to_dict(state: WorkState): Dict {
|
|
|
324
328
|
ui_polish: state.ui_polish,
|
|
325
329
|
contract: state.contract,
|
|
326
330
|
stitch: state.stitch,
|
|
331
|
+
self_fix: state.self_fix,
|
|
327
332
|
halts: [...state.halts],
|
|
328
333
|
persona: state.persona,
|
|
329
334
|
memory: state.memory,
|
|
@@ -406,6 +411,9 @@ export function from_dict(payload: JsonValue): WorkState {
|
|
|
406
411
|
const stitch = payload['stitch'] ?? null;
|
|
407
412
|
_validate_stitch(stitch);
|
|
408
413
|
|
|
414
|
+
const self_fix = payload['self_fix'] ?? null;
|
|
415
|
+
_validate_self_fix(self_fix);
|
|
416
|
+
|
|
409
417
|
const halts = 'halts' in payload ? payload['halts'] : [];
|
|
410
418
|
_validate_halts(halts);
|
|
411
419
|
|
|
@@ -422,6 +430,7 @@ export function from_dict(payload: JsonValue): WorkState {
|
|
|
422
430
|
ui_polish: _isDict(ui_polish) ? { ...ui_polish } : null,
|
|
423
431
|
contract: _isDict(contract) ? { ...contract } : null,
|
|
424
432
|
stitch: _isDict(stitch) ? { ...stitch } : null,
|
|
433
|
+
self_fix: _isDict(self_fix) ? { ...self_fix } : null,
|
|
425
434
|
halts: Array.isArray(halts) ? ([...halts] as Dict[]) : [],
|
|
426
435
|
version: version as number,
|
|
427
436
|
persona: _get(payload, 'persona', 'senior-engineer') as string,
|
|
@@ -866,6 +875,71 @@ function _validate_ui_review(ui_review: JsonValue): void {
|
|
|
866
875
|
}
|
|
867
876
|
}
|
|
868
877
|
|
|
878
|
+
/**
|
|
879
|
+
* Reject malformed `self_fix` envelopes; tolerate `null` and `{}`.
|
|
880
|
+
*
|
|
881
|
+
* `null` means no red check has been seen. Once populated, each key is a lane
|
|
882
|
+
* name (`test`, `verify`) whose record may carry `attempts` (a non-negative
|
|
883
|
+
* int) and `signatures` (a list of strings). As with `ui_polish`, the schema
|
|
884
|
+
* enforces only shape: the ceiling and the no-progress floor are the handler's
|
|
885
|
+
* business (`directives/backend/_self_fix.ts`), so a state file cannot decide
|
|
886
|
+
* how many attempts are allowed.
|
|
887
|
+
*
|
|
888
|
+
* `attempts` is validated but NOT bounded here on purpose. A file carrying
|
|
889
|
+
* `attempts: 99` is well-formed and simply exhausted — rejecting it would turn
|
|
890
|
+
* a spent budget into a crash, and the loop's whole point is a clean exit.
|
|
891
|
+
*/
|
|
892
|
+
function _validate_self_fix(self_fix: JsonValue): void {
|
|
893
|
+
if (self_fix === null) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (!_isDict(self_fix)) {
|
|
897
|
+
throw new SchemaError(
|
|
898
|
+
`state.self_fix must be a JSON object or null; ` +
|
|
899
|
+
`got ${pyTypeName(self_fix)}`,
|
|
900
|
+
);
|
|
901
|
+
}
|
|
902
|
+
for (const [lane, record] of Object.entries(self_fix)) {
|
|
903
|
+
if (!_isDict(record)) {
|
|
904
|
+
throw new SchemaError(
|
|
905
|
+
`state.self_fix.${lane} must be a JSON object; ` +
|
|
906
|
+
`got ${pyTypeName(record)}`,
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
if ('attempts' in record) {
|
|
910
|
+
const attempts = record['attempts'];
|
|
911
|
+
if (
|
|
912
|
+
typeof attempts !== 'number' ||
|
|
913
|
+
typeof attempts === 'boolean' ||
|
|
914
|
+
!Number.isInteger(attempts) ||
|
|
915
|
+
attempts < 0
|
|
916
|
+
) {
|
|
917
|
+
throw new SchemaError(
|
|
918
|
+
`state.self_fix.${lane}.attempts must be a non-negative ` +
|
|
919
|
+
`integer when present; got ${pyRepr(attempts)}`,
|
|
920
|
+
);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
if ('signatures' in record) {
|
|
924
|
+
const signatures = record['signatures'];
|
|
925
|
+
if (!Array.isArray(signatures)) {
|
|
926
|
+
throw new SchemaError(
|
|
927
|
+
`state.self_fix.${lane}.signatures must be a list when ` +
|
|
928
|
+
`present; got ${pyTypeName(signatures)}`,
|
|
929
|
+
);
|
|
930
|
+
}
|
|
931
|
+
for (const entry of signatures) {
|
|
932
|
+
if (typeof entry !== 'string') {
|
|
933
|
+
throw new SchemaError(
|
|
934
|
+
`state.self_fix.${lane}.signatures entries must be ` +
|
|
935
|
+
`strings; got ${pyTypeName(entry)}`,
|
|
936
|
+
);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
869
943
|
/**
|
|
870
944
|
* Reject malformed `ui_polish` envelopes; tolerate `null` and `{}`.
|
|
871
945
|
*
|
|
@@ -150,6 +150,7 @@ export function _to_delivery(work: WorkState): DeliveryState {
|
|
|
150
150
|
contract: work.contract as Record<string, DeliveryAny> | null,
|
|
151
151
|
stitch: work.stitch as Record<string, DeliveryAny> | null,
|
|
152
152
|
stack: work.stack as Record<string, DeliveryAny> | null,
|
|
153
|
+
self_fix: work.self_fix as Record<string, DeliveryAny> | null,
|
|
153
154
|
});
|
|
154
155
|
}
|
|
155
156
|
|
|
@@ -182,6 +183,11 @@ export function _sync_back(work: WorkState, delivery: DeliveryState): void {
|
|
|
182
183
|
work.contract = delivery.contract as Dict | null;
|
|
183
184
|
work.stitch = delivery.stitch as Dict | null;
|
|
184
185
|
work.stack = delivery.stack as Dict | null;
|
|
186
|
+
// Reassigned (not mutated) by `record_attempt`, so the defensive mirror
|
|
187
|
+
// above is load-bearing here rather than a no-op: without this line the
|
|
188
|
+
// attempt counter would reset on every dispatch and the loop would never
|
|
189
|
+
// reach either floor.
|
|
190
|
+
work.self_fix = delivery.self_fix as Dict | null;
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
/**
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Committed target table for the road-to-cost-parity-* family (part 0, Phase 1). Registers what a session costs today, what the family is steering toward, and what the rule payload actually is PER HOST — the correction that matters most, because every diet target in the source drafts was set against one aggregate number that describes no host. Nothing here changes behaviour: no carrier ships for these thresholds (part 0 Phase 4.5), and no gate reads the target columns. What IS gated is the file's own shape — lint_budget_ownership rejects a row without a `source`, and rejects a `revised_from` without `revision_evidence`, so a number cannot enter or move without a traceable origin.",
|
|
3
|
+
"schema_version": 1,
|
|
4
|
+
"registered_at": "2026-08-10",
|
|
5
|
+
"owner": "maintainer",
|
|
6
|
+
"review_by": "2026-11-10",
|
|
7
|
+
"row_schema": {
|
|
8
|
+
"_comment": "Reading of part 0 § 1.2b: `schema_version` / `registered_at` / `owner` / `review_by` are FILE-level (as in every sibling budget file — a per-row copy would be four constants repeated N times), and every ROW carries `source` plus the three honest-null fields from § 1.3. The validator enforces exactly that split.",
|
|
9
|
+
"required_row_fields": ["id", "unit", "baseline", "source", "revisable", "revision_evidence", "revised_from"],
|
|
10
|
+
"honest_null_contract": "A target bends to a measurement, never to a preference. `revisable: true` says the number is revisable at all; `revised_from` records what it used to be once it moves; `revision_evidence` must then point at the evidence note that forced the move. The validator fails the file when `revised_from` is set and `revision_evidence` is empty — a revision without published evidence is the one form of drift this schema exists to refuse."
|
|
11
|
+
},
|
|
12
|
+
"measurement": {
|
|
13
|
+
"_comment": "Both measurement rules from part 0's Context are fields here, not prose: a rule that lives only in a paragraph is a rule the next author does not inherit.",
|
|
14
|
+
"measurement_date": "2026-08-10",
|
|
15
|
+
"commit": "c073d5732fb890bbf8b41ec879f62f651c7ba293",
|
|
16
|
+
"projection_regenerated": true,
|
|
17
|
+
"regeneration_command": "task sync && task generate-tools — verified no-op afterwards (git status clean), so the payload rows below measure the commit and not when someone last ran a generator",
|
|
18
|
+
"emitter_generation": "real, frontmatter-less projection files (25 of 110 .claude/rules entries carry a `paths:` block; the rest carry no frontmatter). NOT the older emitter that symlinked .claude/rules/* into dist/agent-src/rules/ and so inherited dist's full frontmatter. This is the THIRD variable behind the 92-vs-110 file-count spread: a projection count is a fact about which emitter last wrote the tree, on top of how stale that tree is.",
|
|
19
|
+
"carrier_divergence_admitted": false,
|
|
20
|
+
"carrier_divergence_reason": "report_carrier_divergence compares a globally installed carrier against the project projection on disk; both can be stale independently, so it measures relative cache staleness rather than any property of the source. It may diagnose a behind-the-times install; it may never gate a phase, and no row here is derived from it."
|
|
21
|
+
},
|
|
22
|
+
"session_cost_targets": [
|
|
23
|
+
{
|
|
24
|
+
"id": "median_final_context_tokens",
|
|
25
|
+
"unit": "billable input tokens of the last main-chain assistant record (per cc_transcript.ts)",
|
|
26
|
+
"baseline": 519349,
|
|
27
|
+
"interim": 300000,
|
|
28
|
+
"target": 200000,
|
|
29
|
+
"direction": "lower_is_better",
|
|
30
|
+
"source": "agents/evidence/analysis/token-economy-recycling-phase1.md:45 — median over 201 sessions with parseable final usage (:44) out of 205 non-empty (:10)",
|
|
31
|
+
"revisable": true,
|
|
32
|
+
"revision_evidence": "",
|
|
33
|
+
"revised_from": null
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"id": "p90_final_context_tokens",
|
|
37
|
+
"unit": "billable input tokens of the last main-chain assistant record",
|
|
38
|
+
"baseline": 807937,
|
|
39
|
+
"interim": 550000,
|
|
40
|
+
"target": 400000,
|
|
41
|
+
"direction": "lower_is_better",
|
|
42
|
+
"source": "agents/evidence/analysis/token-economy-recycling-phase1.md:45 — same 201-session corpus",
|
|
43
|
+
"revisable": true,
|
|
44
|
+
"revision_evidence": "",
|
|
45
|
+
"revised_from": null
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "auto_compact_incidence",
|
|
49
|
+
"unit": "share of sessions with >= 1 auto-compaction event",
|
|
50
|
+
"baseline": 0.112,
|
|
51
|
+
"interim": 0.05,
|
|
52
|
+
"target": 0.0,
|
|
53
|
+
"direction": "lower_is_better",
|
|
54
|
+
"source": "agents/evidence/analysis/token-economy-recycling-phase1.md:59 — 23 of 205 sessions, 31 events (:60), all trigger:\"auto\"",
|
|
55
|
+
"revisable": true,
|
|
56
|
+
"revision_evidence": "",
|
|
57
|
+
"revised_from": null
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "late_early_per_call_cost_ratio",
|
|
61
|
+
"unit": "median billable input per call over the last 20 calls / the first 20 calls of a session",
|
|
62
|
+
"baseline": 2.1,
|
|
63
|
+
"interim": 1.6,
|
|
64
|
+
"target": 1.3,
|
|
65
|
+
"direction": "lower_is_better",
|
|
66
|
+
"source": "agents/evidence/analysis/token-economy-recycling-phase1.md:69-75 — 145 sessions with >= 200 main-chain calls; median early 279,859 vs late 594,985; p90 ratio 3.3x",
|
|
67
|
+
"revisable": true,
|
|
68
|
+
"revision_evidence": "",
|
|
69
|
+
"revised_from": null
|
|
70
|
+
}
|
|
71
|
+
],
|
|
72
|
+
"payload_rows": [
|
|
73
|
+
{
|
|
74
|
+
"_comment": "Per host, never one number. The aggregate the source drafts dieted against (\"419 KB always-loaded layer\") describes no host that exists: it is the whole rule tree, and only 9 of 116 rules are type: always.",
|
|
75
|
+
"id": "src_rules_maintained",
|
|
76
|
+
"unit": "bytes across src/rules/*.md",
|
|
77
|
+
"baseline": 424896,
|
|
78
|
+
"files": 116,
|
|
79
|
+
"target_bytes": null,
|
|
80
|
+
"target_owner": "none — this is the maintained source, not a delivered payload; it shrinks only as a consequence of a diet elsewhere",
|
|
81
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration (see `measurement`)",
|
|
82
|
+
"revisable": true,
|
|
83
|
+
"revision_evidence": "",
|
|
84
|
+
"revised_from": null
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "dist_agent_src_rules",
|
|
88
|
+
"unit": "bytes across dist/agent-src/rules/*.md",
|
|
89
|
+
"baseline": 420051,
|
|
90
|
+
"files": 115,
|
|
91
|
+
"target_bytes": null,
|
|
92
|
+
"target_owner": "none — the tracked projection source. It is the ONLY rule-payload figure that is stable across checkouts, because it is committed; every other row below is regenerated and therefore machine-dependent.",
|
|
93
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration",
|
|
94
|
+
"revisable": true,
|
|
95
|
+
"revision_evidence": "",
|
|
96
|
+
"revised_from": null
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "augment_rules_payload",
|
|
100
|
+
"unit": "bytes across .augment/rules/*.md",
|
|
101
|
+
"baseline": 420051,
|
|
102
|
+
"files": 115,
|
|
103
|
+
"target_bytes": null,
|
|
104
|
+
"target_owner": "src/config/budgets.yml § standing_rule_delivery (total_cap_tokens 110000) already owns the delivered-rule ceiling. This family does not open a second, byte-denominated target for the same surface — a metric with two owners is a metric with none.",
|
|
105
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration; mirrors dist/ by symlink",
|
|
106
|
+
"revisable": true,
|
|
107
|
+
"revision_evidence": "",
|
|
108
|
+
"revised_from": null
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
"id": "claude_rules_payload",
|
|
112
|
+
"unit": "bytes across .claude/rules/*.md",
|
|
113
|
+
"baseline": 339035,
|
|
114
|
+
"files": 110,
|
|
115
|
+
"target_bytes": null,
|
|
116
|
+
"target_owner": "src/config/budgets.yml § standing_rule_delivery — same ceiling, same reason as the augment row",
|
|
117
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration; 25 of 110 files carry a `paths:` block, the rest none",
|
|
118
|
+
"revisable": true,
|
|
119
|
+
"revision_evidence": "",
|
|
120
|
+
"revised_from": null
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"_comment": "CORRECTION to part 0's triage table, which recorded these two as \"~3 KB / ~0 KB\" and therefore as already-lean hosts needing no target. Re-measured post-regeneration they are 408 KB and 330 KB — a full payload each. The earlier reading is a stale-projection artefact of exactly the class measurement rule 1 warns about: both carriers are UNTRACKED and written only by `task generate-tools`, so a checkout that has not run it reads them as near-empty. Risk 3's mitigation (\"the lean hosts need no target\") does not survive the re-measure and is not encoded here.",
|
|
124
|
+
"id": "clinerules_payload",
|
|
125
|
+
"unit": "bytes across .clinerules/*.md",
|
|
126
|
+
"baseline": 417703,
|
|
127
|
+
"files": 111,
|
|
128
|
+
"target_bytes": null,
|
|
129
|
+
"target_owner": "unowned — a diet for this host belongs to the parked rule-payload part of this family, not to part 0",
|
|
130
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration; untracked, generated by task generate-tools",
|
|
131
|
+
"revisable": true,
|
|
132
|
+
"revision_evidence": "",
|
|
133
|
+
"revised_from": null
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"id": "windsurfrules_payload",
|
|
137
|
+
"unit": "bytes of the single .windsurfrules file",
|
|
138
|
+
"baseline": 338160,
|
|
139
|
+
"files": 1,
|
|
140
|
+
"target_bytes": null,
|
|
141
|
+
"target_owner": "unowned — see the clinerules row; same correction, same parked owner",
|
|
142
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration; untracked, generated by task generate-tools",
|
|
143
|
+
"revisable": true,
|
|
144
|
+
"revision_evidence": "",
|
|
145
|
+
"revised_from": null
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
"id": "gemini_md_payload",
|
|
149
|
+
"unit": "bytes of GEMINI.md",
|
|
150
|
+
"baseline": 2982,
|
|
151
|
+
"files": 1,
|
|
152
|
+
"target_bytes": null,
|
|
153
|
+
"target_owner": "none — genuinely lean, and the only host in this table for which that is true. It is a pointer file, not a rule payload.",
|
|
154
|
+
"source": "measured 2026-08-10 at commit c073d5732, post-regeneration",
|
|
155
|
+
"revisable": true,
|
|
156
|
+
"revision_evidence": "",
|
|
157
|
+
"revised_from": null
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
"anchors": {
|
|
161
|
+
"premium_band_tokens": 200000,
|
|
162
|
+
"rationale": "Above ~200k a session sits in the premium pricing band, which is why the median target is set there rather than at a round fraction of the baseline.",
|
|
163
|
+
"known_noise_problem": "187 of 205 observed sessions end at or above 200k, so a session-end hint at this threshold would fire on ~91% of sessions — a constant line, not an advisory. The 200k figure is a TARGET here and nothing else.",
|
|
164
|
+
"no_carrier_ships": "Part 0 § 4.5: no session-end advisory ships for this anchor. The existing 800k recycle threshold (src/config/recycle-threshold-budget.json) stays the only strong line, and any carrier for 200k is a separate, separately-argued change.",
|
|
165
|
+
"source": "agents/evidence/analysis/token-economy-recycling-phase1.md — session-final distribution (:45) over the 205-session corpus (:10)"
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -40,13 +40,16 @@
|
|
|
40
40
|
# visible, or it repeats the sin it exists to catch.
|
|
41
41
|
# canary optional mutation recipe (Phase 7) — see CANARY below
|
|
42
42
|
#
|
|
43
|
-
# COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-08-
|
|
43
|
+
# COVERAGE — THE HONEST DENOMINATOR (re-measured 2026-08-10)
|
|
44
44
|
# ----------------------------------------------------------
|
|
45
|
-
#
|
|
45
|
+
# 251 `lint_*` / `check_*` / `audit_*` scripts live under `src/scripts/`
|
|
46
46
|
# (the count is computed, not asserted — `check_gate_coverage.count_gate_scripts`).
|
|
47
|
-
# **
|
|
48
|
-
# CI-identical argv, and all
|
|
47
|
+
# **33 of them emit the machine-readable `scanned: <N>` line under their
|
|
48
|
+
# CI-identical argv, and all 33 are listed below.** The manifest is therefore
|
|
49
49
|
# complete with respect to its own contract, and covers ~13 % of the population.
|
|
50
|
+
# (The 2026-08-05 reading said "32 … and all 31 are listed": the two halves of
|
|
51
|
+
# one sentence disagreed by one, which is the drift this paragraph exists to
|
|
52
|
+
# make visible. Both are recounted above, not adjusted toward each other.)
|
|
50
53
|
#
|
|
51
54
|
# Two revisions of this header have now gone stale the same way. It claimed
|
|
52
55
|
# "8 of 211", then "22 of 223" while 26 entries were listed. Both halves drift
|
|
@@ -98,7 +101,7 @@
|
|
|
98
101
|
# which is the shape this repo tells a gate author to use — writes the line from
|
|
99
102
|
# inside the library, so its own source never matches and the test cannot see it.
|
|
100
103
|
# Live split: **1 unregistered gate emits the inline shape (`check_gate_coverage`
|
|
101
|
-
# itself), and
|
|
104
|
+
# itself), and 205 of 251 gate scripts emit through the helper.** So for the
|
|
102
105
|
# recommended shape the sentence above is false, and the list CAN fall behind.
|
|
103
106
|
#
|
|
104
107
|
# Not widened here, deliberately. Widening the regex to include the helper turns
|
|
@@ -557,6 +560,42 @@ gates:
|
|
|
557
560
|
untouched original stayed clean. Floor at 450 against a live 503 leaves
|
|
558
561
|
room for skill retirement without redding the change that retires one.
|
|
559
562
|
|
|
563
|
+
- id: lint_workflow_paths
|
|
564
|
+
argv: []
|
|
565
|
+
min_scanned: 20
|
|
566
|
+
corpus: "every workflow under .github/workflows (28 at baseline); the unit is the file, and each file's `paths:` / `paths-ignore:` entries are resolved against `git ls-files`"
|
|
567
|
+
status: enforced
|
|
568
|
+
canary:
|
|
569
|
+
class: undetected-antipattern
|
|
570
|
+
path: .github/workflows/zz-canary-workflow-paths.yml
|
|
571
|
+
content: |
|
|
572
|
+
name: Canary
|
|
573
|
+
on:
|
|
574
|
+
pull_request:
|
|
575
|
+
paths:
|
|
576
|
+
- "retired/tree/that/never/existed/**"
|
|
577
|
+
jobs:
|
|
578
|
+
noop:
|
|
579
|
+
runs-on: ubuntu-latest
|
|
580
|
+
steps:
|
|
581
|
+
- run: echo canary
|
|
582
|
+
note: >
|
|
583
|
+
Registered STRICT on day one for the same reason `lint_example_fences`
|
|
584
|
+
was, and on the same evidence standard: the classification pass ran
|
|
585
|
+
BEFORE the gate existed and returned **20 dead entries across 6 workflow
|
|
586
|
+
files**, each classified by hand into one of four causes — a tree retired
|
|
587
|
+
by ADR-051, a gitignored projection that can never appear in a diff, two
|
|
588
|
+
paths moved by the Python-to-TS migration, and bare root paths whose real
|
|
589
|
+
files sit under `src/`. All 20 were repaired in the change that added the
|
|
590
|
+
gate, so the corpus is verified empty rather than unexamined, and an
|
|
591
|
+
advisory gate over it could only ever be ignored. Floor at 20 against a
|
|
592
|
+
live 28 so retiring a workflow does not red the change that retires it.
|
|
593
|
+
Worth recording that the highest-severity hit was invisible to the hand
|
|
594
|
+
census and only the gate found it: `release-validation.yml` filtered on a
|
|
595
|
+
root `marketplace.json` that has never existed, so a version drift in the
|
|
596
|
+
real manifest did not re-trigger the `version-consistency` job whose
|
|
597
|
+
entire purpose is catching that drift.
|
|
598
|
+
|
|
560
599
|
- id: check_depth_budget
|
|
561
600
|
argv: []
|
|
562
601
|
min_scanned: 140
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Pre-registered council ATTENDANCE metrics (road-to-inbox-harvest-2026-08-b Phase 1.4). Registered here rather than in roadmap prose because a roadmap is archived when it closes and a metric outlives it — the sibling budget files (hook-token-budget.json, recycle-threshold-budget.json) already carry the owner / review_by / honest-gap discipline this reuses. The instrument is the quorum_result line appendQuorumEvent writes to agents/runtime/council/events.log (schema v2); nothing here is measured yet, and no threshold is committed before data exists.",
|
|
3
|
+
"schema_version": 1,
|
|
4
|
+
"registered_at": "2026-08-10",
|
|
5
|
+
"owner": "maintainer",
|
|
6
|
+
"review_by": "2026-11-10",
|
|
7
|
+
"unit": "quorum_result event lines in agents/runtime/council/events.log",
|
|
8
|
+
"privacy": "a quorum_result line carries {phase, command, dispatch, verdict, threshold, configured_total, total, present, solo, absent[{member, reason}]} — counts, closed vocabularies, and member ids only. The type has no field able to hold free-form content, so the CLI's own `detail` string (provider error text, which can embed paths and prompt fragments) is dropped at the boundary rather than scrubbed.",
|
|
9
|
+
"phase_split": "A pass emits `pre_run` (build_members — who could be constructed) and, on the `run` path, `post_run` (_postRunQuorum — who actually returned a usable response). They disagree whenever a member constructs and then fails mid-flight, so a rate computed over both double-counts. A `pre_run` line is NOT evidence a pass ran: `command: estimate` is a spend-free preview, and a `run` / `debate` line is written before the `--confirm` gate (which defaults to false and returns before any provider call) and before the necessity and size-fit gates. A `post_run` line is the only line that proves providers were called — which is why every rate below is post_run-scoped, and why `command` exists at all.",
|
|
10
|
+
"attendance_rate": {
|
|
11
|
+
"definition": "of the members that entered the pass, how many answered: present / total per post_run pass. Bounded by 1 by construction (evaluateQuorum clamps present into [0, total]).",
|
|
12
|
+
"instrument": "quorum_result lines, phase=post_run, command=run, fields present + total",
|
|
13
|
+
"threshold": "none committed before data — baseline first, threshold at review date"
|
|
14
|
+
},
|
|
15
|
+
"roster_shortfall_rate": {
|
|
16
|
+
"definition": "how often the council shrank BEFORE the pass: share of post_run passes with total < configured_total, plus the size of the gap. This is the half attendance_rate cannot see — a pass with 3 configured, 2 failing to construct and 1 answering reads 1/1 = 100 % attendance and a shortfall of 2. Both numbers are needed and neither substitutes for the other; a single ratio over mixed rosters is the error this pair replaces.",
|
|
17
|
+
"instrument": "quorum_result lines, phase=post_run, command=run, configured_total vs total",
|
|
18
|
+
"threshold": "none committed before data — baseline first, threshold at review date",
|
|
19
|
+
"not_an_error": "total > configured_total is legitimate and not a shortfall: `--siblings` fans ONE enabled config entry into N clients, so the client roster can exceed the entry count. configured_total counts enabled config ENTRIES; total counts CLIENTS. Compare them for direction, never divide one by the other."
|
|
20
|
+
},
|
|
21
|
+
"solo_conclusion_rate": {
|
|
22
|
+
"definition": "share of post_run passes with `solo: true` — written by the isSoloConcluded predicate in quorum.ts, never re-derived from the numbers here, so one change to the rule cannot leave this definition stale. ceil(n/2) makes 1-of-2 a legitimate conclusion; this measures how often that legitimacy is used, not whether it should be. THREE cases carry solo: true and they are not the same finding — a degraded multi-member council (configured_total > 1), a council configured with one member (configured_total = 1, dispatch = full), and a deliberate --single dispatch (dispatch = single). Split on dispatch and configured_total before reading the rate.",
|
|
23
|
+
"instrument": "quorum_result lines, phase=post_run, command=run, field solo, split by dispatch + configured_total",
|
|
24
|
+
"threshold": "none committed before data. The quorum-solo-floor blocker pre-registers the three outcomes this rate decides between — a third CLI member, a gate-scoped min_present: 2, or a published null under 5 % — and that choice is made against real data, never ahead of it. It is decided on the degraded case only; a --single pass is the operator asking for one voice, not the council failing to assemble."
|
|
25
|
+
},
|
|
26
|
+
"absent_reason_distribution": {
|
|
27
|
+
"definition": "count per reason over absent[] entries — no_binary / no_auth / timeout / quota (AbsentReason) plus the two runtime fallbacks the CLI writes directly, `unavailable` (transport resolved to nothing) and `binary_missing` (a CLI client refused to construct). Distinguishes a council degraded by configuration from one degraded by provider behaviour, which have opposite fixes.",
|
|
28
|
+
"instrument": "quorum_result lines, absent[].reason, split by phase (a pre_run absence is configuration; a post_run absence is mid-flight). Excluding `command: estimate` is NOT sufficient: `run` and `debate` also reach construction and return at the `--confirm` gate, which defaults to false. The reliable filter is a pre_run line whose pass produced a matching post_run line; a pre_run line with no post_run partner reached construction and stopped there, whatever its command says.",
|
|
29
|
+
"threshold": "none committed before data — the distribution IS the finding at review date",
|
|
30
|
+
"merge_before_reading": "`no_binary` and `binary_missing` are two spellings of one condition — the CLI binary is unusable — written by different code paths (transport_resolver's static classification vs the CLI client's construction failure). They are recorded separately because that is what the tree emits, not because they are separate findings; merge them before reading the distribution, or the largest bucket is split in two."
|
|
31
|
+
},
|
|
32
|
+
"honest_null_consequence": "If the log accumulates and the solo-conclusion rate sits near zero, that is the published answer to the quorum-solo-floor blocker and 1.5 is cancelled against it — not quietly dropped. If attendance data never accumulates at all (nobody runs a metered council pass in the window), that is reported as an unmeasured metric at the review date, never as a passing one.",
|
|
33
|
+
"honest_gaps": [
|
|
34
|
+
"`lens` and `invocation` are empty on every pre_run line — neither is known at member-construction time — so any per-lens breakdown is post_run-only.",
|
|
35
|
+
"`command: debate` carries the pre_run half ONLY. There is no _postRunQuorum on the debate path, and defining `usable response` for a multi-round debate is a separate question this registration does not answer. So debate contributes to absent_reason_distribution and to nothing else: attendance_rate and solo_conclusion_rate cover the `run` path, and a reader must not read them as covering every metered pass.",
|
|
36
|
+
"A pre_run line proves construction happened, never that a pass followed — the `--confirm` gate (default false), the necessity gate and the size-fit gate all abort afterwards and nothing on the line records it. Any count over pre_run lines is an upper bound; pair it with a post_run line before treating it as a pass.",
|
|
37
|
+
"The CLI emit wiring itself has no test. The two modules are covered directly, but nothing exercises build_members / cmd_run / cmd_debate end-to-end against a captured log — and `_emitQuorumEvent` forwards no AppendEventOptions, so there is no target to capture into. Two defects in this file's first version (a `dispatch` written from the flag rather than the outcome, and a rate mixing config entries with clients) were found by review rather than by a test, which is what that gap costs."
|
|
38
|
+
]
|
|
39
|
+
}
|