opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.8

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 (207) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +318 -2
  3. package/index.js +1836 -0
  4. package/package.json +70 -4
  5. package/skills/codebase-design/SKILL.md +114 -0
  6. package/skills/design-contract/SKILL.md +27 -0
  7. package/skills/design-critique/SKILL.md +19 -0
  8. package/skills/design-system/SKILL.md +17 -0
  9. package/skills/design-system/references/image-analysis.md +14 -0
  10. package/skills/design-system/references/tokens.md +32 -0
  11. package/skills/design-system/references/ui-rules.md +14 -0
  12. package/skills/diagnosing-bugs/SKILL.md +138 -0
  13. package/skills/frontend-design/SKILL.md +36 -0
  14. package/skills/handoff/SKILL.md +16 -0
  15. package/skills/improve-codebase-architecture/SKILL.md +71 -0
  16. package/skills/linter-negative/SKILL.md +74 -0
  17. package/skills/no-slop-ui/SKILL.md +26 -0
  18. package/skills/prototype/SKILL.md +26 -0
  19. package/skills/registry.json +44087 -0
  20. package/skills/tastemaker/SKILL.md +8 -0
  21. package/skills/tastemaker/references/tastemaker.md +14 -0
  22. package/skills/tdd/SKILL.md +38 -0
  23. package/skills/visual-review/SKILL.md +103 -0
  24. package/skills/visual-review/references/review-gates.md +42 -0
  25. package/skills/visual-review/references/surface-archetypes.md +37 -0
  26. package/skills/wizard/SKILL.md +44 -0
  27. package/skills/writing-for-agents/SKILL.md +81 -0
  28. package/src/cognition/cognition-store.js +251 -0
  29. package/src/cognition/cognitive-state.js +126 -0
  30. package/src/cognition/hypothesis-manager.js +113 -0
  31. package/src/cognition/observation-engine.js +112 -0
  32. package/src/cognition/observation-provenance.js +268 -0
  33. package/src/context/active-context-boundary.js +68 -0
  34. package/src/context/assembly.js +494 -0
  35. package/src/context/assessment-rules.js +55 -0
  36. package/src/context/capsule-staleness.js +267 -0
  37. package/src/context/capsules.js +2 -0
  38. package/src/context/compare-array.js +98 -0
  39. package/src/context/compare-object.js +89 -0
  40. package/src/context/compare-values.js +42 -0
  41. package/src/context/context-assembly-contract.js +71 -0
  42. package/src/context/context-budget-manager.js +232 -0
  43. package/src/context/context-builder.js +49 -0
  44. package/src/context/context-cache.js +55 -0
  45. package/src/context/context-capture.js +243 -0
  46. package/src/context/context-classification.js +199 -0
  47. package/src/context/context-compaction.js +179 -0
  48. package/src/context/context-decision-audit.js +267 -0
  49. package/src/context/context-demotion.js +173 -0
  50. package/src/context/context-dictionary.js +106 -0
  51. package/src/context/context-drift-detection.js +225 -0
  52. package/src/context/context-evaluation.js +374 -0
  53. package/src/context/context-event-ingress.js +100 -0
  54. package/src/context/context-event-normalization.js +224 -0
  55. package/src/context/context-feedback-loop.js +216 -0
  56. package/src/context/context-freshness.js +21 -0
  57. package/src/context/context-graph-builder.js +71 -0
  58. package/src/context/context-graph.js +451 -0
  59. package/src/context/context-history-relevance.js +321 -0
  60. package/src/context/context-interception.js +145 -0
  61. package/src/context/context-invalidation-events.js +154 -0
  62. package/src/context/context-lifecycle.js +170 -0
  63. package/src/context/context-loop-prevention.js +347 -0
  64. package/src/context/context-manager.js +340 -0
  65. package/src/context/context-memory-layers.js +265 -0
  66. package/src/context/context-minimality.js +166 -0
  67. package/src/context/context-normalization.js +130 -0
  68. package/src/context/context-optimization-metrics.js +434 -0
  69. package/src/context/context-output-policy.js +135 -0
  70. package/src/context/context-pack-authority.js +157 -0
  71. package/src/context/context-promotion.js +172 -0
  72. package/src/context/context-query-contract.js +101 -0
  73. package/src/context/context-ranking.js +164 -0
  74. package/src/context/context-rate-distortion.js +261 -0
  75. package/src/context/context-recovery.js +209 -0
  76. package/src/context/context-retrieval-integration.js +60 -0
  77. package/src/context/context-retrieval.js +93 -0
  78. package/src/context/context-router.js +457 -0
  79. package/src/context/context-routing-evaluation.js +331 -0
  80. package/src/context/context-routing-observability.js +296 -0
  81. package/src/context/context-routing-shadow.js +237 -0
  82. package/src/context/context-scope.js +181 -0
  83. package/src/context/context-snapshot.js +226 -0
  84. package/src/context/context-source-registry.js +95 -0
  85. package/src/context/context-sufficiency-gate.js +120 -0
  86. package/src/context/context-sufficiency-oracle.js +167 -0
  87. package/src/context/context-telemetry.js +176 -0
  88. package/src/context/context.js +633 -0
  89. package/src/context/dependency-closure.js +246 -0
  90. package/src/context/page-fault-tracker.js +275 -0
  91. package/src/context/prompt-engine.js +74 -0
  92. package/src/context/runtime-context-graph.js +260 -0
  93. package/src/context/selection.js +2 -0
  94. package/src/context/session.js +2 -0
  95. package/src/context/sufficiency-contract.js +300 -0
  96. package/src/context/tokenizer.js +17 -0
  97. package/src/context-benchmark-router.mjs +125 -0
  98. package/src/evidence/evidence-engine.js +161 -0
  99. package/src/evidence/evidence-factory.js +3 -0
  100. package/src/evidence/evidence-freshness.js +463 -0
  101. package/src/evidence/evidence-gap.js +76 -0
  102. package/src/evidence/evidence-lineage.js +525 -0
  103. package/src/evidence/evidence.js +136 -0
  104. package/src/evidence/freshness-validation.js +187 -0
  105. package/src/evidence/lineage.js +4 -0
  106. package/src/execution/action-evaluation.js +179 -0
  107. package/src/execution/circuit-breaker.js +70 -0
  108. package/src/execution/claim-interception.js +63 -0
  109. package/src/execution/execution-engine.js +258 -0
  110. package/src/execution/execution-intent.js +69 -0
  111. package/src/execution/execution-state.js +89 -0
  112. package/src/execution/formatting.js +60 -0
  113. package/src/execution/l1-sanitize.js +16 -0
  114. package/src/execution/l2-resource-guards.js +19 -0
  115. package/src/execution/l3-invariant-checker.js +5 -0
  116. package/src/execution/l4-containment.js +6 -0
  117. package/src/execution/runtime-guards.js +106 -0
  118. package/src/execution/strategy-identity.js +162 -0
  119. package/src/execution/tool-interceptor.js +7 -0
  120. package/src/execution/tool-result-classifier.js +63 -0
  121. package/src/integration/canary-deploy.js +143 -0
  122. package/src/integration/collector.js +59 -0
  123. package/src/integration/error-recovery.js +6 -0
  124. package/src/integration/event-bus.js +66 -0
  125. package/src/integration/health-checker.js +57 -0
  126. package/src/integration/logger.js +39 -0
  127. package/src/integration/message-handler.js +611 -0
  128. package/src/integration/opencode-db.js +153 -0
  129. package/src/integration/orchestration.js +419 -0
  130. package/src/integration/part-provenance.js +65 -0
  131. package/src/integration/rate-limiter.js +55 -0
  132. package/src/integration/release-gate.js +336 -0
  133. package/src/integration/rollback-manager.js +164 -0
  134. package/src/integration/router-adapter.js +269 -0
  135. package/src/integration/telemetry.js +45 -0
  136. package/src/integration/update-checker.js +10 -0
  137. package/src/persistence/event-sourcing.js +75 -0
  138. package/src/persistence/memory.js +501 -0
  139. package/src/persistence/migration-runner.js +16 -0
  140. package/src/persistence/persistence-manager.js +39 -0
  141. package/src/persistence/transaction-log.js +157 -0
  142. package/src/policy/action-capabilities.js +116 -0
  143. package/src/policy/action-policy.js +4 -0
  144. package/src/policy/assessment-engine.js +104 -0
  145. package/src/policy/assumption-tracking.js +66 -0
  146. package/src/policy/budget-enforcer.js +7 -0
  147. package/src/policy/config-validator.js +102 -0
  148. package/src/policy/execution-assessment.js +142 -0
  149. package/src/policy/failure-classification.js +5 -0
  150. package/src/policy/feature-flags.js +65 -0
  151. package/src/policy/governance-enforcement.js +118 -0
  152. package/src/policy/locale.js +15 -0
  153. package/src/policy/multi-signer.js +151 -0
  154. package/src/policy/policy-state-machine.js +97 -0
  155. package/src/policy/ponytail-constraint.js +38 -0
  156. package/src/policy/project-inspector.js +93 -0
  157. package/src/policy/query-builder.js +14 -0
  158. package/src/policy/request-classifier.js +79 -0
  159. package/src/policy/risk-engine.js +266 -0
  160. package/src/policy/scope-enforcement.js +140 -0
  161. package/src/policy/secret-scanner.js +68 -0
  162. package/src/policy/skill-routing.js +104 -0
  163. package/src/policy/strategy-capabilities.js +211 -0
  164. package/src/policy/strategy.js +6 -0
  165. package/src/policy/uncertainty.js +117 -0
  166. package/src/shared/container.js +44 -0
  167. package/src/shared/decision-types.js +24 -0
  168. package/src/shared/simple-test.js +160 -0
  169. package/src/shared/wam-events.js +52 -0
  170. package/src/skills/engine.js +1705 -0
  171. package/src/skills/skill-registry.js +19 -0
  172. package/src/skills/skill-routing.js +340 -0
  173. package/src/state/lifecycle-manager.js +113 -0
  174. package/src/state/operational-state.js +198 -0
  175. package/src/state/projections.js +47 -0
  176. package/src/state/replay-engine.js +5 -0
  177. package/src/state/requirement-ownership.js +20 -0
  178. package/src/state/requirement-state.js +152 -0
  179. package/src/state/run-event-identity.js +172 -0
  180. package/src/state/state-engine.js +19 -0
  181. package/src/state/state-machine.js +119 -0
  182. package/src/state/state-schema.js +318 -0
  183. package/src/state/state-store.js +335 -0
  184. package/src/state/state-transition.js +84 -0
  185. package/src/state/task-dependencies.js +287 -0
  186. package/src/state/task-execution.js +217 -0
  187. package/src/state/task-lifecycle.js +59 -0
  188. package/src/state/task-runs.js +361 -0
  189. package/src/state/task-state-file.js +86 -0
  190. package/src/state/task-state.js +41 -0
  191. package/src/state/task-status.js +54 -0
  192. package/src/state/task-store.js +48 -0
  193. package/src/state/wam-state.js +148 -0
  194. package/src/verification/completion-gate-simple.js +8 -0
  195. package/src/verification/completion-gate.js +183 -0
  196. package/src/verification/contract.js +9 -0
  197. package/src/verification/contradiction-resolution.js +255 -0
  198. package/src/verification/diagnostic-engine.js +659 -0
  199. package/src/verification/false-completion-prevention.js +100 -0
  200. package/src/verification/hook-validator.js +6 -0
  201. package/src/verification/requirements.js +6 -0
  202. package/src/verification/verification-context.js +162 -0
  203. package/src/verification/verification-lifecycle.js +66 -0
  204. package/src/verification/verification-model.js +89 -0
  205. package/src/verification/verification-persistence.js +129 -0
  206. package/src/verification/verification-policy.js +127 -0
  207. package/src/verification/verification.js +415 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * WAM Context Routing — Evaluation Suite
