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.
- package/LICENSE +21 -0
- package/README.md +318 -2
- package/index.js +1836 -0
- package/package.json +70 -4
- package/skills/codebase-design/SKILL.md +114 -0
- package/skills/design-contract/SKILL.md +27 -0
- package/skills/design-critique/SKILL.md +19 -0
- package/skills/design-system/SKILL.md +17 -0
- package/skills/design-system/references/image-analysis.md +14 -0
- package/skills/design-system/references/tokens.md +32 -0
- package/skills/design-system/references/ui-rules.md +14 -0
- package/skills/diagnosing-bugs/SKILL.md +138 -0
- package/skills/frontend-design/SKILL.md +36 -0
- package/skills/handoff/SKILL.md +16 -0
- package/skills/improve-codebase-architecture/SKILL.md +71 -0
- package/skills/linter-negative/SKILL.md +74 -0
- package/skills/no-slop-ui/SKILL.md +26 -0
- package/skills/prototype/SKILL.md +26 -0
- package/skills/registry.json +44087 -0
- package/skills/tastemaker/SKILL.md +8 -0
- package/skills/tastemaker/references/tastemaker.md +14 -0
- package/skills/tdd/SKILL.md +38 -0
- package/skills/visual-review/SKILL.md +103 -0
- package/skills/visual-review/references/review-gates.md +42 -0
- package/skills/visual-review/references/surface-archetypes.md +37 -0
- package/skills/wizard/SKILL.md +44 -0
- package/skills/writing-for-agents/SKILL.md +81 -0
- package/src/cognition/cognition-store.js +251 -0
- package/src/cognition/cognitive-state.js +126 -0
- package/src/cognition/hypothesis-manager.js +113 -0
- package/src/cognition/observation-engine.js +112 -0
- package/src/cognition/observation-provenance.js +268 -0
- package/src/context/active-context-boundary.js +68 -0
- package/src/context/assembly.js +494 -0
- package/src/context/assessment-rules.js +55 -0
- package/src/context/capsule-staleness.js +267 -0
- package/src/context/capsules.js +2 -0
- package/src/context/compare-array.js +98 -0
- package/src/context/compare-object.js +89 -0
- package/src/context/compare-values.js +42 -0
- package/src/context/context-assembly-contract.js +71 -0
- package/src/context/context-budget-manager.js +232 -0
- package/src/context/context-builder.js +49 -0
- package/src/context/context-cache.js +55 -0
- package/src/context/context-capture.js +243 -0
- package/src/context/context-classification.js +199 -0
- package/src/context/context-compaction.js +179 -0
- package/src/context/context-decision-audit.js +267 -0
- package/src/context/context-demotion.js +173 -0
- package/src/context/context-dictionary.js +106 -0
- package/src/context/context-drift-detection.js +225 -0
- package/src/context/context-evaluation.js +374 -0
- package/src/context/context-event-ingress.js +100 -0
- package/src/context/context-event-normalization.js +224 -0
- package/src/context/context-feedback-loop.js +216 -0
- package/src/context/context-freshness.js +21 -0
- package/src/context/context-graph-builder.js +71 -0
- package/src/context/context-graph.js +451 -0
- package/src/context/context-history-relevance.js +321 -0
- package/src/context/context-interception.js +145 -0
- package/src/context/context-invalidation-events.js +154 -0
- package/src/context/context-lifecycle.js +170 -0
- package/src/context/context-loop-prevention.js +347 -0
- package/src/context/context-manager.js +340 -0
- package/src/context/context-memory-layers.js +265 -0
- package/src/context/context-minimality.js +166 -0
- package/src/context/context-normalization.js +130 -0
- package/src/context/context-optimization-metrics.js +434 -0
- package/src/context/context-output-policy.js +135 -0
- package/src/context/context-pack-authority.js +157 -0
- package/src/context/context-promotion.js +172 -0
- package/src/context/context-query-contract.js +101 -0
- package/src/context/context-ranking.js +164 -0
- package/src/context/context-rate-distortion.js +261 -0
- package/src/context/context-recovery.js +209 -0
- package/src/context/context-retrieval-integration.js +60 -0
- package/src/context/context-retrieval.js +93 -0
- package/src/context/context-router.js +457 -0
- package/src/context/context-routing-evaluation.js +331 -0
- package/src/context/context-routing-observability.js +296 -0
- package/src/context/context-routing-shadow.js +237 -0
- package/src/context/context-scope.js +181 -0
- package/src/context/context-snapshot.js +226 -0
- package/src/context/context-source-registry.js +95 -0
- package/src/context/context-sufficiency-gate.js +120 -0
- package/src/context/context-sufficiency-oracle.js +167 -0
- package/src/context/context-telemetry.js +176 -0
- package/src/context/context.js +633 -0
- package/src/context/dependency-closure.js +246 -0
- package/src/context/page-fault-tracker.js +275 -0
- package/src/context/prompt-engine.js +74 -0
- package/src/context/runtime-context-graph.js +260 -0
- package/src/context/selection.js +2 -0
- package/src/context/session.js +2 -0
- package/src/context/sufficiency-contract.js +300 -0
- package/src/context/tokenizer.js +17 -0
- package/src/context-benchmark-router.mjs +125 -0
- package/src/evidence/evidence-engine.js +161 -0
- package/src/evidence/evidence-factory.js +3 -0
- package/src/evidence/evidence-freshness.js +463 -0
- package/src/evidence/evidence-gap.js +76 -0
- package/src/evidence/evidence-lineage.js +525 -0
- package/src/evidence/evidence.js +136 -0
- package/src/evidence/freshness-validation.js +187 -0
- package/src/evidence/lineage.js +4 -0
- package/src/execution/action-evaluation.js +179 -0
- package/src/execution/circuit-breaker.js +70 -0
- package/src/execution/claim-interception.js +63 -0
- package/src/execution/execution-engine.js +258 -0
- package/src/execution/execution-intent.js +69 -0
- package/src/execution/execution-state.js +89 -0
- package/src/execution/formatting.js +60 -0
- package/src/execution/l1-sanitize.js +16 -0
- package/src/execution/l2-resource-guards.js +19 -0
- package/src/execution/l3-invariant-checker.js +5 -0
- package/src/execution/l4-containment.js +6 -0
- package/src/execution/runtime-guards.js +106 -0
- package/src/execution/strategy-identity.js +162 -0
- package/src/execution/tool-interceptor.js +7 -0
- package/src/execution/tool-result-classifier.js +63 -0
- package/src/integration/canary-deploy.js +143 -0
- package/src/integration/collector.js +59 -0
- package/src/integration/error-recovery.js +6 -0
- package/src/integration/event-bus.js +66 -0
- package/src/integration/health-checker.js +57 -0
- package/src/integration/logger.js +39 -0
- package/src/integration/message-handler.js +611 -0
- package/src/integration/opencode-db.js +153 -0
- package/src/integration/orchestration.js +419 -0
- package/src/integration/part-provenance.js +65 -0
- package/src/integration/rate-limiter.js +55 -0
- package/src/integration/release-gate.js +336 -0
- package/src/integration/rollback-manager.js +164 -0
- package/src/integration/router-adapter.js +269 -0
- package/src/integration/telemetry.js +45 -0
- package/src/integration/update-checker.js +10 -0
- package/src/persistence/event-sourcing.js +75 -0
- package/src/persistence/memory.js +501 -0
- package/src/persistence/migration-runner.js +16 -0
- package/src/persistence/persistence-manager.js +39 -0
- package/src/persistence/transaction-log.js +157 -0
- package/src/policy/action-capabilities.js +116 -0
- package/src/policy/action-policy.js +4 -0
- package/src/policy/assessment-engine.js +104 -0
- package/src/policy/assumption-tracking.js +66 -0
- package/src/policy/budget-enforcer.js +7 -0
- package/src/policy/config-validator.js +102 -0
- package/src/policy/execution-assessment.js +142 -0
- package/src/policy/failure-classification.js +5 -0
- package/src/policy/feature-flags.js +65 -0
- package/src/policy/governance-enforcement.js +118 -0
- package/src/policy/locale.js +15 -0
- package/src/policy/multi-signer.js +151 -0
- package/src/policy/policy-state-machine.js +97 -0
- package/src/policy/ponytail-constraint.js +38 -0
- package/src/policy/project-inspector.js +93 -0
- package/src/policy/query-builder.js +14 -0
- package/src/policy/request-classifier.js +79 -0
- package/src/policy/risk-engine.js +266 -0
- package/src/policy/scope-enforcement.js +140 -0
- package/src/policy/secret-scanner.js +68 -0
- package/src/policy/skill-routing.js +104 -0
- package/src/policy/strategy-capabilities.js +211 -0
- package/src/policy/strategy.js +6 -0
- package/src/policy/uncertainty.js +117 -0
- package/src/shared/container.js +44 -0
- package/src/shared/decision-types.js +24 -0
- package/src/shared/simple-test.js +160 -0
- package/src/shared/wam-events.js +52 -0
- package/src/skills/engine.js +1705 -0
- package/src/skills/skill-registry.js +19 -0
- package/src/skills/skill-routing.js +340 -0
- package/src/state/lifecycle-manager.js +113 -0
- package/src/state/operational-state.js +198 -0
- package/src/state/projections.js +47 -0
- package/src/state/replay-engine.js +5 -0
- package/src/state/requirement-ownership.js +20 -0
- package/src/state/requirement-state.js +152 -0
- package/src/state/run-event-identity.js +172 -0
- package/src/state/state-engine.js +19 -0
- package/src/state/state-machine.js +119 -0
- package/src/state/state-schema.js +318 -0
- package/src/state/state-store.js +335 -0
- package/src/state/state-transition.js +84 -0
- package/src/state/task-dependencies.js +287 -0
- package/src/state/task-execution.js +217 -0
- package/src/state/task-lifecycle.js +59 -0
- package/src/state/task-runs.js +361 -0
- package/src/state/task-state-file.js +86 -0
- package/src/state/task-state.js +41 -0
- package/src/state/task-status.js +54 -0
- package/src/state/task-store.js +48 -0
- package/src/state/wam-state.js +148 -0
- package/src/verification/completion-gate-simple.js +8 -0
- package/src/verification/completion-gate.js +183 -0
- package/src/verification/contract.js +9 -0
- package/src/verification/contradiction-resolution.js +255 -0
- package/src/verification/diagnostic-engine.js +659 -0
- package/src/verification/false-completion-prevention.js +100 -0
- package/src/verification/hook-validator.js +6 -0
- package/src/verification/requirements.js +6 -0
- package/src/verification/verification-context.js +162 -0
- package/src/verification/verification-lifecycle.js +66 -0
- package/src/verification/verification-model.js +89 -0
- package/src/verification/verification-persistence.js +129 -0
- package/src/verification/verification-policy.js +127 -0
- 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
|
+
}
|