@aiwg/cli 2026.10.4 → 2026.10.6

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 (220) hide show
  1. package/agentic/code/addons/composition-engine/lib/runtime.mjs +1401 -0
  2. package/agentic/code/addons/composition-engine/lib/validator.mjs +513 -0
  3. package/agentic/code/addons/composition-engine/schemas/flow-graph.schema.json +372 -0
  4. package/agentic/code/providers/model-capabilities.v1.json +74 -50
  5. package/bin/aiwg.mjs +3 -1
  6. package/dist/src/artifacts/browser-export.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +8 -1
  8. package/dist/src/artifacts/cli.js +72 -9
  9. package/dist/src/artifacts/fortemi-core-query-adapter.js +2 -0
  10. package/dist/src/artifacts/fortemi-core-sync.js +26 -8
  11. package/dist/src/artifacts/graph-query.js +18 -8
  12. package/dist/src/artifacts/index-adapters.js +339 -0
  13. package/dist/src/artifacts/index-builder.js +146 -16
  14. package/dist/src/artifacts/index-files.js +15 -4
  15. package/dist/src/artifacts/index-preparation.js +91 -26
  16. package/dist/src/artifacts/index-reader.js +44 -0
  17. package/dist/src/artifacts/index-refresh.js +130 -56
  18. package/dist/src/artifacts/index-status.js +20 -5
  19. package/dist/src/artifacts/index-worker.mjs +9 -7
  20. package/dist/src/artifacts/memory-maintenance.js +503 -0
  21. package/dist/src/artifacts/prebuilt-relations.js +3 -1
  22. package/dist/src/artifacts/query-engine.js +263 -108
  23. package/dist/src/artifacts/relations-builder.js +2 -2
  24. package/dist/src/artifacts/source-graph.js +2 -2
  25. package/dist/src/artifacts/types.js +9 -1
  26. package/dist/src/cli/handlers/decision.js +19 -1
  27. package/dist/src/cli/handlers/guided-setup.js +470 -0
  28. package/dist/src/cli/handlers/help.js +2 -2
  29. package/dist/src/cli/handlers/housekeeper.js +1110 -0
  30. package/dist/src/cli/handlers/index.js +4 -2
  31. package/dist/src/cli/handlers/init.js +41 -6
  32. package/dist/src/cli/handlers/local-executor.js +5 -3
  33. package/dist/src/cli/handlers/mc.js +509 -53
  34. package/dist/src/cli/handlers/models.js +32 -2
  35. package/dist/src/cli/handlers/ralph-launcher.js +76 -4
  36. package/dist/src/cli/handlers/rules.js +1 -1
  37. package/dist/src/cli/handlers/script-runner.js +73 -2
  38. package/dist/src/cli/handlers/serve.js +5 -3
  39. package/dist/src/cli/handlers/sessions.js +40 -6
  40. package/dist/src/cli/handlers/setup.js +428 -1
  41. package/dist/src/cli/handlers/skill-lint.js +1 -1
  42. package/dist/src/cli/handlers/subcommands.js +118 -8
  43. package/dist/src/cli/handlers/use.js +279 -93
  44. package/dist/src/cli/handlers/workspace.js +3 -1
  45. package/dist/src/cli/prompt-utils.js +8 -0
  46. package/dist/src/cli/router.js +16 -1
  47. package/dist/src/config/aiwg-config.js +42 -0
  48. package/dist/src/config/cli.js +7 -3
  49. package/dist/src/config/decision-config.js +14 -1
  50. package/dist/src/config/native-sandbox-warnings.js +25 -0
  51. package/dist/src/contributors/validation.js +6 -5
  52. package/dist/src/decision/admission.js +7 -3
  53. package/dist/src/decision/benchmark.js +469 -0
  54. package/dist/src/decision/ledger.js +22 -3
  55. package/dist/src/decision/pricing.js +147 -0
  56. package/dist/src/decision/qualification/gate-evidence.js +18 -15
  57. package/dist/src/decision/qualification/load-staged.js +539 -0
  58. package/dist/src/decision/qualification/load.js +109 -33
  59. package/dist/src/decision/qualification/runner.js +5 -2
  60. package/dist/src/decision/review/service.js +6 -4
  61. package/dist/src/decision/review/validate.js +23 -2
  62. package/dist/src/decision/savings.js +8 -3
  63. package/dist/src/discovery/index.js +492 -0
  64. package/dist/src/discovery/interview.js +233 -0
  65. package/dist/src/discovery/plan-apply.js +571 -0
  66. package/dist/src/discovery/plan-store.js +171 -0
  67. package/dist/src/discovery/plan-types.js +2 -0
  68. package/dist/src/discovery/plan-validation.js +139 -0
  69. package/dist/src/discovery/types.js +2 -0
  70. package/dist/src/extensions/commands/definitions.js +49 -9
  71. package/dist/src/extensions/manifest.js +71 -44
  72. package/dist/src/extensions/project-local-discovery.js +1 -1
  73. package/dist/src/extensions/project-quickref.js +18 -17
  74. package/dist/src/extensions/validation.js +32 -29
  75. package/dist/src/housekeeper/daemon-config.js +148 -0
  76. package/dist/src/housekeeper/ipc.js +84 -0
  77. package/dist/src/housekeeper/lifecycle.js +148 -0
  78. package/dist/src/housekeeper/platform.js +35 -0
  79. package/dist/src/housekeeper/status.js +70 -0
  80. package/dist/src/housekeeper/units.js +114 -0
  81. package/dist/src/jobs/flow.js +22 -21
  82. package/dist/src/lint/rule-activation.js +20 -5
  83. package/dist/src/lint/runner.js +1 -1
  84. package/dist/src/mcp/cli.mjs +5 -6
  85. package/dist/src/mcp/config-file.mjs +6 -2
  86. package/dist/src/mcp/config-layers.mjs +23 -7
  87. package/dist/src/mcp/profiles.js +18 -31
  88. package/dist/src/mcp/profiles.mjs +18 -32
  89. package/dist/src/mcp/registry.js +7 -27
  90. package/dist/src/mcp/registry.mjs +7 -28
  91. package/dist/src/mcp/server.mjs +34 -1
  92. package/dist/src/mcp/tools/agentic-sandbox.mjs +8 -7
  93. package/dist/src/mcp/tools/command-run.mjs +3 -2
  94. package/dist/src/mcp/tools/decision.mjs +49 -48
  95. package/dist/src/mcp/tools/discovery.mjs +46 -21
  96. package/dist/src/mcp/tools/orchestration.mjs +22 -22
  97. package/dist/src/mcp/tools/subsystems.mjs +9 -8
  98. package/dist/src/mcp/tools/worker-invoke.mjs +17 -4
  99. package/dist/src/mcp/tools/writer-profiles.mjs +1 -1
  100. package/dist/src/memory/audit.js +719 -0
  101. package/dist/src/memory/canonical-context.js +392 -13
  102. package/dist/src/memory/canonical-maintenance.js +95 -0
  103. package/dist/src/memory/cli-result.js +22 -0
  104. package/dist/src/memory/cli.js +176 -0
  105. package/dist/src/memory/context-pack.js +57 -6
  106. package/dist/src/memory/cost.js +512 -0
  107. package/dist/src/memory/firewall-classification.d.mts +7 -0
  108. package/dist/src/memory/firewall-classification.mjs +32 -0
  109. package/dist/src/memory/index.js +8 -0
  110. package/dist/src/memory/line-maintenance.js +68 -0
  111. package/dist/src/memory/maintenance-cli.js +286 -0
  112. package/dist/src/memory/maintenance-contract.js +136 -0
  113. package/dist/src/memory/maintenance-index.js +18 -0
  114. package/dist/src/memory/maintenance-owner.js +2 -0
  115. package/dist/src/memory/maintenance-record.js +138 -0
  116. package/dist/src/memory/maintenance.js +254 -0
  117. package/dist/src/memory/provider-memory-adapter.js +154 -0
  118. package/dist/src/memory/recommend-cli.js +83 -0
  119. package/dist/src/memory/recommend.js +269 -0
  120. package/dist/src/memory/scheduled.js +81 -0
  121. package/dist/src/memory/taxonomy.js +44 -0
  122. package/dist/src/mission-protocol/codecs.js +19 -9
  123. package/dist/src/models/config-loader.js +11 -1
  124. package/dist/src/models/model-capabilities.v1.json +74 -50
  125. package/dist/src/models/provider-policy.js +125 -62
  126. package/dist/src/models/resolver.js +45 -8
  127. package/dist/src/models/worker-installation.d.mts +7 -0
  128. package/dist/src/models/worker-installation.mjs +169 -37
  129. package/dist/src/models/wrapper-route.js +1 -1
  130. package/dist/src/output-modes/registry.js +8 -7
  131. package/dist/src/plugin/metadata-validator.js +1 -1
  132. package/dist/src/providers/deploy-version.d.mts +1 -0
  133. package/dist/src/providers/deploy-version.mjs +17 -0
  134. package/dist/src/providers/provider-definitions.js +19 -16
  135. package/dist/src/providers/skill-ownership.d.mts +11 -2
  136. package/dist/src/providers/skill-ownership.mjs +35 -17
  137. package/dist/src/rules/activation.d.mts +11 -2
  138. package/dist/src/rules/activation.mjs +82 -6
  139. package/dist/src/rules/inventory.js +1 -1
  140. package/dist/src/rules/rule-frontmatter.schema.json +2 -0
  141. package/dist/src/schema/zod-validation.d.mts +6 -0
  142. package/dist/src/schema/zod-validation.mjs +50 -0
  143. package/dist/src/serve/executor-registry.js +297 -20
  144. package/dist/src/serve/executor-token.d.mts +1 -0
  145. package/dist/src/serve/executor-token.mjs +8 -0
  146. package/dist/src/serve/shared-host-scheduler.js +1102 -31
  147. package/dist/src/serve/worker-invocation.js +114 -15
  148. package/dist/src/sessions/adapters/claude.js +19 -18
  149. package/dist/src/sessions/adapters/codex.js +33 -19
  150. package/dist/src/sessions/adapters/copilot.js +14 -13
  151. package/dist/src/sessions/adapters/cursor.js +19 -18
  152. package/dist/src/sessions/adapters/deepseek-harness.js +9 -8
  153. package/dist/src/sessions/adapters/factory.js +11 -10
  154. package/dist/src/sessions/adapters/generic.js +32 -23
  155. package/dist/src/sessions/adapters/grokbot.js +35 -35
  156. package/dist/src/sessions/adapters/hermes.js +20 -19
  157. package/dist/src/sessions/adapters/muse.js +50 -49
  158. package/dist/src/sessions/adapters/omp.js +5 -4
  159. package/dist/src/sessions/adapters/openclaw.js +30 -29
  160. package/dist/src/sessions/adapters/opencode.js +26 -26
  161. package/dist/src/sessions/adapters/openhuman.js +24 -23
  162. package/dist/src/sessions/adapters/pi.js +5 -4
  163. package/dist/src/sessions/adapters/windsurf.js +8 -8
  164. package/dist/src/sessions/analytics.js +10 -9
  165. package/dist/src/sessions/batch-contracts.js +1 -1
  166. package/dist/src/sessions/batch-import.js +202 -2
  167. package/dist/src/sessions/candidates.js +7 -6
  168. package/dist/src/sessions/contracts.js +73 -54
  169. package/dist/src/sessions/coverage-preflight.js +26 -1
  170. package/dist/src/sessions/output-registration.js +11 -10
  171. package/dist/src/sessions/policy.js +18 -7
  172. package/dist/src/sessions/refresh.js +2 -3
  173. package/dist/src/sessions/repository.js +4 -2
  174. package/dist/src/sessions/workspace-discovery.js +92 -26
  175. package/dist/src/smiths/context-pipeline/aiwg-md.js +18 -1
  176. package/dist/src/smiths/context-pipeline/claude-hook.js +42 -6
  177. package/dist/src/smiths/context-pipeline/parallelism-section.js +72 -5
  178. package/dist/src/smiths/context-pipeline/workspace-context.js +106 -10
  179. package/dist/src/update/checker.d.mts +4 -0
  180. package/dist/src/update/checker.mjs +14 -0
  181. package/dist/src/writing/exemplar-selection.js +6 -6
  182. package/dist/src/writing/voice-evaluation.js +24 -23
  183. package/dist/src/writing/writer-migration.js +14 -13
  184. package/dist/src/writing/writer-profile.js +19 -18
  185. package/dist/src/writing/writing-brief.js +18 -17
  186. package/dist/src/writing/writing-receipt.js +47 -46
  187. package/package.json +5 -2
  188. package/schemas/decision/DecisionReview.schema.json +78 -1
  189. package/schemas/mission-v1.schema.json +3 -0
  190. package/tools/agents/deploy-agents.mjs +67 -39
  191. package/tools/agents/providers/antigravity.mjs +6 -1
  192. package/tools/agents/providers/base.mjs +19 -13
  193. package/tools/agents/providers/claude.mjs +1 -1
  194. package/tools/agents/providers/codex.mjs +50 -56
  195. package/tools/agents/providers/copilot.mjs +8 -2
  196. package/tools/agents/providers/cursor.mjs +1 -1
  197. package/tools/agents/providers/deepseek-harness.mjs +1 -1
  198. package/tools/agents/providers/factory.mjs +7 -1
  199. package/tools/agents/providers/grok-build.mjs +18 -2
  200. package/tools/agents/providers/grokbot.mjs +1 -1
  201. package/tools/agents/providers/hermes.mjs +7 -13
  202. package/tools/agents/providers/muse-hooks.mjs +21 -14
  203. package/tools/agents/providers/muse.mjs +7 -2
  204. package/tools/agents/providers/omp.mjs +28 -4
  205. package/tools/agents/providers/opencode.mjs +14 -5
  206. package/tools/agents/providers/openhuman.mjs +1 -1
  207. package/tools/agents/providers/pi.mjs +1 -1
  208. package/tools/agents/providers/provider-prepare-coordinator.mjs +29 -9
  209. package/tools/agents/providers/rule-delivery.mjs +263 -71
  210. package/tools/agents/providers/windsurf.mjs +7 -0
  211. package/tools/cli/workspace-status.mjs +5 -0
  212. package/tools/commands/deploy-prompts-codex.mjs +11 -8
  213. package/tools/ralph-external/lib/codex-adapter.mjs +6 -2
  214. package/tools/ralph-external/lib/muse-adapter.mjs +12 -2
  215. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  216. package/tools/ralph-external/lib/worker-isolation.mjs +65 -9
  217. package/tools/ralph-external/process-monitor.mjs +7 -4
  218. package/tools/ralph-external/session-launcher.mjs +280 -32
  219. package/tools/skills/deploy-skills-codex.mjs +11 -7
  220. package/vscode-extension/schemas/aiwg.config.v1.json +11 -1