3
+ *
4
+ * Compares the existing WAM Context Pack against the shadow routing strategy.
5
+ * Prioritizes task correctness and verification over token reduction.
6
+ */
7
+
8
+ import { ContextGraph } from "./context-graph.js";
9
+ import { getCurrentContextPack, getProposedContext, hasRequiredContext, computeOverlap } from "./context-routing-shadow.js";
10
+ import { persistTaskState } from "../skills/engine.js";
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+
14
+ // -- Types --
15
+
16
+ /**
17
+ * @typedef {Object} EvaluationResult
18
+ * @property {string} scenario
19
+ * @property {string} strategy
20
+ * @property {boolean} taskSuccess
21
+ * @property {boolean} verificationSuccess
22
+ * @property {number} contextTokens
23
+ * @property {number} contextRecall
24
+ * @property {number} contextPrecision
25
+ * @property {number} dependencyCoverage
26
+ * @property {number} evidenceCoverage
27
+ * @property {string[]} missingDependencies
28
+ */
29
+
30
+ // -- Token estimation --
31
+
32
+ function estimateTokens(text = "") {
33
+ return Math.ceil((text || "").length / 4);
34
+ }
35
+
36
+ function nodeTokens(node) {
37
+ return estimateTokens(node.content) + 20;
38
+ }
39
+
40
+ // -- Metrics --
41
+
42
+ function computeMetrics(result, expected, graph) {
43
+ const resultIds = new Set(result.nodeIds);
44
+ const expectedIds = new Set(expected.requiredNodes);
45
+
46
+ // Context Recall
47
+ let recall = 0;
48
+ if (expectedIds.size > 0) {
49
+ for (const id of expectedIds) {
50
+ if (resultIds.has(id)) recall++;
51
+ }
52
+ recall /= expectedIds.size;
53
+ }
54
+
55
+ // Context Precision
56
+ let precision = 0;
57
+ if (resultIds.size > 0) {
58
+ for (const id of resultIds) {
59
+ if (expectedIds.has(id)) precision++;
60
+ }
61
+ precision /= resultIds.size;
62
+ }
63
+
64
+ // Dependency Coverage
65
+ let depCoverage = 1;
66
+ if (expected.missingDeps && expected.missingDeps.length > 0) {
67
+ const foundMissing = result.gaps ? result.gaps.filter((g) => g.type === "missing_dependency") : [];
68
+ depCoverage = foundMissing.length > 0 ? 0 : 1;
69
+ }
70
+
71
+ // Evidence Coverage
72
+ const evidenceNodes = graph ? graph.getNodes("evidence") : [];
73
+ const requiredEvidence = evidenceNodes.filter((e) =>
74
+ [...expectedIds].some((id) => {
75
+ if (!graph) return false;
76
+ const edges = graph.getEdgesFrom(id);
77
+ return edges.some((edge) => edge.to === e.id);
78
+ })
79
+ );
80
+
81
+ let evidenceCoverage = 1;
82
+ if (requiredEvidence.length > 0) {
83
+ const foundEvidence = requiredEvidence.filter((e) => resultIds.has(e.id));
84
+ evidenceCoverage = foundEvidence.length / requiredEvidence.length;
85
+ }
86
+
87
+ // Task Success
88
+ const taskSuccess = expectedIds.size > 0 &&
89
+ [...expectedIds].every((id) => resultIds.has(id));
90
+
91
+ // Verification Success
92
+ const contradictions = result.gaps ? result.gaps.filter((g) => g.type === "contradiction") : [];
93
+ const verificationSuccess = contradictions.length === 0 && evidenceCoverage > 0.5;
94
+
95
+ return {
96
+ taskSuccess,
97
+ verificationSuccess,
98
+ contextTokens: result.tokenEstimate || 0,
99
+ contextRecall: recall,
100
+ contextPrecision: precision,
101
+ dependencyCoverage: depCoverage,
102
+ evidenceCoverage,
103
+ missingDependencies: result.gaps ? result.gaps.filter((g) => g.type === "missing_dependency").map((g) => g.description) : [],
104
+ };
105
+ }
106
+
107
+ // -- Scenarios --
108
+
109
+ function buildNoiseScenario() {
110
+ const graph = new ContextGraph();
111
+ const taskId = "task-noise";
112
+
113
+ graph.addNode({ id: taskId, type: "task", content: "Build authentication system", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
114
+ graph.addNode({ id: "auth-output", type: "output", content: "JWT auth module with bcrypt", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
115
+ graph.addNode({ id: "auth-evidence", type: "evidence", content: "Auth test passed with 100% coverage", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
116
+ graph.addNode({ id: "auth-decision", type: "decision", content: "Use JWT tokens with 24h expiry", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
117
+ graph.addNode({ id: "auth-constraint", type: "constraint", content: "Must use bcrypt for password hashing", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
118
+
119
+ graph.addEdge({ from: taskId, to: "auth-output", type: "produces" });
120
+ graph.addEdge({ from: taskId, to: "auth-decision", type: "produces" });
121
+ graph.addEdge({ from: "auth-evidence", to: "auth-output", type: "supports" });
122
+ graph.addEdge({ from: taskId, to: "auth-constraint", type: "requires_decision" });
123
+
124
+ for (let i = 0; i < 95; i++) {
125
+ graph.addNode({ id: `irr-${i}`, type: "observation", content: `Irrelevant observation ${i}: The weather is nice today`, verified: false, createdAt: Date.now(), updatedAt: Date.now() });
126
+ }
127
+
128
+ return {
129
+ name: "noise",
130
+ graph,
131
+ taskId,
132
+ expected: {
133
+ requiredNodes: [taskId, "auth-output", "auth-evidence", "auth-decision", "auth-constraint"],
134
+ missingDeps: [],
135
+ },
136
+ };
137
+ }
138
+
139
+ function buildHiddenDependencyScenario() {
140
+ const graph = new ContextGraph();
141
+ const taskId = "task-deploy";
142
+
143
+ graph.addNode({ id: "db-setup", type: "task", content: "Configure PostgreSQL connection pool", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
144
+ graph.addNode({ id: "db-output", type: "output", content: "PostgreSQL connection string and pool config", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
145
+ graph.addEdge({ from: "db-setup", to: "db-output", type: "produces" });
146
+
147
+ graph.addNode({ id: "api-task", type: "task", content: "Create REST endpoints for user management", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
148
+ graph.addNode({ id: "api-output", type: "output", content: "Express routes with authentication middleware", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
149
+ graph.addEdge({ from: "api-task", to: "api-output", type: "produces" });
150
+ graph.addEdge({ from: "api-task", to: "db-output", type: "requires_output" });
151
+
152
+ graph.addNode({ id: taskId, type: "task", content: "Deploy application to Kubernetes cluster", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
153
+ graph.addEdge({ from: taskId, to: "api-output", type: "requires_output" });
154
+
155
+ return {
156
+ name: "hidden-dependency",
157
+ graph,
158
+ taskId,
159
+ expected: {
160
+ requiredNodes: [taskId, "api-output", "db-output"],
161
+ missingDeps: [],
162
+ },
163
+ };
164
+ }
165
+
166
+ function buildContradictoryEvidenceScenario() {
167
+ const graph = new ContextGraph();
168
+ const taskId = "task-test";
169
+
170
+ graph.addNode({ id: taskId, type: "task", content: "Verify feature works", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
171
+ graph.addNode({ id: "feature-output", type: "output", content: "Feature implementation", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
172
+ graph.addNode({ id: "evidence-pass", type: "evidence", content: "Unit test passed locally", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
173
+ graph.addNode({ id: "evidence-fail", type: "evidence", content: "Integration test failed on CI", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
174
+
175
+ graph.addEdge({ from: taskId, to: "feature-output", type: "requires_output" });
176
+ graph.addEdge({ from: taskId, to: "evidence-pass", type: "requires_evidence" });
177
+ graph.addEdge({ from: taskId, to: "evidence-fail", type: "requires_evidence" });
178
+ graph.addEdge({ from: "evidence-pass", to: "evidence-fail", type: "contradicts" });
179
+
180
+ return {
181
+ name: "contradictory-evidence",
182
+ graph,
183
+ taskId,
184
+ expected: {
185
+ requiredNodes: [taskId, "feature-output", "evidence-pass", "evidence-fail"],
186
+ missingDeps: [],
187
+ },
188
+ };
189
+ }
190
+
191
+ function buildStaleEvidenceScenario() {
192
+ const graph = new ContextGraph();
193
+ const taskId = "task-data";
194
+
195
+ graph.addNode({ id: "old-decision", type: "decision", content: "Use MongoDB for data storage", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
196
+ graph.addNode({ id: "new-evidence", type: "evidence", content: "MongoDB performance degrades at scale", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
197
+ graph.addNode({ id: taskId, type: "task", content: "Design data layer", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
198
+
199
+ graph.addEdge({ from: "new-evidence", to: "old-decision", type: "invalidates" });
200
+ graph.addEdge({ from: taskId, to: "old-decision", type: "requires_decision" });
201
+
202
+ return {
203
+ name: "stale-evidence",
204
+ graph,
205
+ taskId,
206
+ expected: {
207
+ requiredNodes: [taskId, "old-decision", "new-evidence"],
208
+ missingDeps: [],
209
+ },
210
+ };
211
+ }
212
+
213
+ function buildMissingDependencyScenario() {
214
+ const graph = new ContextGraph();
215
+ const taskId = "task-missing";
216
+
217
+ graph.addNode({ id: taskId, type: "task", content: "Build feature that depends on missing service", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
218
+
219
+ // Add edge manually to non-existent node
220
+ const edges = graph.edgesFrom.get(taskId) || [];
221
+ edges.push({ from: taskId, to: "output-missing", type: "requires_output", weight: 1.0 });
222
+ graph.edgesFrom.set(taskId, edges);
223
+ const toEdges = graph.edgesTo.get("output-missing") || [];
224
+ toEdges.push({ from: taskId, to: "output-missing", type: "requires_output", weight: 1.0 });
225
+ graph.edgesTo.set("output-missing", toEdges);
226
+
227
+ return {
228
+ name: "missing-dependency",
229
+ graph,
230
+ taskId,
231
+ expected: {
232
+ requiredNodes: [taskId],
233
+ missingDeps: ["output-missing"],
234
+ },
235
+ };
236
+ }
237
+
238
+ function buildPlaywrightChromiumScenario() {
239
+ const graph = new ContextGraph();
240
+ const taskId = "task-scrape";
241
+
242
+ graph.addNode({ id: taskId, type: "task", content: "Scrape Lider website with Playwright", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
243
+ graph.addNode({ id: "browser-output", type: "output", content: "Scraped product data from Lider", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
244
+ graph.addNode({ id: "chromium-dep", type: "artifact", content: "Chromium browser binary installed", verified: false, createdAt: Date.now(), updatedAt: Date.now() });
245
+ graph.addNode({ id: "playwright-dep", type: "artifact", content: "Playwright npm package installed", verified: true, createdAt: Date.now(), updatedAt: Date.now() });
246
+
247
+ graph.addEdge({ from: taskId, to: "browser-output", type: "produces" });
248
+ graph.addEdge({ from: taskId, to: "chromium-dep", type: "depends_on_artifact" });
249
+ graph.addEdge({ from: taskId, to: "playwright-dep", type: "depends_on_artifact" });
250
+
251
+ return {
252
+ name: "playwright-chromium",
253
+ graph,
254
+ taskId,
255
+ expected: {
256
+ requiredNodes: [taskId, "browser-output", "chromium-dep", "playwright-dep"],
257
+ missingDeps: [],
258
+ },
259
+ };
260
+ }
261
+
262
+ const SCENARIOS = [
263
+ buildNoiseScenario,
264
+ buildHiddenDependencyScenario,
265
+ buildContradictoryEvidenceScenario,
266
+ buildStaleEvidenceScenario,
267
+ buildMissingDependencyScenario,
268
+ buildPlaywrightChromiumScenario,
269
+ ];
270
+
271
+ // -- Benchmark runner --
272
+
273
+ function runScenarioWithStrategy(scenarioBuilder, strategyName, strategyFn, root) {
274
+ const { name, graph, taskId, expected } = scenarioBuilder();
275
+
276
+ // Create task state on disk
277
+ const dir = path.join(root || process.cwd(), ".wam", "tasks", taskId);
278
+ fs.mkdirSync(dir, { recursive: true });
279
+ fs.writeFileSync(
280
+ path.join(dir, "state.yaml"),
281
+ JSON.stringify({
282
+ phase: "IMPLEMENTING",
283
+ lastAction: `Test: ${name}`,
284
+ contract: { objective: `Test ${name} scenario` },
285
+ requirements: [{ id: "req-1", title: "Test", status: "pending", evidence: [] }],
286
+ executions: [],
287
+ }, null, 2)
288
+ );
289
+
290
+ const result = strategyFn(taskId, graph, root);
291
+ const metrics = computeMetrics(result, expected, graph);
292
+
293
+ return {
294
+ scenario: name,
295
+ strategy: strategyName,
296
+ ...metrics,
297
+ };
298
+ }
299
+
300
+ export function runEvaluation(root) {
301
+ const strategies = [
302
+ { name: "current-context-pack", fn: (taskId, graph, root) => getCurrentContextPack(taskId, root) },
303
+ { name: "shadow-routing", fn: (taskId, graph, root) => getProposedContext(taskId, graph, root) },
304
+ ];
305
+
306
+ const results = [];
307
+ for (const strategy of strategies) {
308
+ for (const scenarioBuilder of SCENARIOS) {
309
+ const result = runScenarioWithStrategy(scenarioBuilder, strategy.name, strategy.fn, root);
310
+ results.push(result);
311
+ }
312
+ }
313
+
314
+ // Summary
315
+ const summary = {};
316
+ for (const r of results) {
317
+ if (!summary[r.strategy]) {
318
+ summary[r.strategy] = { totalTokens: 0, successes: 0, total: 0, avgRecall: 0 };
319
+ }
320
+ summary[r.strategy].totalTokens += r.contextTokens;
321
+ summary[r.strategy].total++;
322
+ if (r.taskSuccess) summary[r.strategy].successes++;
323
+ summary[r.strategy].avgRecall += r.contextRecall;
324
+ }
325
+
326
+ for (const key of Object.keys(summary)) {
327
+ summary[key].avgRecall /= summary[key].total;
328
+ }
329
+
330
+ return { results, summary };
331
+ }
@@ -0,0 +1,296 @@
1
+ /**
2
+ * WAM Context Routing — Observability
3
+ *
4
+ * Exposes why WAM included, excluded, deferred or rejected context during
5
+ * context routing. Every routing decision must be explainable.
6
+ *
7
+ * Decision types:
8
+ * - included
9
+ * - excluded
10
+ * - dependency-required
11
+ * - evidence-required
12
+ * - superseded
13
+ * - stale
14
+ * - invalidated
15
+ * - budget-exceeded
16
+ * - low-relevance
17
+ * - missing
18
+ */
19
+
20
+ import { ContextGraph } from "./context-graph.js";
21
+ import { getHistoricalCandidates } from "./context-history-relevance.js";
22
+ import { getDependencies, detectContextGaps } from "../state/task-dependencies.js";
23
+
24
+ /**
25
+ * @typedef {"included"|"excluded"|"dependency-required"|"evidence-required"|"superseded"|"stale"|"invalidated"|"budget-exceeded"|"low-relevance"|"missing"} RoutingDecisionType
26
+ */
27
+
28
+ /**
29
+ * @typedef {Object} RoutingDecision
30
+ * @property {string} itemId
31
+ * @property {RoutingDecisionType} type
32
+ * @property {string} reason
33
+ * @property {string} [supersededBy]
34
+ * @property {string} [requiredBy]
35
+ */
36
+
37
+ export function explainRoutingDecision(item, context, graph) {
38
+ const decisions = [];
39
+
40
+ // Check if item was included
41
+ if (context.included?.includes(item.id)) {
42
+ const reason = explainInclusion(item, context, graph);
43
+ decisions.push({
44
+ itemId: item.id,
45
+ type: "included",
46
+ reason,
47
+ });
48
+ return decisions;
49
+ }
50
+
51
+ // Check various exclusion reasons
52
+ if (item.status === "superseded") {
53
+ decisions.push({
54
+ itemId: item.id,
55
+ type: "superseded",
56
+ reason: `Superseded by ${item.supersededBy || "newer version"}`,
57
+ supersededBy: item.supersededBy,
58
+ });
59
+ } else if (item.status === "stale") {
60
+ decisions.push({
61
+ itemId: item.id,
62
+ type: "stale",
63
+ reason: "No longer relevant to current task",
64
+ });
65
+ } else if (item.status === "invalidated") {
66
+ decisions.push({
67
+ itemId: item.id,
68
+ type: "invalidated",
69
+ reason: item.invalidationReason || "Evidence or output was invalidated",
70
+ });
71
+ } else if (context.excluded?.includes(item.id)) {
72
+ const reason = explainExclusion(item, context, graph);
73
+ decisions.push({
74
+ itemId: item.id,
75
+ type: reason.type,
76
+ reason: reason.description,
77
+ });
78
+ } else if (
79
+ context.missing &&
80
+ context.missing.some(
81
+ (g) =>
82
+ g.requiredBy === item.id ||
83
+ (typeof g.description === "string" && g.description.includes(item.id)) ||
84
+ (g.type === "missing_dependency" && g.requiredBy)
85
+ )
86
+ ) {
87
+ const gap = context.missing.find(
88
+ (g) =>
89
+ g.requiredBy === item.id ||
90
+ (typeof g.description === "string" && g.description.includes(item.id)) ||
91
+ (g.type === "missing_dependency" && g.requiredBy)
92
+ );
93
+ decisions.push({
94
+ itemId: item.id,
95
+ type: "missing",
96
+ reason: gap.description || "Required but missing",
97
+ requiredBy: gap.requiredBy,
98
+ });
99
+ } else if (!context.included?.includes(item.id) && context.included) {
100
+ decisions.push({
101
+ itemId: item.id,
102
+ type: "low-relevance",
103
+ reason: `Relevance score ${item.relevanceScore || 0} below threshold`,
104
+ });
105
+ }
106
+
107
+ return decisions;
108
+ }
109
+
110
+ function explainInclusion(item, context, graph) {
111
+ if (item.dependencyOf) {
112
+ return `Required by ${item.dependencyOf}`;
113
+ }
114
+ if (item.supports) {
115
+ return `Supports evidence ${item.supports}`;
116
+ }
117
+ if (item.type === "decision") {
118
+ return "Active decision affecting current task";
119
+ }
120
+ if (item.type === "requirement" && item.status === "unresolved") {
121
+ return "Unresolved requirement from current task";
122
+ }
123
+ if (item.relevanceScore > 0.7) {
124
+ return `High relevance score: ${item.relevanceScore.toFixed(2)}`;
125
+ }
126
+ return "Included based on routing rules";
127
+ }
128
+
129
+ function explainExclusion(item, context, graph) {
130
+ // Check if required but missing
131
+ const gaps = context.missing || [];
132
+ const itemId = item.id;
133
+ const missingGap = gaps.find(
134
+ (g) =>
135
+ g.requiredBy === itemId ||
136
+ (typeof g.description === "string" && g.description.includes(itemId)) ||
137
+ (g.type === "missing_dependency" && g.requiredBy)
138
+ );
139
+ if (missingGap) {
140
+ return { type: "missing", description: missingGap.description };
141
+ }
142
+
143
+ // Check budget
144
+ if (context.tokenBudget && context.totalTokens > context.tokenBudget) {
145
+ return { type: "budget-exceeded", description: "Token budget exceeded" };
146
+ }
147
+
148
+ // Check if superseded
149
+ if (item.status === "superseded") {
150
+ return { type: "superseded", description: `Superseded by ${item.supersededBy || "newer version"}` };
151
+ }
152
+
153
+ // Check if stale
154
+ if (item.status === "stale") {
155
+ return { type: "stale", description: "No longer relevant" };
156
+ }
157
+
158
+ // Check if invalidated
159
+ if (item.status === "invalidated") {
160
+ return { type: "invalidated", description: "Content was invalidated" };
161
+ }
162
+
163
+ // Default: low relevance
164
+ return { type: "low-relevance", description: `Relevance score below threshold` };
165
+ }
166
+
167
+ export function explainContextRouting(taskId, graph, context, options = {}) {
168
+ const decisions = [];
169
+ const { includeExcluded = true, includeIncluded = true } = options;
170
+
171
+ // Process included items
172
+ if (includeIncluded && context.included) {
173
+ for (const itemId of context.included) {
174
+ const item = findItem(itemId, context, graph);
175
+ if (item) {
176
+ decisions.push(...explainRoutingDecision(item, context, graph));
177
+ }
178
+ }
179
+ }
180
+
181
+ // Process excluded items
182
+ if (includeExcluded && context.excluded) {
183
+ for (const itemId of context.excluded) {
184
+ const item = findItem(itemId, context, graph);
185
+ if (item) {
186
+ decisions.push(...explainRoutingDecision(item, context, graph));
187
+ }
188
+ }
189
+ }
190
+
191
+ // Process missing items
192
+ if (context.missing) {
193
+ for (const gap of context.missing) {
194
+ decisions.push({
195
+ itemId: gap.type,
196
+ type: "missing",
197
+ reason: gap.description,
198
+ requiredBy: gap.requiredBy,
199
+ });
200
+ }
201
+ }
202
+
203
+ return decisions;
204
+ }
205
+
206
+ function findItem(itemId, context, graph) {
207
+ // Check included items
208
+ if (context.includedItems) {
209
+ const item = context.includedItems.find((i) => i.id === itemId);
210
+ if (item) return item;
211
+ }
212
+
213
+ // Check graph
214
+ if (graph) {
215
+ const node = graph.getNode(itemId);
216
+ if (node) return node;
217
+ }
218
+
219
+ // Return minimal object
220
+ return { id: itemId };
221
+ }
222
+
223
+ export function formatRoutingReport(decisions) {
224
+ const byType = {
225
+ included: [],
226
+ excluded: [],
227
+ missing: [],
228
+ superseded: [],
229
+ stale: [],
230
+ invalidated: [],
231
+ budgetExceeded: [],
232
+ lowRelevance: [],
233
+ };
234
+
235
+ for (const d of decisions) {
236
+ if (d.type === "included") byType.included.push(d);
237
+ else if (d.type === "missing") byType.missing.push(d);
238
+ else if (d.type === "superseded") byType.superseded.push(d);
239
+ else if (d.type === "stale") byType.stale.push(d);
240
+ else if (d.type === "invalidated") byType.invalidated.push(d);
241
+ else if (d.type === "budget-exceeded") byType.budgetExceeded.push(d);
242
+ else byType.excluded.push(d);
243
+ }
244
+
245
+ const lines = ["# Routing Decision Report", ""];
246
+
247
+ if (byType.included.length > 0) {
248
+ lines.push("## Included");
249
+ for (const d of byType.included) {
250
+ lines.push(`- ${d.itemId}: ${d.reason}`);
251
+ }
252
+ lines.push("");
253
+ }
254
+
255
+ if (byType.missing.length > 0) {
256
+ lines.push("## Missing");
257
+ for (const d of byType.missing) {
258
+ lines.push(`- ${d.itemId}: ${d.reason}`);
259
+ }
260
+ lines.push("");
261
+ }
262
+
263
+ if (byType.excluded.length > 0) {
264
+ lines.push("## Excluded");
265
+ for (const d of byType.excluded) {
266
+ lines.push(`- ${d.itemId}: ${d.reason}`);
267
+ }
268
+ lines.push("");
269
+ }
270
+
271
+ if (byType.superseded.length > 0) {
272
+ lines.push("## Superseded");
273
+ for (const d of byType.superseded) {
274
+ lines.push(`- ${d.itemId}: ${d.reason}`);
275
+ }
276
+ lines.push("");
277
+ }
278
+
279
+ if (byType.stale.length > 0) {
280
+ lines.push("## Stale");
281
+ for (const d of byType.stale) {
282
+ lines.push(`- ${d.itemId}: ${d.reason}`);
283
+ }
284
+ lines.push("");
285
+ }
286
+
287
+ if (byType.invalidated.length > 0) {
288
+ lines.push("## Invalidated");
289
+ for (const d of byType.invalidated) {
290
+ lines.push(`- ${d.itemId}: ${d.reason}`);
291
+ }
292
+ lines.push("");
293
+ }
294
+
295
+ return lines.join("\n");
296
+ }