@aiwg/cli 2026.10.11 → 2026.10.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/agentic/code/providers/model-catalog.v1.json +62 -12
  2. package/dist/src/artifacts/binding-target-identity.js +51 -0
  3. package/dist/src/artifacts/checksum-manifest.js +12 -0
  4. package/dist/src/artifacts/index-builder.js +11 -13
  5. package/dist/src/artifacts/query-engine.js +5 -11
  6. package/dist/src/cli/handlers/doctor-help.js +32 -0
  7. package/dist/src/cli/handlers/gates.js +19 -18
  8. package/dist/src/cli/handlers/models-help.js +7 -1
  9. package/dist/src/cli/handlers/models.js +3 -3
  10. package/dist/src/cli/handlers/script-runner.js +8 -5
  11. package/dist/src/cli/handlers/sessions-intake.js +104 -0
  12. package/dist/src/cli/handlers/sessions-tail.js +172 -0
  13. package/dist/src/cli/handlers/sessions.js +90 -17
  14. package/dist/src/cli/handlers/skill-lint.js +10 -3
  15. package/dist/src/cli/handlers/steward-format.js +100 -0
  16. package/dist/src/cli/handlers/steward-models.js +167 -0
  17. package/dist/src/cli/handlers/steward.js +14 -248
  18. package/dist/src/cli/handlers/use-bundle-counts.js +61 -0
  19. package/dist/src/cli/handlers/use.js +6 -24
  20. package/dist/src/cli/handlers/utilities.js +1 -24
  21. package/dist/src/config/aiwg-config.js +7 -0
  22. package/dist/src/config/authorization-cli.js +89 -0
  23. package/dist/src/config/cli-help.js +1 -0
  24. package/dist/src/config/cli.js +10 -1
  25. package/dist/src/decision/context-pruning.js +3 -2
  26. package/dist/src/decision/ensemble/contract.js +15 -4
  27. package/dist/src/decision/ensemble-study/artifacts.js +5 -0
  28. package/dist/src/decision/ensemble-study/score.js +4 -3
  29. package/dist/src/decision/feature-export.js +3 -9
  30. package/dist/src/decision/heldout/collector.js +2 -2
  31. package/dist/src/decision/issue-triage/runtime.js +3 -9
  32. package/dist/src/decision/qualification/gates.js +2 -1
  33. package/dist/src/decision/qualification/release.js +2 -3
  34. package/dist/src/decision/routing/report.js +2 -0
  35. package/dist/src/decision/sdlc-screening.js +3 -5
  36. package/dist/src/decision/sensitivity/comparative.js +5 -3
  37. package/dist/src/gates/driver.js +6 -77
  38. package/dist/src/gates/evaluate.js +4 -11
  39. package/dist/src/gates/floors.js +4 -4
  40. package/dist/src/gates/index.js +3 -3
  41. package/dist/src/gates/lattice.js +26 -0
  42. package/dist/src/gates/registry.js +2 -1
  43. package/dist/src/gates/report.js +19 -7
  44. package/dist/src/issues/audit-cli.js +120 -0
  45. package/dist/src/issues/audit-contract.js +64 -0
  46. package/dist/src/issues/audit-local.js +82 -0
  47. package/dist/src/issues/audit.js +220 -0
  48. package/dist/src/lint/rule-activation.js +15 -1
  49. package/dist/src/mission-protocol/canonical-codecs.js +270 -0
  50. package/dist/src/mission-protocol/codecs.js +22 -550
  51. package/dist/src/mission-protocol/health-trace-codecs.js +281 -0
  52. package/dist/src/mission-protocol/health-trace-types.js +43 -0
  53. package/dist/src/mission-protocol/types.js +2 -42
  54. package/dist/src/mission-protocol/worker-contract-json.js +10 -0
  55. package/dist/src/mission-protocol/worker-review-gate.js +100 -0
  56. package/dist/src/mission-protocol/worker-review-types.js +2 -0
  57. package/dist/src/models/catalog-policy.js +280 -0
  58. package/dist/src/models/index.js +1 -1
  59. package/dist/src/models/model-catalog.v1.json +62 -12
  60. package/dist/src/models/policy-resources.js +28 -0
  61. package/dist/src/models/provider-policy.js +10 -546
  62. package/dist/src/models/resolver.js +26 -8
  63. package/dist/src/models/router.js +15 -2
  64. package/dist/src/models/tiers.js +13 -0
  65. package/dist/src/models/types.js +0 -6
  66. package/dist/src/models/worker-ceilings.js +59 -0
  67. package/dist/src/models/worker-invocation-policy.js +243 -0
  68. package/dist/src/models/worker-plan.js +16 -20
  69. package/dist/src/models/worker-review-lineage.js +55 -0
  70. package/dist/src/models/worker-selection-policy.js +1 -1
  71. package/dist/src/models/wrapper-route.js +6 -3
  72. package/dist/src/policy/authorization.js +1 -0
  73. package/dist/src/policy/hitl-config.js +63 -0
  74. package/dist/src/policy/hitl-receipt.js +57 -0
  75. package/dist/src/policy/hitl-types.js +4 -0
  76. package/dist/src/policy/hitl-validation.js +39 -0
  77. package/dist/src/policy/hitl.js +117 -0
  78. package/dist/src/rules/activation.d.mts +1 -1
  79. package/dist/src/rules/activation.mjs +0 -2
  80. package/dist/src/rules/gate-coverage.js +51 -0
  81. package/dist/src/rules/gate-references.js +16 -0
  82. package/dist/src/rules/rule-frontmatter.schema.json +7 -1
  83. package/dist/src/serve/worker-command-request.js +21 -4
  84. package/dist/src/serve/worker-invocation-preparation.js +3 -1
  85. package/dist/src/serve/worker-invocation.js +21 -10
  86. package/dist/src/sessions/adapters/deepseek-harness.js +1 -0
  87. package/dist/src/sessions/adapters/grok-build-native.js +269 -0
  88. package/dist/src/sessions/adapters/grok-build.js +57 -15
  89. package/dist/src/sessions/adapters/hermes-state-db.js +252 -0
  90. package/dist/src/sessions/adapters/hermes.js +42 -3
  91. package/dist/src/sessions/adapters/warp.js +1 -1
  92. package/dist/src/sessions/batch-import.js +10 -0
  93. package/dist/src/sessions/contracts.js +1 -1
  94. package/dist/src/sessions/discovery-capability.js +38 -0
  95. package/dist/src/sessions/importer.js +8 -251
  96. package/dist/src/sessions/index.js +4 -0
  97. package/dist/src/sessions/intake-dir.js +71 -0
  98. package/dist/src/sessions/normalization.js +251 -0
  99. package/dist/src/sessions/tail.js +91 -0
  100. package/dist/src/sessions/workspace-discovery.js +83 -7
  101. package/dist/src/skills/agent-skill-sidecar.js +28 -0
  102. package/dist/src/skills/agent-skills.js +4 -23
  103. package/dist/src/skills/claude-surface-projection.js +25 -0
  104. package/dist/src/skills/deployer.js +8 -288
  105. package/dist/src/skills/deployment-policy.js +114 -0
  106. package/dist/src/skills/deployment-projection.js +152 -0
  107. package/dist/src/skills/deployment-types.js +20 -0
  108. package/dist/src/skills/exporter.js +8 -31
  109. package/dist/src/skills/provider-extensions.js +41 -0
  110. package/dist/src/skills/run.js +6 -0
  111. package/dist/src/skills/types.js +0 -8
  112. package/dist/src/skills/validator-resources.js +68 -0
  113. package/dist/src/skills/validator.js +6 -55
  114. package/dist/src/storage/cli-migrate-options.js +125 -0
  115. package/dist/src/storage/cli-migrate.js +111 -0
  116. package/dist/src/storage/cli.js +4 -279
  117. package/dist/src/storage/migration-adapter.js +79 -0
  118. package/dist/src/storage/migration-copy.js +108 -0
  119. package/dist/src/storage/migration-protocol.js +14 -197
  120. package/dist/src/storage/migration-records.js +186 -0
  121. package/dist/src/storage/migration-types.js +12 -0
  122. package/dist/src/tracker/forge-http.js +2 -1
  123. package/dist/src/tracker/issue-pages.js +110 -0
  124. package/package.json +1 -1
  125. package/schemas/mission-v1.schema.json +335 -0
  126. package/schemas/models/model-catalog.v1.schema.json +19 -1
  127. package/schemas/models/model-config-compatibility.v1.schema.json +3 -2
  128. package/schemas/models/model-policy.v1.schema.json +1 -1
  129. package/tools/agents/providers/provider-paths-staging.mjs +35 -0
  130. package/tools/agents/providers/rule-binding-targets.mjs +146 -0
  131. package/tools/agents/providers/rule-delivery.mjs +7 -126
  132. package/vscode-extension/schemas/aiwg.config.v1.json +177 -0