@@ -0,0 +1,147 @@
1
+ import { emptyConfig, readAiwgConfig, writeAiwgConfig } from '../config/aiwg-config.js';
2
+ import { decisionSavingsWeights } from './savings.js';
3
+ export const JEV_PRICING_SOURCE = 'https://docs.typesafe.ai/models.md';
4
+ export const BUNDLED_JEV_PRICING = {
5
+ schema: 'aiwg-decision-pricing/v1', source: JEV_PRICING_SOURCE,
6
+ checkedAt: '2026-10-18T00:00:00.000Z', currency: 'USD', endpointOrigin: 'https://api.typesafe.ai',
7
+ models: { 'jev-1.13.0': { inputPerMillionUsd: 0.042, outputPerMillionUsd: 0 },
8
+ 'jev-1.14.0': { inputPerMillionUsd: 0.042, outputPerMillionUsd: 0 } },
9
+ };
10
+ const MAX_BYTES = 256 * 1024;
11
+ export function validTokenPrice(value) {
12
+ const p = value;
13
+ return !!p && [p.inputPerMillionUsd, p.outputPerMillionUsd, ...(p.cachedInputPerMillionUsd === undefined ? [] : [p.cachedInputPerMillionUsd])]
14
+ .every(n => typeof n === 'number' && Number.isFinite(n) && n >= 0 && n <= 1_000_000);
15
+ }
16
+ function validSnapshot(value) {
17
+ const p = value;
18
+ return !!p && p.schema === BUNDLED_JEV_PRICING.schema && p.source === JEV_PRICING_SOURCE && p.currency === 'USD'
19
+ && p.endpointOrigin === BUNDLED_JEV_PRICING.endpointOrigin && Number.isFinite(Date.parse(p.checkedAt))
20
+ && !!p.models && Object.keys(p.models).length > 0 && Object.entries(p.models).every(([id, price]) => /^jev-\d+\.\d+\.\d+$/.test(id) && validTokenPrice(price));
21
+ }
22
+ /** Parse only the official versioned-model table; never guess output billing or aliases. */
23
+ export function parseJevPricing(markdown, checkedAt = new Date().toISOString()) {
24
+ const rows = markdown.split('\n').filter(line => line.trim().startsWith('|')).map(line => line.split('|').slice(1, -1).map(cell => cell.trim()));
25
+ const ids = rows.find(row => row[0] === 'Model ID');
26
+ const prices = rows.find(row => row[0] === 'Price (per Btok / per Mtok)');
27
+ if (!ids || !prices || ids.length !== prices.length || !/Output tokens are free\./i.test(markdown))
28
+ throw new Error('Official Jev pricing format changed; keeping the prior snapshot.');
29
+ const models = {};
30
+ for (let i = 1; i < ids.length; i++) {
31
+ const id = /^`(jev-\d+\.\d+\.\d+)`$/.exec(ids[i])?.[1];
32
+ const price = /^\\?\$([\d.]+)\s*\/\s*\\?\$([\d.]+)$/.exec(prices[i]);
33
+ if (!id || !price || !Number.isFinite(Number(price[1])) || !Number.isFinite(Number(price[2])) || Math.abs(Number(price[1]) / 1000 - Number(price[2])) > 1e-10)
34
+ throw new Error('Invalid official Jev price table.');
35
+ if (models[id])
36
+ throw new Error('Duplicate Jev model in official price table.');
37
+ models[id] = { inputPerMillionUsd: Number(price[2]), outputPerMillionUsd: 0 };
38
+ }
39
+ const snapshot = { ...BUNDLED_JEV_PRICING, models, checkedAt };
40
+ if (!validSnapshot(snapshot))
41
+ throw new Error('Invalid Jev pricing snapshot.');
42
+ return snapshot;
43
+ }
44
+ /** Explicit public-document fetch only; no credentials, redirects, cache or model calls. */
45
+ async function fetchPricing(fetcher, now) {
46
+ const controller = new AbortController();
47
+ let timeout;
48
+ const operation = async () => {
49
+ const response = await fetcher(JEV_PRICING_SOURCE, {
50
+ redirect: 'error', credentials: 'omit', signal: controller.signal, headers: { Accept: 'text/markdown' },
51
+ });
52
+ if (!response.ok || !response.body)
53
+ throw new Error(`Jev pricing refresh failed (${response.status}).`);
54
+ const reader = response.body.getReader();
55
+ const chunks = [];
56
+ let size = 0;
57
+ try {
58
+ while (true) {
59
+ const chunk = await reader.read();
60
+ if (chunk.done)
61
+ break;
62
+ size += chunk.value.byteLength;
63
+ if (size > MAX_BYTES)
64
+ throw new Error('Jev pricing document exceeds limit.');
65
+ chunks.push(chunk.value);
66
+ }
67
+ }
68
+ finally {
69
+ void reader.cancel().catch(() => { });
70
+ }
71
+ return parseJevPricing(Buffer.concat(chunks).toString('utf8'), now.toISOString());
72
+ };
73
+ try {
74
+ // Bound the entire body read, including injected fetchers which ignore abort.
75
+ return await Promise.race([operation(), new Promise((_, reject) => {
76
+ timeout = setTimeout(() => { controller.abort(); reject(new Error('Jev pricing refresh timed out.')); }, 5000);
77
+ })]);
78
+ }
79
+ finally {
80
+ clearTimeout(timeout);
81
+ }
82
+ }
83
+ /** The existing project config is the only persisted pricing system. */
84
+ export async function decisionPricing(options) {
85
+ if (!options.refresh && (options.offline || options.allowSnapshot || options.dryRun || options.model)) {
86
+ throw new Error('--offline, --allow-snapshot, --dry-run and --model require --refresh');
87
+ }
88
+ const config = (await readAiwgConfig(options.cwd)) ?? emptyConfig();
89
+ const before = config.decision?.ask?.cost ?? {};
90
+ if (!options.refresh) {
91
+ const weights = decisionSavingsWeights(config.decision?.ask);
92
+ return { refreshed: false, written: false, current: { llm_per_mtok: weights.llm_per_mtok, jev_per_mtok: weights.jev_per_mtok,
93
+ ...before, source: before.source ?? 'manual', checked_at: before.checked_at ?? null, jev_model: before.jev_model ?? null }, sourceUrl: JEV_PRICING_SOURCE };
94
+ }
95
+ // Validate selection before starting any network operation.
96
+ const model = options.model ?? before.jev_model;
97
+ if (!model || !/^jev-\d+\.\d+\.\d+$/.test(model))
98
+ throw new Error('Refresh requires --model jev-X.Y.Z or a configured jev_model. Config unchanged.');
99
+ let snapshot;
100
+ let warning = null;
101
+ let source = 'typesafe-docs';
102
+ if (options.offline) {
103
+ snapshot = structuredClone(BUNDLED_JEV_PRICING);
104
+ source = 'typesafe-snapshot';
105
+ }
106
+ else {
107
+ try {
108
+ snapshot = await fetchPricing(options.fetch ?? fetch, options.now ?? new Date());
109
+ }
110
+ catch (error) {
111
+ if (!options.allowSnapshot)
112
+ throw new Error(`${error instanceof Error ? error.message : String(error)} Config unchanged; use --allow-snapshot to permit the dated bundled snapshot.`);
113
+ snapshot = structuredClone(BUNDLED_JEV_PRICING);
114
+ source = 'typesafe-snapshot';
115
+ warning = error instanceof Error ? error.message : String(error);
116
+ }
117
+ }
118
+ // Require an explicit or previously selected version rather than guessing latest.
119
+ const price = snapshot.models[model];
120
+ if (!price)
121
+ throw new Error(`No sourced rates for ${model}. Config unchanged.`);
122
+ const after = { ...before, jev_input_per_mtok: price.inputPerMillionUsd, jev_output_per_mtok: price.outputPerMillionUsd,
123
+ source, checked_at: snapshot.checkedAt.slice(0, 10), jev_model: model };
124
+ const diff = Object.entries(after).filter(([key, value]) => before[key] !== value)
125
+ .map(([key, value]) => ({ key: `decision.ask.cost.${key}`, before: before[key] ?? null, after: value }));
126
+ if (!options.dryRun) {
127
+ config.decision = { ...config.decision, ask: { ...config.decision?.ask, cost: after } };
128
+ await writeAiwgConfig(options.cwd, config);
129
+ }
130
+ return { refreshed: true, written: !options.dryRun, dryRun: !!options.dryRun, current: after, diff,
131
+ sourceUrl: snapshot.source, warning, snapshotDate: source === 'typesafe-snapshot' ? after.checked_at : null };
132
+ }
133
+ /** API-equivalent cost from complete billable usage. Missing cache billing stays unknown. */
134
+ export function tokenCost(usage, price) {
135
+ if (!validTokenPrice(price) || !usage || usage.inputTokens === null
136
+ || (price.outputPerMillionUsd > 0 && usage.outputTokens === null))
137
+ return null;
138
+ if (![usage.inputTokens, usage.outputTokens ?? 0].every(n => Number.isSafeInteger(n) && n >= 0))
139
+ return null;
140
+ const cached = price.cachedInputPerMillionUsd === undefined ? 0 : usage.cachedInputTokens;
141
+ if (cached == null || !Number.isSafeInteger(cached) || cached < 0 || cached > usage.inputTokens)
142
+ return null;
143
+ return ((usage.inputTokens - cached) * price.inputPerMillionUsd
144
+ + cached * (price.cachedInputPerMillionUsd ?? price.inputPerMillionUsd)
145
+ + (usage.outputTokens ?? 0) * price.outputPerMillionUsd) / 1_000_000;
146
+ }
147
+ //# sourceMappingURL=pricing.js.map
@@ -4,6 +4,8 @@ import { matchEvidenceDigest } from '../../gates/stats/index.js';
4
4
  import { CalibrationRegistry, calibrationArtifactDigest } from '../calibration/registry.js';
