opencode-wait-a-minute 0.0.0-stage → 1.1.0-rc.10
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,457 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WAM Context Routing — Task-aware context selection.
|
|
3
|
+
*
|
|
4
|
+
* Given an active task and a Context Graph, determines the minimum
|
|
5
|
+
* sufficient context required for that task. Deterministic, no LLM.
|
|
6
|
+
*
|
|
7
|
+
* Admission classes:
|
|
8
|
+
* MANDATORY — never dropped, even if exceeding budget
|
|
9
|
+
* CONDITIONAL — dropped after OPTIONAL
|
|
10
|
+
* OPTIONAL — dropped first when budget is tight
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { EDGE_TYPES } from "./context-graph.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Admission classes for context budget policy.
|
|
17
|
+
*/
|
|
18
|
+
export const ADMISSION = {
|
|
19
|
+
MANDATORY: "MANDATORY",
|
|
20
|
+
CONDITIONAL: "CONDITIONAL",
|
|
21
|
+
OPTIONAL: "OPTIONAL",
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// -- Types --
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {"MANDATORY" | "CONDITIONAL" | "OPTIONAL"} AdmissionClass
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {Object} ResolveContextOptions
|
|
32
|
+
* @property {string} taskId - The task to resolve context for
|
|
33
|
+
* @property {number} [maxTokens=4000] - Maximum token budget
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} ContextGap
|
|
38
|
+
* @property {string} requiredBy - Node ID that requires this context
|
|
39
|
+
* @property {string} type - Type of missing context
|
|
40
|
+
* @property {string} description - What is missing
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @typedef {Object} OmittedNode
|
|
45
|
+
* @property {string} id - Node ID
|
|
46
|
+
* @property {string} reason - Why it was omitted
|
|
47
|
+
* @property {AdmissionClass} admission - Admission class
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @typedef {Object} ResolvedContext
|
|
52
|
+
* @property {import('./context-graph.js').ContextNode[]} nodes
|
|
53
|
+
* @property {import('./context-graph.js').ContextEdge[]} edges
|
|
54
|
+
* @property {ContextGap[]} missing
|
|
55
|
+
* @property {OmittedNode[]} omitted
|
|
56
|
+
* @property {boolean} complete
|
|
57
|
+
* @property {boolean} sufficient
|
|
58
|
+
* @property {"COMPLETE" | "PARTIAL" | "INSUFFICIENT" | "CONFLICTED"} status
|
|
59
|
+
* @property {number} tokenEstimate
|
|
60
|
+
* @property {boolean} budgetOverflow
|
|
61
|
+
* @property {number} mandatoryOmitted
|
|
62
|
+
* @property {{required: number, selected: number, omitted: number, missing: number, contradictions: number}} summary
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
// -- Token estimation --
|
|
66
|
+
|
|
67
|
+
function estimateTokens(text = "") {
|
|
68
|
+
return Math.ceil((text || "").length / 4);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function nodeTokens(node) {
|
|
72
|
+
return estimateTokens(node.content) + 20; // overhead for metadata
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Determine admission class for a node based on causal necessity.
|
|
77
|
+
*
|
|
78
|
+
* MANDATORY: Required to continue execution or satisfy completion.
|
|
79
|
+
* CONDITIONAL: Evidence/Decisions required for verification.
|
|
80
|
+
* OPTIONAL: Extra context.
|
|
81
|
+
*/
|
|
82
|
+
function getAdmissionClass(node, taskNode, graph) {
|
|
83
|
+
// 1. Mandatory: Task/Completion requirements or output type with MANDATORY admission
|
|
84
|
+
if (node.id === taskNode?.id || node.type === "task" || node.type === "requirement" || (node.type === "output" && node.metadata?.admission === "MANDATORY")) {
|
|
85
|
+
return ADMISSION.MANDATORY;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Mandatory: Execution dependencies
|
|
89
|
+
// If it's required for the task to proceed, it's MANDATORY
|
|
90
|
+
const edgesToTask = graph.getEdgesTo(taskNode?.id);
|
|
91
|
+
const isDirectDep = edgesToTask.some(e => e.from === node.id || e.to === node.id);
|
|
92
|
+
if (isDirectDep) return ADMISSION.MANDATORY;
|
|
93
|
+
|
|
94
|
+
// 3. Conditional: Evidence/Decisions for verification
|
|
95
|
+
if (node.type === "evidence" || node.type === "decision" || node.type === "constraint") {
|
|
96
|
+
return ADMISSION.CONDITIONAL;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// 4. Everything else is OPTIONAL
|
|
100
|
+
return ADMISSION.OPTIONAL;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// -- Relevance scoring --
|
|
104
|
+
|
|
105
|
+
function computeRelevance(node, taskNode, graph) {
|
|
106
|
+
let score = 0;
|
|
107
|
+
|
|
108
|
+
// Base: type relevance
|
|
109
|
+
const typeWeights = {
|
|
110
|
+
output: 1.0,
|
|
111
|
+
evidence: 0.9,
|
|
112
|
+
requirement: 0.85,
|
|
113
|
+
decision: 0.8,
|
|
114
|
+
constraint: 0.75,
|
|
115
|
+
artifact: 0.7,
|
|
116
|
+
task: 0.6,
|
|
117
|
+
claim: 0.5,
|
|
118
|
+
observation: 0.4,
|
|
119
|
+
subtask: 0.3,
|
|
120
|
+
};
|
|
121
|
+
score += typeWeights[node.type] || 0.1;
|
|
122
|
+
|
|
123
|
+
// Verification bonus
|
|
124
|
+
if (node.verified) score *= 1.2;
|
|
125
|
+
|
|
126
|
+
// Content similarity (simple word overlap)
|
|
127
|
+
const taskWords = new Set(
|
|
128
|
+
(taskNode?.content || "").toLowerCase().split(/\s+/).filter((w) => w.length > 3)
|
|
129
|
+
);
|
|
130
|
+
const nodeWords = new Set(
|
|
131
|
+
(node.content || "").toLowerCase().split(/\s+/).filter((w) => w.length > 3)
|
|
132
|
+
);
|
|
133
|
+
let overlap = 0;
|
|
134
|
+
for (const w of nodeWords) if (taskWords.has(w)) overlap++;
|
|
135
|
+
if (nodeWords.size > 0) score += (overlap / nodeWords.size) * 0.5;
|
|
136
|
+
|
|
137
|
+
return Math.min(score, 2.0);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Get executable requirements based on causal dependencies.
|
|
142
|
+
*/
|
|
143
|
+
export function getExecutableRequirements(graph) {
|
|
144
|
+
const reqs = graph.getNodes("requirement");
|
|
145
|
+
const executable = [];
|
|
146
|
+
|
|
147
|
+
for (const req of reqs) {
|
|
148
|
+
if (req.verified || req.status === "done") continue;
|
|
149
|
+
|
|
150
|
+
// Traverse upstream to find causal blockers
|
|
151
|
+
const deps = graph.getUpstream(req.id, 5);
|
|
152
|
+
const blockers = deps.filter(d =>
|
|
153
|
+
!d.verified &&
|
|
154
|
+
(d.type === "requirement" || d.type === "decision" || d.type === "constraint")
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
if (blockers.length === 0) {
|
|
158
|
+
executable.push({
|
|
159
|
+
requirementId: req.id,
|
|
160
|
+
action: req.content,
|
|
161
|
+
blockers: [],
|
|
162
|
+
dependencies: deps.map(d => d.id),
|
|
163
|
+
rationale: "All causal dependencies satisfied"
|
|
164
|
+
});
|
|
165
|
+
} else {
|
|
166
|
+
executable.push({
|
|
167
|
+
requirementId: req.id,
|
|
168
|
+
action: req.content,
|
|
169
|
+
blockers: blockers.map(b => b.id),
|
|
170
|
+
dependencies: deps.map(d => d.id),
|
|
171
|
+
rationale: "Blocked by unresolved causal dependencies"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// Sort: executable first
|
|
176
|
+
return executable.sort((a, b) => (a.blockers.length === 0 ? -1 : 1));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Resolve minimum sufficient context for a task.
|
|
181
|
+
*
|
|
182
|
+
* @param {import('./context-graph.js').ContextGraph} graph
|
|
183
|
+
* @param {ResolveContextOptions} options
|
|
184
|
+
* @returns {ResolvedContext}
|
|
185
|
+
*/
|
|
186
|
+
export function resolveContext(graph, options) {
|
|
187
|
+
const { taskId, maxTokens = 4000 } = options;
|
|
188
|
+
|
|
189
|
+
const taskNode = graph.getNode(taskId);
|
|
190
|
+
if (!taskNode) {
|
|
191
|
+
return {
|
|
192
|
+
nodes: [],
|
|
193
|
+
edges: [],
|
|
194
|
+
missing: [{ requiredBy: taskId, type: "task", description: `Task ${taskId} not found` }],
|
|
195
|
+
omitted: [],
|
|
196
|
+
complete: false,
|
|
197
|
+
sufficient: false,
|
|
198
|
+
status: "INSUFFICIENT",
|
|
199
|
+
budgetOverflow: false,
|
|
200
|
+
mandatoryOmitted: 0,
|
|
201
|
+
tokenEstimate: 0,
|
|
202
|
+
summary: {
|
|
203
|
+
required: 1,
|
|
204
|
+
selected: 0,
|
|
205
|
+
omitted: 0,
|
|
206
|
+
missing: 1,
|
|
207
|
+
contradictions: 0,
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Phase 1: Collect directly required nodes
|
|
213
|
+
const required = new Set();
|
|
214
|
+
const missing = [];
|
|
215
|
+
// The active task is always part of its own required closure: it is the
|
|
216
|
+
// anchor the rest of the context hangs from, and consumers (evaluation,
|
|
217
|
+
// assembly) must be able to see it in `nodes`.
|
|
218
|
+
required.add(taskId);
|
|
219
|
+
// Also include any node with admission: "MANDATORY" explicitly
|
|
220
|
+
// Prefer the public getNodes() accessor; fall back to the internal Map.
|
|
221
|
+
const allNodes =
|
|
222
|
+
typeof graph.getNodes === "function"
|
|
223
|
+
? graph.getNodes()
|
|
224
|
+
: graph.nodes instanceof Map
|
|
225
|
+
? [...graph.nodes.values()]
|
|
226
|
+
: [];
|
|
227
|
+
for (const node of allNodes) {
|
|
228
|
+
if (node.metadata?.admission === "MANDATORY") {
|
|
229
|
+
required.add(node.id);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Get direct dependencies
|
|
234
|
+
const deps = graph.getDependencies(taskId);
|
|
235
|
+
for (const dep of deps) {
|
|
236
|
+
required.add(dep.id);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Phase 2: Expand dependency chain (transitively through outputs)
|
|
240
|
+
const upstream = graph.getUpstream(taskId, 5);
|
|
241
|
+
for (const node of upstream) {
|
|
242
|
+
required.add(node.id);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Also traverse through "produces" edges to find hidden dependencies
|
|
246
|
+
for (const id of [...required]) {
|
|
247
|
+
const producing = graph.getEdgesFromByType(id, "produces");
|
|
248
|
+
for (const edge of producing) {
|
|
249
|
+
required.add(edge.to);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Phase 3: Collect supporting evidence for required outputs
|
|
254
|
+
const requiredOutputs = [...required]
|
|
255
|
+
.map((id) => graph.getNode(id))
|
|
256
|
+
.filter((n) => n && n.type === "output");
|
|
257
|
+
|
|
258
|
+
for (const output of requiredOutputs) {
|
|
259
|
+
// Find evidence that supports this output
|
|
260
|
+
const supportingEdges = graph.getEdgesToByType(output.id, "supports");
|
|
261
|
+
for (const edge of supportingEdges) {
|
|
262
|
+
const evidence = graph.getNode(edge.from);
|
|
263
|
+
if (evidence) required.add(evidence.id);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Find requirements that this output fulfills
|
|
267
|
+
const requiredByEdges = graph.getEdgesToByType(output.id, "requires_output");
|
|
268
|
+
for (const edge of requiredByEdges) {
|
|
269
|
+
const req = graph.getNode(edge.from);
|
|
270
|
+
if (req) required.add(req.id);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Phase 4: Check for missing dependencies
|
|
275
|
+
for (const id of [...required]) {
|
|
276
|
+
const node = graph.getNode(id);
|
|
277
|
+
if (!node) {
|
|
278
|
+
// Find who requires this
|
|
279
|
+
const reqEdges = graph.getEdgesTo(id);
|
|
280
|
+
const requiredBy = reqEdges.length > 0 ? reqEdges[0].from : taskId;
|
|
281
|
+
missing.push({
|
|
282
|
+
requiredBy,
|
|
283
|
+
type: "dependency",
|
|
284
|
+
description: `Required node ${id} not found in graph`,
|
|
285
|
+
});
|
|
286
|
+
required.delete(id);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Also check edges from required nodes for missing targets
|
|
291
|
+
for (const id of [...required]) {
|
|
292
|
+
const edges = graph.getEdgesFrom(id);
|
|
293
|
+
for (const edge of edges) {
|
|
294
|
+
if (["requires_output", "requires_completion", "requires_evidence",
|
|
295
|
+
"requires_decision", "depends_on_artifact"].includes(edge.type)) {
|
|
296
|
+
if (!graph.getNode(edge.to)) {
|
|
297
|
+
missing.push({
|
|
298
|
+
requiredBy: id,
|
|
299
|
+
type: "dependency",
|
|
300
|
+
description: `Required node ${edge.to} not found (via ${edge.type})`,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Phase 5: Detect contradictions
|
|
308
|
+
const contradictions = [];
|
|
309
|
+
for (const id of [...required]) {
|
|
310
|
+
const contradicting = graph.getContradicting(id);
|
|
311
|
+
for (const c of contradicting) {
|
|
312
|
+
if (required.has(c.id)) {
|
|
313
|
+
contradictions.push({ node1: id, node2: c.id });
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Phase 6: Filter by token budget with admission classes
|
|
319
|
+
const nodes = [...required]
|
|
320
|
+
.map((id) => graph.getNode(id))
|
|
321
|
+
.filter(Boolean);
|
|
322
|
+
|
|
323
|
+
// Assign admission class based on node type and relationship to task
|
|
324
|
+
const mandatory = [];
|
|
325
|
+
const conditional = [];
|
|
326
|
+
const optional = [];
|
|
327
|
+
|
|
328
|
+
for (const node of nodes) {
|
|
329
|
+
const admission = getAdmissionClass(node, taskNode, graph);
|
|
330
|
+
if (admission === ADMISSION.MANDATORY) {
|
|
331
|
+
mandatory.push(node);
|
|
332
|
+
} else if (admission === ADMISSION.CONDITIONAL) {
|
|
333
|
+
conditional.push(node);
|
|
334
|
+
} else {
|
|
335
|
+
optional.push(node);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Sort each group by relevance (deterministic)
|
|
340
|
+
const sortByRelevance = (a, b) => {
|
|
341
|
+
const ra = computeRelevance(a, taskNode, graph);
|
|
342
|
+
const rb = computeRelevance(b, taskNode, graph);
|
|
343
|
+
if (rb !== ra) return rb - ra;
|
|
344
|
+
return a.id.localeCompare(b.id);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
mandatory.sort(sortByRelevance);
|
|
348
|
+
conditional.sort(sortByRelevance);
|
|
349
|
+
optional.sort(sortByRelevance);
|
|
350
|
+
|
|
351
|
+
// Select MANDATORY first, then CONDITIONAL, then OPTIONAL
|
|
352
|
+
let totalTokens = 0;
|
|
353
|
+
const selectedNodes = [];
|
|
354
|
+
const omitted = [];
|
|
355
|
+
|
|
356
|
+
// Phase 6a: Select all MANDATORY nodes (even if exceeding budget)
|
|
357
|
+
for (const node of mandatory) {
|
|
358
|
+
const tokens = nodeTokens(node);
|
|
359
|
+
selectedNodes.push(node);
|
|
360
|
+
totalTokens += tokens;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Phase 6b: Select CONDITIONAL nodes within remaining budget
|
|
364
|
+
for (const node of conditional) {
|
|
365
|
+
const tokens = nodeTokens(node);
|
|
366
|
+
if (totalTokens + tokens <= maxTokens) {
|
|
367
|
+
selectedNodes.push(node);
|
|
368
|
+
totalTokens += tokens;
|
|
369
|
+
} else {
|
|
370
|
+
omitted.push({
|
|
371
|
+
id: node.id,
|
|
372
|
+
reason: "budget",
|
|
373
|
+
admission: ADMISSION.CONDITIONAL,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Phase 6c: Select OPTIONAL nodes within remaining budget
|
|
379
|
+
for (const node of optional) {
|
|
380
|
+
const tokens = nodeTokens(node);
|
|
381
|
+
if (totalTokens + tokens <= maxTokens) {
|
|
382
|
+
selectedNodes.push(node);
|
|
383
|
+
totalTokens += tokens;
|
|
384
|
+
} else {
|
|
385
|
+
omitted.push({
|
|
386
|
+
id: node.id,
|
|
387
|
+
reason: "budget",
|
|
388
|
+
admission: ADMISSION.OPTIONAL,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// Phase 7: Collect edges between selected nodes
|
|
394
|
+
const selectedIds = new Set(selectedNodes.map((n) => n.id));
|
|
395
|
+
const edges = [];
|
|
396
|
+
for (const node of selectedNodes) {
|
|
397
|
+
for (const edge of graph.getEdgesFrom(node.id)) {
|
|
398
|
+
if (selectedIds.has(edge.to)) {
|
|
399
|
+
edges.push(edge);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Phase 8: Add contradiction warnings to missing
|
|
405
|
+
for (const contr of contradictions) {
|
|
406
|
+
missing.push({
|
|
407
|
+
requiredBy: contr.node1,
|
|
408
|
+
type: "contradiction",
|
|
409
|
+
description: `Node ${contr.node1} contradicts ${contr.node2}`,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Phase 9: Determine completeness and sufficiency
|
|
414
|
+
const complete = missing.length === 0;
|
|
415
|
+
|
|
416
|
+
// Check if any MANDATORY nodes were omitted
|
|
417
|
+
const mandatoryOmitted = omitted.filter((o) => o.admission === ADMISSION.MANDATORY);
|
|
418
|
+
|
|
419
|
+
// Check if mandatory exceeded budget
|
|
420
|
+
const mandatoryTokens = mandatory.reduce((sum, n) => sum + nodeTokens(n), 0);
|
|
421
|
+
const budgetOverflow = mandatoryTokens > maxTokens;
|
|
422
|
+
|
|
423
|
+
// Sufficient = complete + no mandatory omitted + no budget overflow + no contradictions
|
|
424
|
+
const sufficient = complete && mandatoryOmitted.length === 0 && !budgetOverflow && contradictions.length === 0;
|
|
425
|
+
|
|
426
|
+
// Determine status
|
|
427
|
+
let status;
|
|
428
|
+
if (contradictions.length > 0) {
|
|
429
|
+
status = "CONFLICTED";
|
|
430
|
+
} else if (!complete || mandatoryOmitted.length > 0 || budgetOverflow) {
|
|
431
|
+
status = "INSUFFICIENT";
|
|
432
|
+
} else if (selectedNodes.length < required.size) {
|
|
433
|
+
status = "PARTIAL";
|
|
434
|
+
} else {
|
|
435
|
+
status = "COMPLETE";
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
nodes: selectedNodes,
|
|
440
|
+
edges,
|
|
441
|
+
missing,
|
|
442
|
+
omitted,
|
|
443
|
+
complete,
|
|
444
|
+
sufficient,
|
|
445
|
+
status,
|
|
446
|
+
tokenEstimate: totalTokens,
|
|
447
|
+
budgetOverflow,
|
|
448
|
+
mandatoryOmitted: mandatoryOmitted.length,
|
|
449
|
+
summary: {
|
|
450
|
+
required: required.size,
|
|
451
|
+
selected: selectedNodes.length,
|
|
452
|
+
omitted: omitted.length,
|
|
453
|
+
missing: missing.length,
|
|
454
|
+
contradictions: contradictions.length,
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
}
|