@@ -0,0 +1,281 @@
1
+ import { stableJson } from './worker-contract-json.js';
2
+ // Pure Mission supervision trace and lease mapping codecs.
3
+ import { createHash } from 'node:crypto';
4
+ import { assertValidWorkerPayload, validateWorkerPayload } from './codecs.js';
5
+ import { WorkerContractError, HEALTH_ACTIVITY_SUMMARY_MAX_UTF8_BYTES, HEALTH_KNOWN_REASON_CODES } from './types.js';
6
+ // S1 health-to-lease and Mission reconciliation mapping (B-OG-54 / #2916,
7
+ // 05 §8, ADR-OG-10/13). Pure functions over validated shapes: no scheduler,
8
+ // store, Mission runtime or managed import. S2 owners (B-OG-12/16) wire these
9
+ // verdicts into actual renew/fence/stop/settle/release runtime.
10
+ const HEALTH_TERMINAL_LIKE = new Set(['completed', 'failed', 'cancelled', 'crashed', 'failed-to-start']);
11
+ /**
12
+ * Classify supervision evidence into a distinct shared-trace kind. Quiet,
13
+ * stalled and uncertain map distinctly. Heartbeat/lease-only observation
14
+ * yields activity-only, never productive work. A crashed or failed-to-start
15
+ * worker without confirmed termination is terminal-unconfirmed: fail-stop is
16
+ * claimed but a live descendant may survive, so it is never stop proof
17
+ * (ADR-OG-10). Precondition: schema-valid workerHealthSummary.
18
+ */
19
+ export function classifyHealthTrace(health) {
20
+ if (health.termination === 'confirmed' && HEALTH_TERMINAL_LIKE.has(health.state))
21
+ return 'terminal-proof';
22
+ if (HEALTH_TERMINAL_LIKE.has(health.state))
23
+ return 'terminal-unconfirmed';
24
+ // Confirmed termination with a non-terminal state is contradictory owner
25
+ // evidence (finish() never produces it): downgrade, never productive.
26
+ if (health.termination === 'confirmed')
27
+ return 'uncertain';
28
+ if (health.termination === 'uncertain')
29
+ return 'uncertain';
30
+ switch (health.state) {
31
+ case 'launching':
32
+ case 'started':
33
+ return 'setup';
34
+ case 'active':
35
+ return health.lastProgressAt === null ? 'activity-only' : 'productive';
36
+ case 'quiet':
37
+ return 'quiet';
38
+ case 'stalled':
39
+ case 'hung':
40
+ return 'stalled';
41
+ default:
42
+ return 'uncertain';
43
+ }
44
+ }
45
+ const TRACE_TRUNCATION_MARKER = '…[truncated]';
46
+ /** Strip control/format characters and cap at maxUtf8Bytes (marker included). */
47
+ export function boundTraceText(text, maxUtf8Bytes) {
48
+ const clean = String(text).replace(/[\u0000-\u001F\u007F-\u009F\u2028\u2029]/g, '');
49
+ if (maxUtf8Bytes <= 0)
50
+ return '';
51
+ if (Buffer.byteLength(clean, 'utf8') <= maxUtf8Bytes)
52
+ return clean;
53
+ const markerBytes = Buffer.byteLength(TRACE_TRUNCATION_MARKER, 'utf8');
54
+ if (maxUtf8Bytes < markerBytes) {
55
+ // The budget cannot hold even the marker: return the longest
56
+ // character-boundary prefix of the marker that fits, so the output
57
+ // never exceeds the budget.
58
+ let bytes = 0;
59
+ let end = 0;
60
+ for (const char of TRACE_TRUNCATION_MARKER) {
61
+ const size = Buffer.byteLength(char, 'utf8');
62
+ if (bytes + size > maxUtf8Bytes)
63
+ break;
64
+ bytes += size;
65
+ end += char.length;
66
+ }
67
+ return TRACE_TRUNCATION_MARKER.slice(0, end);
68
+ }
69
+ let bytes = 0;
70
+ let end = 0;
71
+ for (const char of clean) {
72
+ const size = Buffer.byteLength(char, 'utf8');
73
+ if (bytes + size > maxUtf8Bytes - markerBytes)
74
+ break;
75
+ bytes += size;
76
+ end += char.length;
77
+ }
78
+ return clean.slice(0, end) + TRACE_TRUNCATION_MARKER;
79
+ }
80
+ /**
81
+ * Allowlisted reason label for the shared trace. Unlisted codes (including
82
+ * any free text carrying secret material) project as UNCLASSIFIED#digest:
83
+ * stable and bounded, never verbatim.
84
+ */
85
+ export function healthReasonLabel(reasonCode) {
86
+ if (HEALTH_KNOWN_REASON_CODES.includes(reasonCode))
87
+ return reasonCode;
88
+ const digest = createHash('sha256').update(reasonCode, 'utf8').digest('hex').slice(0, 12);
89
+ return `UNCLASSIFIED#${digest}`;
90
+ }
91
+ /**
92
+ * Digest-only lifecycle reference for the effect ledger. Only enum, counter,
93
+ * digest and timestamp fields enter the digest; no payload content crosses.
94
+ */
95
+ export function healthLifecycleDigest(health) {
96
+ assertValidWorkerPayload('workerHealthSummary', health);
97
+ const lifecycle = {
98
+ version: health.version,
99
+ state: health.state,
100
+ coverage: health.coverage,
101
+ handshake: health.handshake,
102
+ termination: health.termination,
103
+ confidence: health.confidence,
104
+ observerGeneration: health.observerGeneration,
105
+ transitionCount: health.transitionCount,
106
+ restartCount: health.restartCount,
107
+ lastObservedAt: health.lastObservedAt,
108
+ lastActivityAt: health.lastActivityAt,
109
+ lastProgressAt: health.lastProgressAt,
110
+ };
111
+ const digest = `sha256:${createHash('sha256').update(stableJson(lifecycle), 'utf8').digest('hex')}`;
112
+ return { ref: `artifact:${digest}`, digest };
113
+ }
114
+ /**
115
+ * Closed observed-state vocabulary for the previous-state channel. Free
116
+ * text (including leaked bearer material) never interpolates into the
117
+ * shared trace.
118
+ */
119
+ const HEALTH_OBSERVED_STATES = new Set([
120
+ 'launching', 'started', 'active', 'quiet', 'stalled', 'hung',
121
+ 'crashed', 'failed-to-start', 'completed', 'failed', 'cancelled',
122
+ ]);
123
+ /**
124
+ * Parse an ISO timestamp into epoch milliseconds for chronological
125
+ * comparison. Comparison must never fall back to string ordering: distinct
126
+ * representations of the same instant (e.g. '.000Z' vs 'Z') must compare
127
+ * equal, not greater/lesser. Unparseable values reject as having no
128
+ * timestamp evidence (null), never throw.
129
+ */
130
+ function parseProgressTimestampMs(value) {
131
+ if (value === null)
132
+ return null;
133
+ const ms = Date.parse(value);
134
+ return Number.isFinite(ms) ? ms : null;
135
+ }
136
+ /**
137
+ * Fresh-progress gate: credit requires genuinely new progress evidence.
138
+ * The health shape carries no progress sequence/key, so freshness is a
139
+ * NONDECREASING credit watermark: `previousWatermark` is not merely "the
140
+ * last observation" but the highest-ever CREDITED lastProgressAt, compared
141
+ * by parsed epoch milliseconds (never raw strings). Out-of-order or
142
+ * replayed snapshots (older than, or equal to, the watermark) earn no
143
+ * credit and never move the watermark backward; heartbeats (which advance
144
+ * activity/observed timestamps while the watermark is frozen) never
145
+ * re-credit historical progress. Unparseable timestamps earn no credit.
146
+ * Callers thread the watermark forward with `advanceProgressWatermark`.
147
+ */
148
+ function isFreshProgressEvidence(health, previousWatermark) {
149
+ const currentMs = parseProgressTimestampMs(health.lastProgressAt);
150
+ if (currentMs === null)
151
+ return false;
152
+ if (previousWatermark === null || previousWatermark.lastProgressAt === null)
153
+ return true;
154
+ const watermarkMs = parseProgressTimestampMs(previousWatermark.lastProgressAt);
155
+ if (watermarkMs === null)
156
+ return false;
157
+ return currentMs > watermarkMs;
158
+ }
159
+ /**
160
+ * Pure watermark reducer (no store): callers thread the returned value into
161
+ * the next observation's `previousHealth`/watermark argument instead of
162
+ * re-passing the raw last observation. The watermark advances ONLY when the
163
+ * current observation was actually credited; a non-credited observation
164
+ * (replay, heartbeat, or an out-of-order/older snapshot) leaves the
165
+ * watermark exactly where it was. This is what makes real A -> B -> A -> B
166
+ * delivery sequences credit true,true,false,false instead of re-crediting
167
+ * B the second time it arrives: a naive caller that re-threads "the last
168
+ * observation" (rather than this reducer's output) regresses to that bug.
169
+ */
170
+ export function advanceProgressWatermark(previousWatermark, credited, health) {
171
+ if (credited)
172
+ return { lastProgressAt: health.lastProgressAt };
173
+ return previousWatermark ?? { lastProgressAt: null };
174
+ }
175
+ /**
176
+ * Project one health observation into the shared trace. The activity summary
177
+ * is built only from enum/counter/generation fields plus the allowlisted
178
+ * reason label; completionCredit is always false because Mission/Flow
179
+ * completion gates stay authoritative. previousState must be a closed
180
+ * observed state (or null); anything else is rejected, never interpolated.
181
+ * progressCredit requires fresh progress evidence relative to
182
+ * previousHealth, which callers must maintain as the nondecreasing credit
183
+ * watermark (see advanceProgressWatermark) rather than the raw last
184
+ * observation (null for a first observation / no watermark yet).
185
+ */
186
+ export function projectHealthTrace(health, previousState = null, previousHealth = null) {
187
+ assertValidWorkerPayload('workerHealthSummary', health);
188
+ if (previousState !== null && !HEALTH_OBSERVED_STATES.has(previousState)) {
189
+ throw new WorkerContractError('OG_CONTRACT_INVALID', 'Invalid previous health state.');
190
+ }
191
+ const kind = classifyHealthTrace(health);
192
+ const summary = `health.${health.state}:${health.termination}`
193
+ + ` gen=${health.observerGeneration ?? 'none'} transitions=${health.transitionCount}`
194
+ + ` prev=${previousState ?? 'none'} reason=${healthReasonLabel(health.reasonCode)}`;
195
+ return {
196
+ kind,
197
+ progressCredit: kind === 'productive' && isFreshProgressEvidence(health, previousHealth),
198
+ completionCredit: false,
199
+ activitySummary: boundTraceText(summary, HEALTH_ACTIVITY_SUMMARY_MAX_UTF8_BYTES),
200
+ effectRef: healthLifecycleDigest(health),
201
+ };
202
+ }
203
+ /**
204
+ * Derive stop proof from owner-observed supervision evidence (ADR-OG-10).
205
+ * Only a terminal state with owner-confirmed termination is stop proof;
206
+ * terminal-unconfirmed and uncertain observations stay uncertain, and the
207
+ * absence of evidence is none. There is no free-form proof parameter:
208
+ * callers pass the health summary (or null) and the proof is derived.
209
+ * A late final after cancel-disconnect therefore never releases while
210
+ * termination is uncertain (ADR-OG-13: late results stay unaccepted
211
+ * evidence).
212
+ */
213
+ export function stopProofFromHealth(health) {
214
+ if (health === null)
215
+ return 'none';
216
+ assertValidWorkerPayload('workerHealthSummary', health);
217
+ if (health.termination === 'confirmed') {
218
+ return HEALTH_TERMINAL_LIKE.has(health.state) ? 'confirmed' : 'uncertain';
219
+ }
220
+ if (HEALTH_TERMINAL_LIKE.has(health.state) || health.termination === 'uncertain')
221
+ return 'uncertain';
222
+ return 'none';
223
+ }
224
+ /**
225
+ * The generation the stop-proof evidence actually belongs to, bridging a
226
+ * missing observerGeneration to 0 (the bootstrap generation) for fencing —
227
+ * mirroring the same bridge decideLeaseMapping already applies to the
228
+ * caller-supplied leaseGeneration/observedGeneration fields. This is bound
229
+ * to the health summary's OWN observerGeneration field, which the caller
230
+ * cannot set independently of the evidence: a caller cannot claim generation
231
+ * 3 while attaching generation-2 evidence and have it treated as current.
232
+ */
233
+ function stopProofGeneration(health) {
234
+ if (health === null)
235
+ return null;
236
+ return health.observerGeneration ?? 0;
237
+ }
238
+ /**
239
+ * Pure renewal/reconcile/release verdict over a mocked scheduler boundary.
240
+ * Fencing binds every action to the current generation: a stale observation
241
+ * denies renew, cancel, settle and accept alike. Expiry or cancel-disconnect
242
+ * without derived confirmed stop proof retains the uncertain reservation
243
+ * and releases nothing. Confirmed stop proof settles exactly once — and only
244
+ * when that proof's OWN generation (not merely the caller-asserted
245
+ * observedGeneration) matches the current leaseGeneration; a confirmed
246
+ * proof carried over from a stale generation never releases.
247
+ */
248
+ export function decideLeaseMapping(input) {
249
+ let record;
250
+ try {
251
+ if (!input || typeof input !== 'object' || Array.isArray(input))
252
+ throw new Error('Expected object');
253
+ record = input;
254
+ }
255
+ catch {
256
+ throw new WorkerContractError('OG_CONTRACT_INVALID', 'Invalid lease mapping input.');
257
+ }
258
+ const leaseStates = ['admitted', 'expired', 'cancelled-disconnected'];
259
+ if (typeof record.leaseState !== 'string' || !leaseStates.includes(record.leaseState)
260
+ || !Number.isSafeInteger(record.leaseGeneration) || record.leaseGeneration < 0
261
+ || !Number.isSafeInteger(record.observedGeneration) || record.observedGeneration < 0
262
+ || typeof record.settled !== 'boolean'
263
+ || !('health' in record)
264
+ || (record.health !== null && !validateWorkerPayload('workerHealthSummary', record.health).valid)) {
265
+ throw new WorkerContractError('OG_CONTRACT_INVALID', 'Invalid lease mapping input.');
266
+ }
267
+ const mapping = input;
268
+ if (mapping.observedGeneration !== mapping.leaseGeneration)
269
+ return 'deny-stale';
270
+ if (mapping.settled)
271
+ return 'deny-already-settled';
272
+ const stopProof = stopProofFromHealth(mapping.health);
273
+ const proofIsCurrent = stopProofGeneration(mapping.health) === mapping.leaseGeneration;
274
+ if (stopProof === 'confirmed' && proofIsCurrent)
275
+ return 'release-once';
276
+ if (stopProof === 'uncertain' || (stopProof === 'confirmed' && !proofIsCurrent) || mapping.leaseState !== 'admitted') {
277
+ return 'retain-uncertain';
278
+ }
279
+ return 'renew';
280
+ }
281
+ //# sourceMappingURL=health-trace-codecs.js.map
@@ -0,0 +1,43 @@
1
+ /** Bounded activity-log transition summary budget (UTF-8 bytes, marker included). */
2
+ export const HEALTH_ACTIVITY_SUMMARY_MAX_UTF8_BYTES = 1024;
3
+ /**
4
+ * Closed observer reason-code allowlist for activity summaries. Mirrors the
5
+ * `reasons` set in tools/ralph-external/lib/worker-health.mjs: B-OG-50 owns
6
+ * the observer set, B-OG-54 owns this mapping allowlist; extend them together.
7
+ * Unlisted codes project as UNCLASSIFIED#digest so free text (and any secret
8
+ * material it carries) never crosses into the shared trace.
9
+ */
10
+ export const HEALTH_KNOWN_REASON_CODES = [
11
+ 'OK',
12
+ 'AWAITING_HANDSHAKE',
13
+ 'PROCESS_ONLY',
14
+ 'ACCEPTED',
15
+ 'PROGRESS',
16
+ 'QUIET',
17
+ 'NO_PROGRESS',
18
+ 'STARTUP_DEADLINE',
19
+ 'HARD_DEADLINE',
20
+ 'STEP_LIMIT',
21
+ 'AUTH',
22
+ 'RATE_LIMIT',
23
+ 'NETWORK',
24
+ 'MODEL',
25
+ 'TRUST',
26
+ 'STDIN_WAIT',
27
+ 'SANDBOX',
28
+ 'NOT_FOUND',
29
+ 'RESOURCE',
30
+ 'PROVIDER_ERROR',
31
+ 'CRASH',
32
+ 'EXIT_ERROR',
33
+ 'FINAL_INVALID',
34
+ 'CANCELLED',
35
+ 'DISCONNECTED',
36
+ 'OBSERVER_GAP',
37
+ 'CAPTURE_WRITE',
38
+ 'OUTPUT_LIMIT',
39
+ 'MALFORMED_FRAME',
40
+ 'SOURCE_GAP',
41
+ 'IDENTITY_CHANGED',
42
+ ];
43
+ //# sourceMappingURL=health-trace-types.js.map
@@ -1,3 +1,4 @@
1
+ export * from './worker-review-types.js';
1
2
  export const MISSION_API_VERSION = 'mission.aiwg.io/v1';