5
5
  import { validateCaseInventory } from './manifest.js';
6
6
  import { verifyQualificationSplits } from './quality.js';
7
+ import { verifyDecisionStagedLoadRecord } from './load.js';
8
+ import { validateReviewEvaluation } from '../review/validate.js';
7
9
  const range = (prefix, from, to) => Array.from({ length: to - from + 1 }, (_, index) => `${prefix}${String(from + index).padStart(2, '0')}`);
8
10
  export const DECISION_GATE_SUITES = {
9
11
  'contract-suite-complete': { gate: 'G0', caseIds: ['C36'], evidenceIds: [
@@ -124,33 +126,30 @@ function validCalibration(value, context) {
124
126
  && artifact.metrics.calibrationError <= artifact.profile.maximumCalibrationError
125
127
  && artifact.metrics.selectiveRisk <= artifact.profile.maximumSelectiveRisk;
126
128
  }
127
- function validLoadResult(value) {
129
+ function validLoadResult(value, context) {
128
130
  if (!record(value) || value.schemaVersion !== GATE_ARTIFACT_SCHEMAS['load-manifest-qualified'])
129
131
  return false;
130
- const result = value;
131
- if (!record(result.manifest) || result.manifest.schema !== 'decision-load-manifest/v1' || !record(result.manifest.bounds)
132
- || !record(result.observations) || !validDate(result.recordedAt) || result.mode !== result.manifest.mode)
132
+ // Legacy records remain readable, but only completed live v3 evidence bound to
133
+ // this exact clean source revision can qualify the performance gate.
134
+ if (context.manifest.dirty !== false || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(context.manifest.sourceCommit))
133
135
  return false;
134
- let digest;
135
136
  try {
136
- digest = loadManifestDigest(result.manifest);
137
+ return verifyDecisionStagedLoadRecord(value, context.manifest.sourceCommit);
137
138
  }
138
139
  catch {
139
140
  return false;
140
141
  }
141
- const bounds = Object.entries(result.manifest.bounds);
142
- return digest === result.manifestDigest && bounds.length > 0
143
- && Object.keys(result.observations).length === bounds.length
144
- && bounds.every(([name, limit]) => {
145
- const observed = result.observations[name];
146
- return Number.isFinite(limit) && limit >= 0 && typeof observed === 'number' && Number.isFinite(observed)
147
- && observed >= 0 && observed <= limit;
148
- });
149
142
  }
150
143
  function validReview(value, context) {
151
144
  if (!record(value) || value.schemaVersion !== GATE_ARTIFACT_SCHEMAS['review-decision-recorded'])
152
145
  return false;
153
146
  const review = value;
147
+ try {
148
+ validateReviewEvaluation(review.evaluation);
149
+ }
150
+ catch {
151
+ return false;
152
+ }
154
153
  return review.runId === context.manifest.runId && review.sourceCommit === context.manifest.sourceCommit
155
154
  && typeof review.reviewer === 'string' && review.reviewer.trim().length > 0 && review.decision === 'approve'
156
155
  && validDate(review.recordedAt) && review.outcomesDigest === qualificationOutcomesDigest(context.manifest);
@@ -160,7 +159,7 @@ export function validateGateArtifact(flag, value, context) {
160
159
  switch (flag) {
161
160
  case 'immutable-splits': return validSplitPlan(value);
162
161
  case 'calibration-qualified': return validCalibration(value, context);
163
- case 'load-manifest-qualified': return validLoadResult(value);
162
+ case 'load-manifest-qualified': return validLoadResult(value, context);
164
163
  case 'review-decision-recorded': return validReview(value, context);
165
164
  default: return false;
166
165
  }
@@ -181,6 +180,10 @@ export function deriveGateEvidence(manifest, proof = {}) {
181
180
  : ref !== undefined && ref.schemaVersion === GATE_ARTIFACT_SCHEMAS[flag];
182
181
  flags[flag] = versionOk && ref !== undefined
183
182
  && typeof ref.artifact === 'string' && ref.artifact.length > 0 && sha256Pattern.test(ref.digest);
183
+ if (flag === 'load-manifest-qualified') {
184
+ flags[flag] = flags[flag] === true && proof.loadArtifactVerified === true && manifest.dirty === false
185
+ && /^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(manifest.sourceCommit);
186
+ }
184
187
  }
185
188
  return flags;
186
189
  }