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,20 @@
|
|
|
1
|
+
// Requirement ownership: verify who can validate a requirement
|
|
2
|
+
export function verifyRequirementOwnership(task, owner) {
|
|
3
|
+
if (!task || !owner) return { owned: false, reason: "missing-input" };
|
|
4
|
+
const taskOwner = task.owner || task.assignedTo || null;
|
|
5
|
+
if (taskOwner === owner) return { owned: true, owner: taskOwner };
|
|
6
|
+
if (task.verifiers && Array.isArray(task.verifiers) && task.verifiers.includes(owner)) {
|
|
7
|
+
return { owned: true, owner: taskOwner };
|
|
8
|
+
}
|
|
9
|
+
return { owned: false, reason: "not-owner", owner: taskOwner };
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Assert that a task can be verified given its state and ownership
|
|
13
|
+
export function assertCanVerify(task, verifier) {
|
|
14
|
+
if (!task) return { canVerify: false, reason: "missing-task" };
|
|
15
|
+
if (task.status === "VERIFIED") return { canVerify: false, reason: "already-verified" };
|
|
16
|
+
if (task.status === "BLOCKED") return { canVerify: false, reason: "blocked" };
|
|
17
|
+
const ownership = verifyRequirementOwnership(task, verifier);
|
|
18
|
+
if (!ownership.owned) return { canVerify: false, reason: "no-ownership", ...ownership };
|
|
19
|
+
return { canVerify: true, verifier };
|
|
20
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Requirement State Machine
|
|
3
|
+
*
|
|
4
|
+
* Formalizes the lifecycle states of a requirement and the legal transitions
|
|
5
|
+
* between them. Used by completion-gate to determine whether a requirement can
|
|
6
|
+
* be considered complete.
|
|
7
|
+
*
|
|
8
|
+
* States:
|
|
9
|
+
* - PENDING : requirement identified but not started
|
|
10
|
+
* - IN_PROGRESS : work is being done; some evidence may exist
|
|
11
|
+
* - VERIFIED : sufficient verified evidence exists; gates can pass
|
|
12
|
+
* - INVALIDATED : existing evidence was invalidated (stale, contradicted, etc.)
|
|
13
|
+
* - BLOCKED : cannot proceed (dependency, missing info, external blocker)
|
|
14
|
+
* - NEEDS_REPLAN : current approach cannot satisfy the requirement
|
|
15
|
+
*
|
|
16
|
+
* Transition table is the source of truth. Any transition not listed is
|
|
17
|
+
* considered illegal and will return { allowed: false } from canTransition().
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export const REQUIREMENT_STATES = Object.freeze({
|
|
21
|
+
PENDING: "PENDING",
|
|
22
|
+
IN_PROGRESS: "IN_PROGRESS",
|
|
23
|
+
VERIFIED: "VERIFIED",
|
|
24
|
+
INVALIDATED: "INVALIDATED",
|
|
25
|
+
BLOCKED: "BLOCKED",
|
|
26
|
+
NEEDS_REPLAN: "NEEDS_REPLAN",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
export const TERMINAL_STATES = new Set([
|
|
30
|
+
REQUIREMENT_STATES.VERIFIED,
|
|
31
|
+
]);
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Allowed transitions: from -> Set(to).
|
|
35
|
+
* VERIFIED is intentionally reachable from a small set of states to avoid
|
|
36
|
+
* silently promoting blocked/needs-replan requirements.
|
|
37
|
+
*/
|
|
38
|
+
export const TRANSITIONS = Object.freeze({
|
|
39
|
+
[REQUIREMENT_STATES.PENDING]: new Set([
|
|
40
|
+
REQUIREMENT_STATES.IN_PROGRESS,
|
|
41
|
+
REQUIREMENT_STATES.BLOCKED,
|
|
42
|
+
]),
|
|
43
|
+
[REQUIREMENT_STATES.IN_PROGRESS]: new Set([
|
|
44
|
+
REQUIREMENT_STATES.VERIFIED,
|
|
45
|
+
REQUIREMENT_STATES.INVALIDATED,
|
|
46
|
+
REQUIREMENT_STATES.BLOCKED,
|
|
47
|
+
REQUIREMENT_STATES.NEEDS_REPLAN,
|
|
48
|
+
REQUIREMENT_STATES.PENDING,
|
|
49
|
+
]),
|
|
50
|
+
[REQUIREMENT_STATES.VERIFIED]: new Set([
|
|
51
|
+
// Once verified, it can be invalidated if evidence is later contradicted.
|
|
52
|
+
REQUIREMENT_STATES.INVALIDATED,
|
|
53
|
+
]),
|
|
54
|
+
[REQUIREMENT_STATES.INVALIDATED]: new Set([
|
|
55
|
+
REQUIREMENT_STATES.IN_PROGRESS,
|
|
56
|
+
REQUIREMENT_STATES.NEEDS_REPLAN,
|
|
57
|
+
REQUIREMENT_STATES.PENDING,
|
|
58
|
+
]),
|
|
59
|
+
[REQUIREMENT_STATES.BLOCKED]: new Set([
|
|
60
|
+
REQUIREMENT_STATES.IN_PROGRESS,
|
|
61
|
+
REQUIREMENT_STATES.PENDING,
|
|
62
|
+
REQUIREMENT_STATES.NEEDS_REPLAN,
|
|
63
|
+
]),
|
|
64
|
+
[REQUIREMENT_STATES.NEEDS_REPLAN]: new Set([
|
|
65
|
+
REQUIREMENT_STATES.PENDING,
|
|
66
|
+
REQUIREMENT_STATES.IN_PROGRESS,
|
|
67
|
+
]),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns whether `to` is a legal next state from `from`.
|
|
72
|
+
*/
|
|
73
|
+
export function canTransition(from, to) {
|
|
74
|
+
if (!Object.values(REQUIREMENT_STATES).includes(to)) {
|
|
75
|
+
return { allowed: false, reason: `unknown target state: ${to}` };
|
|
76
|
+
}
|
|
77
|
+
const allowed = TRANSITIONS[from];
|
|
78
|
+
if (!allowed) {
|
|
79
|
+
return { allowed: false, reason: `unknown source state: ${from}` };
|
|
80
|
+
}
|
|
81
|
+
if (!allowed.has(to)) {
|
|
82
|
+
return {
|
|
83
|
+
allowed: false,
|
|
84
|
+
reason: `illegal transition ${from} -> ${to}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { allowed: true };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Executes a transition. Returns a new state object (immutable update) or
|
|
92
|
+
* an { error } object if the transition is illegal.
|
|
93
|
+
*/
|
|
94
|
+
export function transition(requirement, to) {
|
|
95
|
+
if (!requirement || typeof requirement !== "object") {
|
|
96
|
+
return { error: "requirement must be an object" };
|
|
97
|
+
}
|
|
98
|
+
const from = requirement.status || REQUIREMENT_STATES.PENDING;
|
|
99
|
+
const check = canTransition(from, to);
|
|
100
|
+
if (!check.allowed) {
|
|
101
|
+
return { error: check.reason, from, to };
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
...requirement,
|
|
105
|
+
status: to,
|
|
106
|
+
lastTransitionAt: new Date().toISOString(),
|
|
107
|
+
lastTransitionFrom: from,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Pure decision: is this requirement eligible for completion?
|
|
113
|
+
* Only VERIFIED requirements are complete.
|
|
114
|
+
*/
|
|
115
|
+
export function isRequirementComplete(requirement) {
|
|
116
|
+
return Boolean(
|
|
117
|
+
requirement &&
|
|
118
|
+
typeof requirement === "object" &&
|
|
119
|
+
requirement.status === REQUIREMENT_STATES.VERIFIED
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Aggregates a list of requirements into an overall verdict.
|
|
125
|
+
* Returns { allComplete, blocked, incomplete, summary }.
|
|
126
|
+
*/
|
|
127
|
+
export function summarizeRequirements(requirements = []) {
|
|
128
|
+
const incomplete = [];
|
|
129
|
+
const blocked = [];
|
|
130
|
+
for (const req of requirements) {
|
|
131
|
+
if (!isRequirementComplete(req)) {
|
|
132
|
+
incomplete.push(req);
|
|
133
|
+
if (
|
|
134
|
+
req.status === REQUIREMENT_STATES.BLOCKED ||
|
|
135
|
+
req.status === REQUIREMENT_STATES.NEEDS_REPLAN
|
|
136
|
+
) {
|
|
137
|
+
blocked.push(req);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return {
|
|
142
|
+
allComplete: incomplete.length === 0,
|
|
143
|
+
incomplete,
|
|
144
|
+
blocked,
|
|
145
|
+
summary: {
|
|
146
|
+
total: requirements.length,
|
|
147
|
+
complete: requirements.length - incomplete.length,
|
|
148
|
+
incomplete: incomplete.length,
|
|
149
|
+
blocked: blocked.length,
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Event Identity — scoped, stable event IDs.
|
|
3
|
+
*
|
|
4
|
+
* Events use composite IDs: <runId>:<type>:<sequence>
|
|
5
|
+
*
|
|
6
|
+
* Format:
|
|
7
|
+
* run-003:act:001
|
|
8
|
+
* run-003:obs:004
|
|
9
|
+
* run-003:dec:002
|
|
10
|
+
* run-003:ev:003
|
|
11
|
+
*
|
|
12
|
+
* Benefits:
|
|
13
|
+
* - Unique within task (different runs have different prefixes)
|
|
14
|
+
* - Readable and debuggable
|
|
15
|
+
* - Legacy IDs still supported for backward compatibility
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import fs from "node:fs";
|
|
19
|
+
import path from "node:path";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {"act" | "obs" | "dec" | "ev"} EventType
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate a scoped event ID.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} runId - Run ID (e.g., "run-003")
|
|
29
|
+
* @param {EventType} type - Event type
|
|
30
|
+
* @param {number} sequence - Sequence number
|
|
31
|
+
* @returns {string} Scoped event ID (e.g., "run-003:act:001")
|
|
32
|
+
*/
|
|
33
|
+
export function generateEventId(runId, type, sequence) {
|
|
34
|
+
const seq = String(sequence).padStart(3, "0");
|
|
35
|
+
return `${runId}:${type}:${seq}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Parse a scoped event ID into components.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} eventId - Scoped event ID
|
|
42
|
+
* @returns {{ runId: string, type: EventType, sequence: number } | null}
|
|
43
|
+
*/
|
|
44
|
+
export function parseEventId(eventId) {
|
|
45
|
+
if (!eventId || typeof eventId !== "string") return null;
|
|
46
|
+
|
|
47
|
+
const parts = eventId.split(":");
|
|
48
|
+
if (parts.length !== 3) return null;
|
|
49
|
+
|
|
50
|
+
const [runId, type, seqStr] = parts;
|
|
51
|
+
const sequence = parseInt(seqStr, 10);
|
|
52
|
+
|
|
53
|
+
if (isNaN(sequence)) return null;
|
|
54
|
+
if (!["act", "obs", "dec", "ev"].includes(type)) return null;
|
|
55
|
+
|
|
56
|
+
return { runId, type: /** @type {EventType} */ (type), sequence };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Check if an ID is a scoped event ID (vs legacy).
|
|
61
|
+
*
|
|
62
|
+
* @param {string} id
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
export function isScopedEventId(id) {
|
|
66
|
+
return parseEventId(id) !== null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Get the next sequence number for an event type in a run.
|
|
71
|
+
*
|
|
72
|
+
* @param {Object} run - Run object
|
|
73
|
+
* @param {EventType} type - Event type
|
|
74
|
+
* @returns {number} Next sequence number
|
|
75
|
+
*/
|
|
76
|
+
export function getNextSequence(run, type) {
|
|
77
|
+
const fieldMap = {
|
|
78
|
+
act: "actions",
|
|
79
|
+
obs: "observations",
|
|
80
|
+
dec: "decisions",
|
|
81
|
+
ev: "evidence",
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const field = fieldMap[type];
|
|
85
|
+
if (!field || !run[field]) return 1;
|
|
86
|
+
|
|
87
|
+
// Count existing scoped IDs for this type
|
|
88
|
+
let maxSeq = 0;
|
|
89
|
+
for (const event of run[field]) {
|
|
90
|
+
if (event.id && isScopedEventId(event.id)) {
|
|
91
|
+
const parsed = parseEventId(event.id);
|
|
92
|
+
if (parsed && parsed.type === type && parsed.sequence > maxSeq) {
|
|
93
|
+
maxSeq = parsed.sequence;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return maxSeq + 1;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Create a scoped event within a run.
|
|
103
|
+
*
|
|
104
|
+
* @param {Object} run - Run object
|
|
105
|
+
* @param {EventType} type - Event type
|
|
106
|
+
* @param {Object} data - Event data
|
|
107
|
+
* @returns {Object} Created event with scoped ID
|
|
108
|
+
*/
|
|
109
|
+
export function createScopedEvent(run, type, data) {
|
|
110
|
+
const sequence = getNextSequence(run, type);
|
|
111
|
+
const id = generateEventId(run.id, type, sequence);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
id,
|
|
115
|
+
...data,
|
|
116
|
+
timestamp: Date.now(),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Migrate legacy event IDs to scoped format.
|
|
122
|
+
*
|
|
123
|
+
* @param {Object} run - Run object
|
|
124
|
+
* @returns {Object} Run with migrated IDs
|
|
125
|
+
*/
|
|
126
|
+
export function migrateRunEventIds(run) {
|
|
127
|
+
if (!run) return run;
|
|
128
|
+
|
|
129
|
+
const fieldMap = {
|
|
130
|
+
actions: "act",
|
|
131
|
+
observations: "obs",
|
|
132
|
+
decisions: "dec",
|
|
133
|
+
evidence: "ev",
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
for (const [field, type] of Object.entries(fieldMap)) {
|
|
137
|
+
if (!run[field]) continue;
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < run[field].length; i++) {
|
|
140
|
+
const event = run[field][i];
|
|
141
|
+
// Only migrate legacy IDs (not already scoped)
|
|
142
|
+
if (event.id && !isScopedEventId(event.id)) {
|
|
143
|
+
const sequence = i + 1;
|
|
144
|
+
event.id = generateEventId(run.id, type, sequence);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return run;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get event type from scoped ID.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} eventId
|
|
156
|
+
* @returns {EventType | null}
|
|
157
|
+
*/
|
|
158
|
+
export function getEventType(eventId) {
|
|
159
|
+
const parsed = parseEventId(eventId);
|
|
160
|
+
return parsed ? parsed.type : null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Get run ID from scoped event ID.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} eventId
|
|
167
|
+
* @returns {string | null}
|
|
168
|
+
*/
|
|
169
|
+
export function getRunIdFromEvent(eventId) {
|
|
170
|
+
const parsed = parseEventId(eventId);
|
|
171
|
+
return parsed ? parsed.runId : null;
|
|
172
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formal Execution State — WAM 1.2
|
|
3
|
+
*
|
|
4
|
+
* Implementa el lifecycle formal de la tarea.
|
|
5
|
+
*/
|
|
6
|
+
export const STATE = {
|
|
7
|
+
INITIALIZING: "INITIALIZING",
|
|
8
|
+
INVESTIGATING: "INVESTIGATING",
|
|
9
|
+
EXECUTING: "EXECUTING",
|
|
10
|
+
VERIFYING: "VERIFYING",
|
|
11
|
+
COMPLETED: "COMPLETED",
|
|
12
|
+
BLOCKED: "BLOCKED",
|
|
13
|
+
WAITING_AUTHORIZATION: "WAITING_AUTHORIZATION",
|
|
14
|
+
FAILED: "FAILED",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export function getPhaseLabel(phase) {
|
|
18
|
+
return phase || STATE.INITIALIZING;
|
|
19
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Machine — centralized transitions for hypothesis / experiment /
|
|
3
|
+
* evidence events during the execution loop.
|
|
4
|
+
*
|
|
5
|
+
* Pure functions: given an assessment (and optional supporting context),
|
|
6
|
+
* return the next status and any side-effect descriptors. No I/O.
|
|
7
|
+
*
|
|
8
|
+
* Decouples decision logic from execution-engine.js so status transitions
|
|
9
|
+
* become testable in isolation and reusable across noteSuccess / noteFailure /
|
|
10
|
+
* handleContradiction callers.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { AssessmentResult } from "../policy/assessment-engine.js";
|
|
14
|
+
import { HYPOTHESIS_STATUS, EXPERIMENT_STATUS } from "../cognition/cognition-store.js";
|
|
15
|
+
|
|
16
|
+
export { HYPOTHESIS_STATUS, EXPERIMENT_STATUS };
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Contradiction severity classification. Higher severity → faster replan.
|
|
20
|
+
*/
|
|
21
|
+
export const SEVERITY = Object.freeze({
|
|
22
|
+
HIGH: "high",
|
|
23
|
+
LOW: "low",
|
|
24
|
+
NONE: null,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Derive the next hypothesis status from an assessment verdict.
|
|
29
|
+
* Returns { status, severity } so callers can branch on both.
|
|
30
|
+
*
|
|
31
|
+
* Rules (M2 execution-loop contract):
|
|
32
|
+
* - SUPPORTED → SUPPORTED, no severity
|
|
33
|
+
* - CONTRADICTED → REJECTED (high) when contradicted > supported; else TESTING (low)
|
|
34
|
+
* - INCONCLUSIVE → TESTING, no severity
|
|
35
|
+
*
|
|
36
|
+
* @param {object} assessment - { result, summary: { contradicted, supported } }
|
|
37
|
+
* @returns {{status: string, severity: string|null}}
|
|
38
|
+
*/
|
|
39
|
+
export function deriveHypothesisStatus(assessment) {
|
|
40
|
+
if (!assessment || typeof assessment !== "object") {
|
|
41
|
+
return { status: HYPOTHESIS_STATUS.TESTING, severity: SEVERITY.NONE };
|
|
42
|
+
}
|
|
43
|
+
if (assessment.result === AssessmentResult.SUPPORTED) {
|
|
44
|
+
return { status: HYPOTHESIS_STATUS.SUPPORTED, severity: SEVERITY.NONE };
|
|
45
|
+
}
|
|
46
|
+
if (assessment.result === AssessmentResult.CONTRADICTED) {
|
|
47
|
+
const { summary = {} } = assessment;
|
|
48
|
+
const contradicted = summary.contradicted || 0;
|
|
49
|
+
const supported = summary.supported || 0;
|
|
50
|
+
if (contradicted > supported && contradicted > 0) {
|
|
51
|
+
return { status: HYPOTHESIS_STATUS.REJECTED, severity: SEVERITY.HIGH };
|
|
52
|
+
}
|
|
53
|
+
return { status: HYPOTHESIS_STATUS.TESTING, severity: SEVERITY.LOW };
|
|
54
|
+
}
|
|
55
|
+
return { status: HYPOTHESIS_STATUS.TESTING, severity: SEVERITY.NONE };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Decide whether the current outcome should trigger a replan.
|
|
60
|
+
* A replan is warranted when:
|
|
61
|
+
* - assessment is CONTRADICTED AND
|
|
62
|
+
* - hypothesis has moved to REJECTED (high severity contradiction), OR
|
|
63
|
+
* - hypothesis remains TESTING but contradicted (caller may want to re-explore)
|
|
64
|
+
*
|
|
65
|
+
* @param {object} args
|
|
66
|
+
* @param {object} args.assessment
|
|
67
|
+
* @param {string} args.hypothesisStatus
|
|
68
|
+
* @returns {boolean}
|
|
69
|
+
*/
|
|
70
|
+
export function shouldReplan({ assessment, hypothesisStatus }) {
|
|
71
|
+
if (!assessment || assessment.result !== AssessmentResult.CONTRADICTED) return false;
|
|
72
|
+
if (hypothesisStatus === HYPOTHESIS_STATUS.REJECTED) return true;
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Decide whether the contradiction should be noted as a learning event
|
|
78
|
+
* (without forcing a full replan).
|
|
79
|
+
*
|
|
80
|
+
* @param {object} args
|
|
81
|
+
* @param {object} args.assessment
|
|
82
|
+
* @param {string} args.hypothesisStatus
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
export function shouldNoteContradiction({ assessment, hypothesisStatus }) {
|
|
86
|
+
if (!assessment || assessment.result !== AssessmentResult.CONTRADICTED) return false;
|
|
87
|
+
// When REJECTED, the caller may already replan; still useful to note it.
|
|
88
|
+
return hypothesisStatus !== HYPOTHESIS_STATUS.SUPPORTED;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Pick the experiment terminal status for an outcome.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} outcome - "success" | "failure"
|
|
95
|
+
* @returns {string}
|
|
96
|
+
*/
|
|
97
|
+
export function deriveExperimentStatus(outcome) {
|
|
98
|
+
return outcome === "failure"
|
|
99
|
+
? EXPERIMENT_STATUS.FAILED
|
|
100
|
+
: EXPERIMENT_STATUS.COMPLETED;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Contradiction-handling transition: given a contradicted experiment,
|
|
105
|
+
* compute the next hypothesis status + replan trigger descriptor.
|
|
106
|
+
*
|
|
107
|
+
* @param {object} args
|
|
108
|
+
* @param {string} args.outcome - "success" | "failure" | "contradicted"
|
|
109
|
+
* @param {object} args.assessment
|
|
110
|
+
* @returns {{status: string, replan: boolean, severity: string|null}}
|
|
111
|
+
*/
|
|
112
|
+
export function transitionAfterOutcome({ outcome, assessment }) {
|
|
113
|
+
const derived = deriveHypothesisStatus(assessment);
|
|
114
|
+
const replan = shouldReplan({
|
|
115
|
+
assessment,
|
|
116
|
+
hypothesisStatus: derived.status,
|
|
117
|
+
});
|
|
118
|
+
return { ...derived, replan };
|
|
119
|
+
}
|