2
3
  // Worker dispatch payloads merged from 05 §5.1 into schemas/mission-v1.schema.json.
3
4
  // The schema file is the executable contract; these types mirror its shapes.
@@ -59,48 +60,7 @@ export const WORKER_DEFINITION_PRODUCERS = {
59
60
  workerHealthSummary: 'shared-component',
60
61
  workerEffectiveHealth: 'shared-component',
61
62
  };
62
- /** Bounded activity-log transition summary budget (UTF-8 bytes, marker included). */
63
- export const HEALTH_ACTIVITY_SUMMARY_MAX_UTF8_BYTES = 1024;
64
- /**
65
- * Closed observer reason-code allowlist for activity summaries. Mirrors the
66
- * `reasons` set in tools/ralph-external/lib/worker-health.mjs: B-OG-50 owns
67
- * the observer set, B-OG-54 owns this mapping allowlist; extend them together.
68
- * Unlisted codes project as UNCLASSIFIED#digest so free text (and any secret
69
- * material it carries) never crosses into the shared trace.
70
- */
71
- export const HEALTH_KNOWN_REASON_CODES = [
72
- 'OK',
73
- 'AWAITING_HANDSHAKE',
74
- 'PROCESS_ONLY',
75
- 'ACCEPTED',
76
- 'PROGRESS',
77
- 'QUIET',
78
- 'NO_PROGRESS',
79
- 'STARTUP_DEADLINE',
80
- 'HARD_DEADLINE',
81
- 'STEP_LIMIT',
82
- 'AUTH',
83
- 'RATE_LIMIT',
84
- 'NETWORK',
85
- 'MODEL',
86
- 'TRUST',
87
- 'STDIN_WAIT',
88
- 'SANDBOX',
89
- 'NOT_FOUND',
90
- 'RESOURCE',
91
- 'PROVIDER_ERROR',
92
- 'CRASH',
93
- 'EXIT_ERROR',
94
- 'FINAL_INVALID',
95
- 'CANCELLED',
96
- 'DISCONNECTED',
97
- 'OBSERVER_GAP',
98
- 'CAPTURE_WRITE',
99
- 'OUTPUT_LIMIT',
100
- 'MALFORMED_FRAME',
101
- 'SOURCE_GAP',
102
- 'IDENTITY_CHANGED',
103
- ];
63
+ export * from './health-trace-types.js';
104
64
  export class WorkerContractError extends Error {
105
65
  code;
106
66
  constructor(code, message) {
@@ -0,0 +1,10 @@
1
+ // Stable JSON representation shared by Mission artifact and health digests.
2
+ export function stableJson(value) {
3
+ if (Array.isArray(value))
4
+ return `[${value.map(stableJson).join(',')}]`;
5
+ if (value && typeof value === 'object') {
6
+ return `{${Object.keys(value).sort().map(key => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(',')}}`;
7
+ }
8
+ return JSON.stringify(value) ?? 'null';
9
+ }
10
+ //# sourceMappingURL=worker-contract-json.js.map
@@ -0,0 +1,100 @@
1
+ // Acceptance checks over conductor briefs, trusted head/lineage and verifier records.
2
+ import { workerReviewLineageDiagnostics } from '../models/worker-review-lineage.js';
3
+ import { WorkerContractError, } from './types.js';
4
+ function fail(message) { throw new WorkerContractError('OG_CONTRACT_INVALID', message); }
5
+ /** Additional relational checks after JSON Schema shape validation. */
6
+ export function workerReviewEvidenceErrors(verification) {
7
+ const errors = [];
8
+ const rounds = verification.reviewRounds;
9
+ if (rounds?.some((review, index) => review.round !== index + 1)) {
10
+ errors.push('Review rounds must be contiguous and start at one');
11
+ }
12
+ const ledger = verification.findingsLedger ?? [];
13
+ if (new Set(ledger.map(finding => finding.id)).size !== ledger.length)
14
+ errors.push('Finding IDs must be unique');
15
+ if (rounds && ledger.some(finding => finding.round > rounds.length))
16
+ errors.push('Finding round has no review record');
17
+ if (verification.roundsToClean !== undefined && (verification.status !== 'accepted'
18
+ || verification.roundsToClean !== rounds?.length))
19
+ errors.push('roundsToClean must count all review rounds on acceptance');
20
+ if (verification.status === 'accepted') {
21
+ if (ledger.some(finding => finding.class === 'A' && finding.status === 'open'
22
+ && ['blocker', 'major'].includes(finding.severity)))
23
+ errors.push('Open class A blocker or major findings prevent acceptance');
24
+ if (rounds?.length && (verification.headSha !== rounds.at(-1).headSha
25
+ || verification.roundsToClean !== rounds.length || !verification.findingsLedger)) {
26
+ errors.push('Accepted review needs the final headSha, findingsLedger and roundsToClean');
27
+ }
28
+ }
29
+ const commands = verification.oracleResults?.map(result => result.command) ?? [];
30
+ if (new Set(commands).size !== commands.length)
31
+ errors.push('Oracle result commands must be unique');
32
+ return errors;
33
+ }
34
+ /** Derive telemetry, then validate it through the normal verifier payload codec. */
35
+ export function prepareWorkerAcceptance(brief, execution, supplied, context) {
36
+ const verification = structuredClone(supplied);
37
+ if (verification.status !== 'accepted')
38
+ return verification;
39
+ if (brief.designReviewRequired && !brief.designReviewArtifact) {
40
+ fail('Acceptance requires a design-review artifact in the implementation brief');
41
+ }
42
+ const oracle = brief.verificationOracle;
43
+ if (oracle) {
44
+ const results = verification.oracleResults ?? [];
45
+ if (new Set(oracle.map(check => check.command)).size !== oracle.length
46
+ || results.length !== oracle.length || oracle.some(check => {
47
+ const matches = results.filter(result => result.command === check.command);
48
+ return matches.length !== 1 || matches[0].exitCode !== check.expectedExitCode;
49
+ }))
50
+ fail('Acceptance requires exact verificationOracle command outcomes');
51
+ }
52
+ const required = brief.intent.verification.independentReview === 'required'
53
+ || brief.intent.verification.differentFamily === 'require';
54
+ const rounds = verification.reviewRounds ?? [];
55
+ if (!required && !rounds.length)
56
+ return verification;
57
+ const final = rounds.at(-1);
58
+ if (!final)
59
+ fail('Acceptance requires a final cross-model review record');
60
+ if (!context.headSha || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(context.headSha)
61
+ || final.headSha !== context.headSha || verification.headSha !== undefined && verification.headSha !== context.headSha) {
62
+ fail('Final review is missing the current PR head SHA or is bound to a stale head');
63
+ }
64
+ const writer = context.reviewLineage?.writer;
65
+ const observations = context.reviewLineage?.reviewers;
66
+ const reviewers = Array.isArray(observations)
67
+ ? observations.filter(observation => observation?.recordId === final.reviewerObservationRef) : [];
68
+ if (reviewers.length !== 1)
69
+ fail('Final review is missing unambiguous reviewer lineage');
70
+ const diagnostics = workerReviewLineageDiagnostics({ reviewOf: writer, reviewer: reviewers[0], requireDifferentFamily: true });
71
+ if (diagnostics.length)
72
+ fail(diagnostics.map(diagnostic => `${diagnostic.pointer}: ${diagnostic.message}`).join('; '));
73
+ if (writer.cliId !== brief.selected.cli || writer.family !== execution.observedFamily) {
74
+ fail('Review source lineage differs from the observed writer');
75
+ }
76
+ if (verification.roundsToClean !== undefined && verification.roundsToClean !== rounds.length) {
77
+ fail('roundsToClean differs from the retained review rounds');
78
+ }
79
+ verification.headSha = context.headSha;
80
+ verification.roundsToClean = rounds.length;
81
+ return verification;
82
+ }
83
+ /** Successors keep earlier rounds and findings; resolving a finding retains its ID. */
84
+ export function assertWorkerReviewSuccessor(prior, next) {
85
+ for (const [index, round] of (prior.reviewRounds ?? []).entries()) {
86
+ const successor = next.reviewRounds?.[index];
87
+ if (!successor || successor.round !== round.round || successor.headSha !== round.headSha
88
+ || successor.reviewerObservationRef !== round.reviewerObservationRef
89
+ || successor.artifact.ref !== round.artifact.ref || successor.artifact.digest !== round.artifact.digest) {
90
+ fail('Worker successor drops or rewrites a review round');
91
+ }
92
+ }
93
+ for (const finding of prior.findingsLedger ?? []) {
94
+ const successor = next.findingsLedger?.find(candidate => candidate.id === finding.id);
95
+ if (!successor || successor.class !== finding.class || successor.severity !== finding.severity || successor.round !== finding.round) {
96
+ fail('Worker successor drops or reclassifies a retained finding');
97
+ }
98
+ }
99
+ }
100
+ //# sourceMappingURL=worker-review-gate.js.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=worker-review-types.js.map