cool-workflow 0.1.97 → 0.2.0
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +11 -2
- package/apps/architecture-review/app.json +1 -1
- package/apps/architecture-review-fast/app.json +1 -1
- package/apps/end-to-end-golden-path/app.json +1 -1
- package/apps/pr-review-fix-ci/app.json +1 -1
- package/apps/release-cut/app.json +1 -1
- package/apps/research-synthesis/app.json +1 -1
- package/dist/cli/dispatch.js +236 -0
- package/dist/cli/entry.js +120 -0
- package/dist/cli/io.js +21 -4
- package/dist/cli/parseargv.js +157 -0
- package/dist/cli.js +6 -33
- package/dist/core/capability-table.js +3534 -0
- package/dist/core/format/help.js +314 -0
- package/dist/{state-explosion/format.js → core/format/state-explosion-text.js} +10 -1
- package/dist/core/hash.js +137 -0
- package/dist/core/multi-agent/candidate-scoring.js +219 -0
- package/dist/core/multi-agent/collaboration.js +481 -0
- package/dist/core/multi-agent/coordinator.js +515 -0
- package/dist/core/multi-agent/eval-replay.js +306 -0
- package/dist/core/multi-agent/runtime.js +929 -0
- package/dist/core/multi-agent/topology.js +298 -0
- package/dist/core/multi-agent/trust-policy.js +197 -0
- package/dist/core/pipeline/commit-gate.js +320 -0
- package/dist/{pipeline-contract.js → core/pipeline/contract.js} +24 -37
- package/dist/core/pipeline/dispatch.js +103 -0
- package/dist/core/pipeline/drive-decide.js +227 -0
- package/dist/core/pipeline/error-feedback.js +161 -0
- package/dist/core/pipeline/loop-expansion.js +124 -0
- package/dist/{result-normalize.js → core/pipeline/result-normalize.js} +14 -37
- package/dist/core/pipeline/runner.js +230 -0
- package/dist/{contract-migration.js → core/state/contract-migration.js} +68 -92
- package/dist/{state-migrations.js → core/state/migrations.js} +103 -96
- package/dist/core/state/node-projection.js +95 -0
- package/dist/core/state/node-snapshot.js +230 -0
- package/dist/core/state/run-paths.js +93 -0
- package/dist/{schema-validate.js → core/state/schema-validate.js} +35 -28
- package/dist/core/state/schema.js +50 -0
- package/dist/core/state/state-explosion/digest.js +243 -0
- package/dist/core/state/state-explosion/graph.js +527 -0
- package/dist/{state-explosion → core/state/state-explosion}/helpers.js +34 -3
- package/dist/core/state/state-explosion/report.js +187 -0
- package/dist/{state-explosion → core/state/state-explosion}/size.js +25 -7
- package/dist/{state-node.js → core/state/state-node.js} +90 -113
- package/dist/core/state/types.js +19 -0
- package/dist/{validation.js → core/state/validation.js} +64 -131
- package/dist/core/trust/evidence-grounding.js +134 -0
- package/dist/core/trust/ledger.js +199 -0
- package/dist/{telemetry-attestation.js → core/trust/telemetry-attestation.js} +94 -68
- package/dist/core/trust/telemetry-ledger.js +127 -0
- package/dist/core/types.js +20 -0
- package/dist/core/version.js +16 -0
- package/dist/{workflow-app-framework.js → core/workflow-apps/app-schema.js} +337 -426
- package/dist/mcp/dispatch.js +104 -0
- package/dist/mcp/server.js +144 -0
- package/dist/mcp-server.js +6 -84
- package/dist/{agent-config.js → shell/agent-config.js} +118 -71
- package/dist/shell/app-run-cli.js +88 -0
- package/dist/shell/audit-cli.js +259 -0
- package/dist/shell/audit-provenance.js +83 -0
- package/dist/shell/candidate-scoring-io.js +459 -0
- package/dist/shell/collaboration-io.js +264 -0
- package/dist/shell/commit-summary.js +104 -0
- package/dist/shell/commit.js +290 -0
- package/dist/shell/coordinator-io.js +476 -0
- package/dist/shell/demo-cli.js +19 -0
- package/dist/shell/dispatch.js +162 -0
- package/dist/{doctor.js → shell/doctor.js} +123 -56
- package/dist/shell/drive.js +873 -0
- package/dist/shell/error-feedback-io.js +305 -0
- package/dist/shell/eval-io.js +473 -0
- package/dist/{multi-agent-eval/format.js → shell/eval-text.js} +78 -49
- package/dist/{evidence-reasoning.js → shell/evidence-reasoning.js} +124 -255
- package/dist/shell/exec-backend-cli.js +88 -0
- package/dist/shell/execution-backend/agent.js +475 -0
- package/dist/shell/execution-backend/ci.js +15 -0
- package/dist/shell/execution-backend/container.js +69 -0
- package/dist/shell/execution-backend/envelopes.js +55 -0
- package/dist/shell/execution-backend/local.js +113 -0
- package/dist/shell/execution-backend/probes.js +175 -0
- package/dist/shell/execution-backend/registry.js +402 -0
- package/dist/shell/execution-backend/remote.js +128 -0
- package/dist/shell/execution-backend/types.js +11 -0
- package/dist/shell/feedback-cli.js +81 -0
- package/dist/shell/feedback-operations.js +48 -0
- package/dist/shell/fs-atomic.js +276 -0
- package/dist/shell/harness.js +98 -0
- package/dist/shell/ledger-cli.js +212 -0
- package/dist/shell/ledger-io.js +169 -0
- package/dist/shell/man-cli.js +89 -0
- package/dist/shell/metrics-cli.js +98 -0
- package/dist/shell/multi-agent-cli.js +1002 -0
- package/dist/shell/multi-agent-host.js +563 -0
- package/dist/shell/multi-agent-io.js +387 -0
- package/dist/{multi-agent-operator-ux.js → shell/multi-agent-operator-ux.js} +234 -191
- package/dist/shell/node-store.js +124 -0
- package/dist/{observability/format.js → shell/observability-format.js} +7 -1
- package/dist/{observability/intake.js → shell/observability-intake.js} +79 -56
- package/dist/{observability.js → shell/observability.js} +159 -332
- package/dist/{onramp.js → shell/onramp.js} +11 -0
- package/dist/{operator-ux/format.js → shell/operator-ux-text.js} +250 -239
- package/dist/shell/operator-ux.js +431 -0
- package/dist/shell/orchestrator.js +231 -0
- package/dist/shell/pipeline-cli.js +667 -0
- package/dist/shell/pipeline.js +217 -0
- package/dist/shell/reclamation-io.js +1366 -0
- package/dist/shell/registry-cli.js +329 -0
- package/dist/{remote-source.js → shell/remote-source.js} +12 -5
- package/dist/shell/report-cli.js +101 -0
- package/dist/shell/report-view-cli.js +117 -0
- package/dist/{orchestrator → shell}/report.js +289 -282
- package/dist/shell/reporter.js +62 -0
- package/dist/shell/run-export-cli.js +106 -0
- package/dist/shell/run-export.js +680 -0
- package/dist/shell/run-registry-io.js +1014 -0
- package/dist/shell/run-store.js +164 -0
- package/dist/{sandbox-profile.js → shell/sandbox-profile.js} +134 -95
- package/dist/{scheduler.js → shell/scheduler-io.js} +248 -48
- package/dist/shell/scheduling-io.js +311 -0
- package/dist/shell/state-cli.js +181 -0
- package/dist/shell/state-explosion-cli.js +197 -0
- package/dist/shell/telemetry-cli.js +85 -0
- package/dist/{telemetry-demo.js → shell/telemetry-demo.js} +149 -119
- package/dist/shell/telemetry-ledger-io.js +132 -0
- package/dist/{term.js → shell/term.js} +36 -46
- package/dist/shell/topology-io.js +361 -0
- package/dist/shell/trust-audit.js +471 -0
- package/dist/{multi-agent-trust.js → shell/trust-policy-io.js} +67 -205
- package/dist/shell/verifier.js +48 -0
- package/dist/shell/workbench-host.js +250 -0
- package/dist/shell/workbench-text.js +18 -0
- package/dist/shell/workbench.js +175 -0
- package/dist/shell/worker-cli.js +124 -0
- package/dist/shell/worker-isolation.js +852 -0
- package/dist/shell/workflow-app-loader.js +650 -0
- package/docs/agent-delegation-drive.7.md +4 -0
- package/docs/cli-mcp-parity.7.md +284 -211
- package/docs/contract-migration-tooling.7.md +4 -0
- package/docs/control-plane-scheduling.7.md +4 -0
- package/docs/cross-agent-ledger.7.md +217 -0
- package/docs/designs/handoff-ledger.md +145 -0
- package/docs/durable-state-and-locking.7.md +4 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +4 -0
- package/docs/execution-backends.7.md +4 -0
- package/docs/handoff-setup.md +120 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +4 -0
- package/docs/multi-agent-eval-replay-harness.7.md +4 -0
- package/docs/multi-agent-operator-ux.7.md +4 -0
- package/docs/node-snapshot-diff-replay.7.md +4 -0
- package/docs/observability-cost-accounting.7.md +4 -0
- package/docs/project-index.md +143 -71
- package/docs/real-execution-backends.7.md +4 -0
- package/docs/release-and-migration.7.md +4 -0
- package/docs/release-tooling.7.md +4 -0
- package/docs/run-registry-control-plane.7.md +4 -0
- package/docs/run-retention-reclamation.7.md +25 -0
- package/docs/state-explosion-management.7.md +4 -0
- package/docs/team-collaboration.7.md +4 -0
- package/docs/web-desktop-workbench.7.md +4 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- package/scripts/agents/codex-agent.js +34 -4
- package/scripts/agents/cw-attest-wrap.js +2 -2
- package/scripts/bump-version.js +4 -3
- package/scripts/canonical-apps.js +4 -4
- package/scripts/children/batch-delegate-child.js +30 -10
- package/scripts/dogfood-architecture-review.js +2 -3
- package/scripts/dogfood-release.js +3 -3
- package/scripts/gen-parity-doc.js +15 -2
- package/scripts/golden-path.js +4 -4
- package/scripts/onramp-check.js +1 -1
- package/scripts/parity-check.js +38 -21
- package/scripts/release-flow.js +9 -3
- package/scripts/sync-project-index.js +51 -27
- package/scripts/validate-run-state-schema.js +2 -2
- package/scripts/version-sync-check.js +30 -30
- package/dist/candidate-scoring.js +0 -729
- package/dist/capability-core.js +0 -1186
- package/dist/capability-registry.js +0 -879
- package/dist/cli/command-surface.js +0 -490
- package/dist/cli/format.js +0 -56
- package/dist/cli/handlers/audit.js +0 -82
- package/dist/cli/handlers/blackboard.js +0 -81
- package/dist/cli/handlers/candidate.js +0 -40
- package/dist/cli/handlers/clones.js +0 -34
- package/dist/cli/handlers/collaboration.js +0 -61
- package/dist/cli/handlers/eval.js +0 -40
- package/dist/cli/handlers/maintenance.js +0 -107
- package/dist/cli/handlers/multi-agent.js +0 -165
- package/dist/cli/handlers/node.js +0 -41
- package/dist/cli/handlers/operational.js +0 -155
- package/dist/cli/handlers/operator.js +0 -146
- package/dist/cli/handlers/registry.js +0 -68
- package/dist/cli/handlers/run.js +0 -153
- package/dist/cli/handlers/scheduling.js +0 -132
- package/dist/cli/handlers/workbench.js +0 -41
- package/dist/cli/handlers/worker.js +0 -45
- package/dist/cli/run-summary.js +0 -45
- package/dist/clones.js +0 -162
- package/dist/collaboration.js +0 -726
- package/dist/commit.js +0 -592
- package/dist/compare.js +0 -18
- package/dist/coordinator/classify.js +0 -45
- package/dist/coordinator/paths.js +0 -42
- package/dist/coordinator/util.js +0 -126
- package/dist/coordinator.js +0 -990
- package/dist/daemon.js +0 -44
- package/dist/dispatch.js +0 -250
- package/dist/drive.js +0 -827
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -294
- package/dist/execution-backend/probes.js +0 -112
- package/dist/execution-backend/util.js +0 -47
- package/dist/execution-backend.js +0 -961
- package/dist/gates.js +0 -48
- package/dist/harness.js +0 -61
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -434
- package/dist/mcp/tool-definitions.js +0 -1040
- package/dist/mcp-surface.js +0 -30
- package/dist/multi-agent/graph.js +0 -84
- package/dist/multi-agent/helpers.js +0 -141
- package/dist/multi-agent/ids.js +0 -20
- package/dist/multi-agent/paths.js +0 -22
- package/dist/multi-agent-eval/normalize.js +0 -51
- package/dist/multi-agent-eval.js +0 -678
- package/dist/multi-agent-host.js +0 -777
- package/dist/multi-agent.js +0 -984
- package/dist/node-projection.js +0 -59
- package/dist/node-snapshot.js +0 -260
- package/dist/operator-ux.js +0 -631
- package/dist/orchestrator/app-operations.js +0 -211
- package/dist/orchestrator/audit-operations.js +0 -182
- package/dist/orchestrator/candidate-operations.js +0 -117
- package/dist/orchestrator/cli-options.js +0 -294
- package/dist/orchestrator/collaboration-operations.js +0 -86
- package/dist/orchestrator/feedback-operations.js +0 -81
- package/dist/orchestrator/host-operations.js +0 -78
- package/dist/orchestrator/lifecycle-operations.js +0 -626
- package/dist/orchestrator/migration-operations.js +0 -44
- package/dist/orchestrator/multi-agent-operations.js +0 -362
- package/dist/orchestrator/topology-operations.js +0 -84
- package/dist/orchestrator.js +0 -917
- package/dist/pipeline-runner.js +0 -285
- package/dist/reclamation/hash.js +0 -72
- package/dist/reclamation.js +0 -812
- package/dist/reporter.js +0 -67
- package/dist/run-export.js +0 -784
- package/dist/run-registry/derive.js +0 -175
- package/dist/run-registry/format.js +0 -124
- package/dist/run-registry/gc.js +0 -251
- package/dist/run-registry/policy.js +0 -16
- package/dist/run-registry/queue.js +0 -115
- package/dist/run-registry.js +0 -850
- package/dist/run-state-schema.js +0 -68
- package/dist/scheduling.js +0 -184
- package/dist/state-explosion.js +0 -1014
- package/dist/state.js +0 -367
- package/dist/telemetry-ledger.js +0 -196
- package/dist/topology.js +0 -565
- package/dist/triggers.js +0 -184
- package/dist/trust-audit.js +0 -644
- package/dist/types/blackboard.js +0 -2
- package/dist/types/candidate.js +0 -2
- package/dist/types/collaboration.js +0 -2
- package/dist/types/core.js +0 -2
- package/dist/types/drive.js +0 -10
- package/dist/types/error-feedback.js +0 -2
- package/dist/types/evidence-reasoning.js +0 -2
- package/dist/types/execution-backend.js +0 -2
- package/dist/types/multi-agent.js +0 -2
- package/dist/types/observability.js +0 -2
- package/dist/types/pipeline.js +0 -2
- package/dist/types/reclamation.js +0 -8
- package/dist/types/report-bundle.js +0 -6
- package/dist/types/result.js +0 -2
- package/dist/types/run-registry.js +0 -2
- package/dist/types/run.js +0 -2
- package/dist/types/sandbox.js +0 -2
- package/dist/types/schedule.js +0 -2
- package/dist/types/state-node.js +0 -2
- package/dist/types/topology.js +0 -2
- package/dist/types/trust.js +0 -2
- package/dist/types/workbench.js +0 -2
- package/dist/types/worker.js +0 -2
- package/dist/types/workflow-app.js +0 -2
- package/dist/types.js +0 -44
- package/dist/util/fingerprint.js +0 -19
- package/dist/util/fingerprint.test.js +0 -27
- package/dist/verifier.js +0 -78
- package/dist/version.js +0 -8
- package/dist/workbench-host.js +0 -182
- package/dist/workbench.js +0 -192
- package/dist/worker-accept/acceptance.js +0 -114
- package/dist/worker-accept/blackboard-fanout.js +0 -80
- package/dist/worker-accept/blackboard-linkage.js +0 -19
- package/dist/worker-accept/context.js +0 -2
- package/dist/worker-accept/telemetry-ledger.js +0 -126
- package/dist/worker-accept/validation.js +0 -77
- package/dist/worker-accept/verifier-completion.js +0 -73
- package/dist/worker-isolation/helpers.js +0 -51
- package/dist/worker-isolation/paths.js +0 -46
- package/dist/worker-isolation.js +0 -656
- package/dist/workflow-api.js +0 -131
- /package/dist/{types → core/types}/boundary.js +0 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// core/state/state-explosion/report.ts — buildStateExplosionReport, the
|
|
3
|
+
// operator-digest builder, and maybeCompactRun's PURE decision half.
|
|
4
|
+
//
|
|
5
|
+
// MILESTONE 4. Byte-exact port of the old build's `buildStateExplosion
|
|
6
|
+
// Report`/`buildOperatorDigestWithContext` (src/state-explosion.ts) minus
|
|
7
|
+
// the disk I/O — persistence (`refreshStateExplosionSummaries`'s writes,
|
|
8
|
+
// `loadStateExplosionSummaryIndex`'s read) is shell/state-explosion-
|
|
9
|
+
// cli.ts's job per v2/PLAN.md's core/shell split. This file only builds
|
|
10
|
+
// the report VALUE from an in-memory run + an already-loaded index.
|
|
11
|
+
//
|
|
12
|
+
// See size.ts/graph.ts/digest.ts's own header notes on why `operatorDigest`
|
|
13
|
+
// carries truthfully-empty `failures`/`evidenceDigest`/`trustDigest` at
|
|
14
|
+
// this milestone (no multi-agent/trust records exist yet — milestone 9).
|
|
15
|
+
//
|
|
16
|
+
// Evidence: SPEC/state-core.md "buildStateExplosionReport(...)",
|
|
17
|
+
// "maybeCompactRun(...) — best-effort ... ALL errors silently caught".
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.computeStateSize = computeStateSize;
|
|
20
|
+
exports.buildOperatorDigest = buildOperatorDigest;
|
|
21
|
+
exports.buildStateExplosionReport = buildStateExplosionReport;
|
|
22
|
+
exports.shouldCompactRun = shouldCompactRun;
|
|
23
|
+
const hash_1 = require("../../hash");
|
|
24
|
+
const size_1 = require("./size");
|
|
25
|
+
const graph_1 = require("./graph");
|
|
26
|
+
const digest_1 = require("./digest");
|
|
27
|
+
const helpers_1 = require("./helpers");
|
|
28
|
+
/** `computeStateSize(run, thresholds?)` — builds the graph view via
|
|
29
|
+
* `runToGraphViewFromWorkflowRun` then delegates to
|
|
30
|
+
* `computeStateSizeWithGraph`. Lives here (not size.ts) so size.ts stays
|
|
31
|
+
* graph-builder-agnostic — see size.ts's own header note on this
|
|
32
|
+
* module-boundary choice. */
|
|
33
|
+
function computeStateSize(run, thresholds = size_1.DEFAULT_STATE_EXPLOSION_THRESHOLDS) {
|
|
34
|
+
return (0, size_1.computeStateSizeWithGraph)(run, thresholds, (0, graph_1.runToGraphViewFromWorkflowRun)(run));
|
|
35
|
+
}
|
|
36
|
+
function buildOperatorDigest(run, compact, blackboard, stateSize, now, operator) {
|
|
37
|
+
const hiddenSourceRecords = compact.syntheticNodes.map((syn) => ({
|
|
38
|
+
kind: syn.id.split(":summary:")[1] || syn.kind,
|
|
39
|
+
count: syn.collapsedNodeCount,
|
|
40
|
+
expansionCommand: syn.expansionCommand,
|
|
41
|
+
}));
|
|
42
|
+
const expansionCommands = (0, helpers_1.unique)([
|
|
43
|
+
`node scripts/cw.js multi-agent graph ${run.id} --view full --json`,
|
|
44
|
+
`node scripts/cw.js blackboard message list ${run.id} --topic <topic-id>`,
|
|
45
|
+
`node scripts/cw.js multi-agent graph ${run.id} --view critical-path`,
|
|
46
|
+
`node scripts/cw.js multi-agent failures ${run.id} --json`,
|
|
47
|
+
...compact.syntheticNodes.map((syn) => syn.expansionCommand),
|
|
48
|
+
]);
|
|
49
|
+
const evidence = operator?.evidence || [];
|
|
50
|
+
const adopted = evidence.filter((e) => e.status === "adopted");
|
|
51
|
+
const missing = evidence.filter((e) => e.status === "missing" || e.status === "pending" || e.status === "conflicting");
|
|
52
|
+
const rejected = evidence.filter((e) => e.status === "rejected");
|
|
53
|
+
return {
|
|
54
|
+
schemaVersion: size_1.STATE_EXPLOSION_SCHEMA_VERSION,
|
|
55
|
+
runId: run.id,
|
|
56
|
+
id: "operator-digest",
|
|
57
|
+
scope: "run",
|
|
58
|
+
sourceRecordIds: compact.sourceRecordIds,
|
|
59
|
+
sourceFingerprint: (0, hash_1.fingerprintStrings)([compact.sourceFingerprint, blackboard.sourceFingerprint, String(stateSize.total)]),
|
|
60
|
+
includedCount: compact.compactNodeCount,
|
|
61
|
+
omittedCount: compact.collapsedNodeCount,
|
|
62
|
+
importantRefs: compact.criticalPath,
|
|
63
|
+
evidenceRefs: (0, helpers_1.unique)(adopted.map((e) => e.ref || e.id)),
|
|
64
|
+
trustAuditEventRefs: (0, helpers_1.unique)(blackboard.trustAuditEventRefs),
|
|
65
|
+
generatedAt: now,
|
|
66
|
+
status: "valid",
|
|
67
|
+
deterministic: true,
|
|
68
|
+
// Prefer the operator summary's nextAction (it reflects the whole run's
|
|
69
|
+
// blocked/evidence state); fall back to the compact graph's own when no
|
|
70
|
+
// operator summary was threaded in.
|
|
71
|
+
nextAction: operator?.nextAction || compact.nextAction,
|
|
72
|
+
stateSize,
|
|
73
|
+
compactGraphRef: compact.id,
|
|
74
|
+
blackboardDigestRef: blackboard.id,
|
|
75
|
+
criticalPath: compact.criticalPath,
|
|
76
|
+
failures: (operator?.failures || []).map((f) => ({ id: f.id, kind: f.kind, status: f.status, reason: f.reason, nextCommand: f.nextCommand })),
|
|
77
|
+
evidenceDigest: {
|
|
78
|
+
adopted: adopted.length,
|
|
79
|
+
missing: missing.length,
|
|
80
|
+
rejected: rejected.length,
|
|
81
|
+
entries: [...adopted, ...missing].slice(0, 40).map((e) => ({
|
|
82
|
+
id: e.id,
|
|
83
|
+
label: `${e.ref || e.id} (${e.status})`,
|
|
84
|
+
status: e.status,
|
|
85
|
+
sourceIds: [e.sourceId || e.id].filter(Boolean),
|
|
86
|
+
evidenceRefs: [e.ref || e.id].filter(Boolean),
|
|
87
|
+
expansionCommand: `node scripts/cw.js multi-agent evidence ${run.id} --json`,
|
|
88
|
+
})),
|
|
89
|
+
},
|
|
90
|
+
trustDigest: {
|
|
91
|
+
events: operator?.trustEvents || 0,
|
|
92
|
+
policyViolations: blackboard.policyViolations.length,
|
|
93
|
+
judgeRationales: blackboard.judgeRationale.length,
|
|
94
|
+
entries: (0, helpers_1.unique)([...blackboard.policyViolations.map((p) => p.id), ...blackboard.judgeRationale.map((j) => j.id)]),
|
|
95
|
+
},
|
|
96
|
+
hiddenSourceRecords,
|
|
97
|
+
expansionCommands,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function currentEntryFingerprint(entry, records) {
|
|
101
|
+
if (entry.scope === "blackboard")
|
|
102
|
+
return records.blackboardDigest.sourceFingerprint;
|
|
103
|
+
if (entry.id.startsWith("graph-")) {
|
|
104
|
+
if (entry.id === records.compactGraph.id)
|
|
105
|
+
return records.compactGraph.sourceFingerprint;
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
if (entry.id === "operator-digest")
|
|
109
|
+
return records.operatorDigest.sourceFingerprint;
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
/** Builds the full `StateExplosionReport` VALUE (no disk I/O) from an
|
|
113
|
+
* in-memory run plus an already-loaded persisted index (or none).
|
|
114
|
+
*
|
|
115
|
+
* `options.graphView` lets a caller that already ran
|
|
116
|
+
* `runToGraphViewFromWorkflowRun(run)` this same tick (e.g.
|
|
117
|
+
* `refreshStateExplosionSummaries`) hand that value in, so this function
|
|
118
|
+
* does not build the graph view a second time. Mirrors the old build's
|
|
119
|
+
* `StateExplosionBuildContext` memoization — one graph build per refresh,
|
|
120
|
+
* not one per derived view. */
|
|
121
|
+
function buildStateExplosionReport(run, options = {}) {
|
|
122
|
+
const thresholds = options.thresholds || size_1.DEFAULT_STATE_EXPLOSION_THRESHOLDS;
|
|
123
|
+
const now = options.now || new Date().toISOString();
|
|
124
|
+
const graphView = options.graphView || (0, graph_1.runToGraphViewFromWorkflowRun)(run);
|
|
125
|
+
const stateSize = (0, size_1.computeStateSizeWithGraph)(run, thresholds, graphView);
|
|
126
|
+
const compactGraph = (0, graph_1.buildCompactGraphFromView)(run.id, graphView, "compact", { thresholds, now });
|
|
127
|
+
const criticalPathGraph = (0, graph_1.buildCompactGraphFromView)(run.id, graphView, "critical-path", { thresholds, now });
|
|
128
|
+
// `run.blackboard` is `unknown[]`-typed at this milestone (core/state/
|
|
129
|
+
// types.ts's MultiAgentState/BlackboardState header note — real record
|
|
130
|
+
// shapes land with milestone 9); this cast is the one bridge point
|
|
131
|
+
// between "genuinely empty today" and "typed once milestone 9 writes
|
|
132
|
+
// real records", matching digest.ts's own DigestTopic/Message/... shapes.
|
|
133
|
+
const blackboardDigest = (0, digest_1.summarizeBlackboardDigest)({
|
|
134
|
+
id: run.id,
|
|
135
|
+
blackboard: run.blackboard,
|
|
136
|
+
}, undefined, now);
|
|
137
|
+
const operatorDigest = buildOperatorDigest(run, compactGraph, blackboardDigest, stateSize, now, options.operator);
|
|
138
|
+
const currentFingerprint = (0, hash_1.fingerprintStrings)([
|
|
139
|
+
compactGraph.sourceFingerprint,
|
|
140
|
+
blackboardDigest.sourceFingerprint,
|
|
141
|
+
operatorDigest.sourceFingerprint,
|
|
142
|
+
String(stateSize.total),
|
|
143
|
+
]);
|
|
144
|
+
const persisted = options.index;
|
|
145
|
+
const staleScopes = [];
|
|
146
|
+
let status = persisted ? "valid" : "absent";
|
|
147
|
+
if (persisted) {
|
|
148
|
+
if (persisted.sourceFingerprint !== currentFingerprint)
|
|
149
|
+
status = "stale";
|
|
150
|
+
for (const entry of persisted.entries) {
|
|
151
|
+
const current = currentEntryFingerprint(entry, { compactGraph, blackboardDigest, operatorDigest });
|
|
152
|
+
if (current && current !== entry.sourceFingerprint)
|
|
153
|
+
staleScopes.push(`${entry.scope}:${entry.id}`);
|
|
154
|
+
}
|
|
155
|
+
if (staleScopes.length)
|
|
156
|
+
status = "stale";
|
|
157
|
+
}
|
|
158
|
+
const nextAction = status === "stale" || status === "absent" ? `node scripts/cw.js summary refresh ${run.id}` : operatorDigest.nextAction;
|
|
159
|
+
return {
|
|
160
|
+
schemaVersion: size_1.STATE_EXPLOSION_SCHEMA_VERSION,
|
|
161
|
+
runId: run.id,
|
|
162
|
+
generatedAt: now,
|
|
163
|
+
stateSize,
|
|
164
|
+
freshness: {
|
|
165
|
+
status,
|
|
166
|
+
persistedFingerprint: persisted?.sourceFingerprint,
|
|
167
|
+
currentFingerprint,
|
|
168
|
+
staleScopes: staleScopes.sort(),
|
|
169
|
+
},
|
|
170
|
+
index: persisted,
|
|
171
|
+
compactGraph,
|
|
172
|
+
criticalPathGraph,
|
|
173
|
+
blackboardDigest,
|
|
174
|
+
operatorDigest,
|
|
175
|
+
hiddenSourceRecords: operatorDigest.hiddenSourceRecords,
|
|
176
|
+
expansionCommands: operatorDigest.expansionCommands,
|
|
177
|
+
nextAction,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** The pure decision half of `maybeCompactRun`: true when the caller
|
|
181
|
+
* should refresh summaries. The actual refresh (disk write) + the "catch
|
|
182
|
+
* ALL errors" best-effort wrapper live in shell/state-explosion-cli.ts,
|
|
183
|
+
* since only that layer can fail on real disk I/O. */
|
|
184
|
+
function shouldCompactRun(run, thresholds = size_1.DEFAULT_STATE_EXPLOSION_THRESHOLDS) {
|
|
185
|
+
const graphView = (0, graph_1.runToGraphViewFromWorkflowRun)(run);
|
|
186
|
+
return (0, size_1.computeStateSizeWithGraph)(run, thresholds, graphView).compactionRecommended;
|
|
187
|
+
}
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// core/state/state-explosion/size.ts — computeStateSizeWithGraph,
|
|
3
|
+
// DEFAULT_STATE_EXPLOSION_THRESHOLDS.
|
|
4
|
+
//
|
|
5
|
+
// MILESTONE 4 (v2/PLAN.md build order, step 4). Byte-exact port of the old
|
|
6
|
+
// build's src/state-explosion/size.ts. The counts are the same 12
|
|
7
|
+
// categories the old build reads off `run.multiAgent`/`run.blackboard`
|
|
8
|
+
// (loose `unknown[]`-typed record arrays at this milestone — see core/
|
|
9
|
+
// state/types.ts's header note; their per-record shapes land with
|
|
10
|
+
// milestone 9), so this file needs no reshape once those arrays start
|
|
11
|
+
// carrying real records.
|
|
12
|
+
//
|
|
13
|
+
// `computeStateSize` (the old build's one-arg convenience wrapper that
|
|
14
|
+
// ALSO builds the graph) is NOT defined here — the old build's own module
|
|
15
|
+
// boundary already has size.ts call the graph builder from a DIFFERENT
|
|
16
|
+
// file (multi-agent-operator-ux.ts); this file mirrors that by staying
|
|
17
|
+
// graph-builder-agnostic (`computeStateSizeWithGraph` takes the graph
|
|
18
|
+
// view as a plain parameter) and core/state/state-explosion/report.ts
|
|
19
|
+
// (which already imports both this file and graph.ts) supplies the
|
|
20
|
+
// one-arg convenience wrapper instead, avoiding a size.ts <-> graph.ts
|
|
21
|
+
// import cycle.
|
|
22
|
+
//
|
|
23
|
+
// Evidence: SPEC/state-core.md "src/state-explosion.ts + src/state-
|
|
24
|
+
// explosion/* — derived summary layer", "computeStateSize(...)".
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.DEFAULT_STATE_EXPLOSION_THRESHOLDS = exports.STATE_EXPLOSION_SCHEMA_VERSION = void 0;
|
|
4
|
-
exports.computeStateSize = computeStateSize;
|
|
5
27
|
exports.computeStateSizeWithGraph = computeStateSizeWithGraph;
|
|
6
|
-
const multi_agent_operator_ux_1 = require("../multi-agent-operator-ux");
|
|
7
28
|
exports.STATE_EXPLOSION_SCHEMA_VERSION = 1;
|
|
8
29
|
exports.DEFAULT_STATE_EXPLOSION_THRESHOLDS = {
|
|
9
30
|
graphNodes: 40,
|
|
@@ -11,11 +32,8 @@ exports.DEFAULT_STATE_EXPLOSION_THRESHOLDS = {
|
|
|
11
32
|
blackboardMessages: 25,
|
|
12
33
|
blackboardRecords: 40,
|
|
13
34
|
collapseBucket: 6,
|
|
14
|
-
totalRecords: 80
|
|
35
|
+
totalRecords: 80,
|
|
15
36
|
};
|
|
16
|
-
function computeStateSize(run, thresholds = exports.DEFAULT_STATE_EXPLOSION_THRESHOLDS) {
|
|
17
|
-
return computeStateSizeWithGraph(run, thresholds, (0, multi_agent_operator_ux_1.buildMultiAgentOperatorGraph)(run));
|
|
18
|
-
}
|
|
19
37
|
function computeStateSizeWithGraph(run, thresholds, graph) {
|
|
20
38
|
const ma = run.multiAgent || { runs: [], roles: [], groups: [], memberships: [], fanouts: [], fanins: [] };
|
|
21
39
|
const bb = run.blackboard || { topics: [], messages: [], contexts: [], artifacts: [], snapshots: [], decisions: [] };
|
|
@@ -33,7 +51,7 @@ function computeStateSizeWithGraph(run, thresholds, graph) {
|
|
|
33
51
|
snapshots: (bb.snapshots || []).length,
|
|
34
52
|
decisions: (bb.decisions || []).length,
|
|
35
53
|
graphNodes: graph.nodes.length,
|
|
36
|
-
graphEdges: graph.edges.length
|
|
54
|
+
graphEdges: graph.edges.length,
|
|
37
55
|
};
|
|
38
56
|
const total = counts.multiAgentRuns +
|
|
39
57
|
counts.roles +
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
// core/state/state-node.ts — StateNode lifecycle and pipeline-contract gates.
|
|
3
|
+
//
|
|
4
|
+
// MILESTONE 3. Byte-exact port of the old build's src/state-node.ts, split
|
|
5
|
+
// into a PURE half (this file: create/transition/validate/link/record —
|
|
6
|
+
// everything that does not touch disk) and a shell half
|
|
7
|
+
// (shell/node-store.ts: writeRunNode, the only disk write). `appendRunNode`
|
|
8
|
+
// mutates `run.nodes` in place (pure data mutation, no fs) and then calls
|
|
9
|
+
// out to a caller-supplied persist function so this file itself never
|
|
10
|
+
// imports fs.
|
|
11
|
+
//
|
|
12
|
+
// Evidence: SPEC/state-core.md "src/state-node.ts — StateNode lifecycle and
|
|
13
|
+
// contract gates", "StateNode transition matrix", "Deterministic id
|
|
14
|
+
// fallback", "Contract gates" (v2/PLAN.md byte-compat item 7 — the double
|
|
15
|
+
// commit gate).
|
|
5
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
17
|
exports.PipelineContractError = exports.PIPELINE_CONTRACT_SCHEMA_VERSION = exports.STATE_NODE_SCHEMA_VERSION = void 0;
|
|
7
18
|
exports.createStateNode = createStateNode;
|
|
@@ -12,37 +23,36 @@ exports.recordNodeError = recordNodeError;
|
|
|
12
23
|
exports.linkStateNodes = linkStateNodes;
|
|
13
24
|
exports.appendRunNode = appendRunNode;
|
|
14
25
|
exports.upsertRunContract = upsertRunContract;
|
|
15
|
-
exports.writeRunNode = writeRunNode;
|
|
16
26
|
exports.artifactExists = artifactExists;
|
|
17
|
-
const
|
|
18
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
19
|
-
const state_1 = require("./state");
|
|
20
|
-
const execution_backend_1 = require("./execution-backend");
|
|
21
|
-
const telemetry_attestation_1 = require("./telemetry-attestation");
|
|
27
|
+
const hash_1 = require("../hash");
|
|
22
28
|
exports.STATE_NODE_SCHEMA_VERSION = 1;
|
|
23
29
|
exports.PIPELINE_CONTRACT_SCHEMA_VERSION = 1;
|
|
24
30
|
class PipelineContractError extends Error {
|
|
25
31
|
structured;
|
|
26
|
-
|
|
32
|
+
/** `error.at` wins when the caller already has one (e.g. re-wrapping a
|
|
33
|
+
* structured error); `now` is the explicit clock value for a fresh one;
|
|
34
|
+
* the real clock is read ONLY when neither is given, matching
|
|
35
|
+
* node-snapshot.ts's `options.now` fallback pattern. */
|
|
36
|
+
constructor(error, now) {
|
|
27
37
|
super(error.message);
|
|
28
38
|
this.name = "PipelineContractError";
|
|
29
|
-
this.structured = {
|
|
30
|
-
...error,
|
|
31
|
-
at: error.at || new Date().toISOString()
|
|
32
|
-
};
|
|
39
|
+
this.structured = { ...error, at: error.at || now || new Date().toISOString() };
|
|
33
40
|
}
|
|
34
41
|
}
|
|
35
42
|
exports.PipelineContractError = PipelineContractError;
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
/** `now` is the explicit clock value; the real clock is read ONLY when it
|
|
44
|
+
* is omitted, matching node-snapshot.ts's `options.now` fallback so a
|
|
45
|
+
* caller that always passes `now` gets byte-stable createdAt/updatedAt. */
|
|
46
|
+
function createStateNode(input, now) {
|
|
47
|
+
const at = now || new Date().toISOString();
|
|
38
48
|
return {
|
|
39
49
|
schemaVersion: exports.STATE_NODE_SCHEMA_VERSION,
|
|
40
50
|
id: input.id || createNodeId(input),
|
|
41
51
|
kind: input.kind,
|
|
42
52
|
status: input.status || "pending",
|
|
43
53
|
loopStage: input.loopStage,
|
|
44
|
-
createdAt:
|
|
45
|
-
updatedAt:
|
|
54
|
+
createdAt: at,
|
|
55
|
+
updatedAt: at,
|
|
46
56
|
inputs: input.inputs || {},
|
|
47
57
|
outputs: input.outputs || {},
|
|
48
58
|
artifacts: input.artifacts || [],
|
|
@@ -51,44 +61,34 @@ function createStateNode(input) {
|
|
|
51
61
|
parents: input.parents || [],
|
|
52
62
|
children: input.children || [],
|
|
53
63
|
contractId: input.contractId,
|
|
54
|
-
metadata: input.metadata
|
|
64
|
+
metadata: input.metadata,
|
|
55
65
|
};
|
|
56
66
|
}
|
|
57
|
-
|
|
67
|
+
/** The full transition matrix PLUS the second gate: `committed` is refused
|
|
68
|
+
* unless the node is already `verified` (checked AFTER the matrix, its own
|
|
69
|
+
* error code `commit-without-verifier`). An illegal transition throws
|
|
70
|
+
* BEFORE the node changes. */
|
|
71
|
+
function transitionStateNode(node, input, now) {
|
|
58
72
|
if (!isLegalTransition(node.status, input.status)) {
|
|
59
|
-
throw contractError("illegal-transition", `State node ${node.id} cannot transition from ${node.status} to ${input.status}`, {
|
|
60
|
-
nodeId: node.id,
|
|
61
|
-
details: {
|
|
62
|
-
from: node.status,
|
|
63
|
-
to: input.status
|
|
64
|
-
}
|
|
65
|
-
});
|
|
73
|
+
throw contractError("illegal-transition", `State node ${node.id} cannot transition from ${node.status} to ${input.status}`, { nodeId: node.id, details: { from: node.status, to: input.status } });
|
|
66
74
|
}
|
|
67
75
|
if (input.status === "committed" && node.status !== "verified") {
|
|
68
|
-
throw contractError("commit-without-verifier", `State node ${node.id} cannot be committed before it is verified`, {
|
|
69
|
-
nodeId: node.id,
|
|
70
|
-
details: {
|
|
71
|
-
from: node.status,
|
|
72
|
-
to: input.status
|
|
73
|
-
}
|
|
74
|
-
});
|
|
76
|
+
throw contractError("commit-without-verifier", `State node ${node.id} cannot be committed before it is verified`, { nodeId: node.id, details: { from: node.status, to: input.status } });
|
|
75
77
|
}
|
|
76
78
|
return {
|
|
77
79
|
...node,
|
|
78
80
|
status: input.status,
|
|
79
81
|
loopStage: input.loopStage || node.loopStage,
|
|
80
|
-
updatedAt: new Date().toISOString(),
|
|
82
|
+
updatedAt: now || new Date().toISOString(),
|
|
81
83
|
outputs: input.outputs ? { ...node.outputs, ...input.outputs } : node.outputs,
|
|
82
84
|
artifacts: input.artifacts ? mergeById(node.artifacts, input.artifacts) : node.artifacts,
|
|
83
85
|
evidence: input.evidence ? mergeById(node.evidence, input.evidence) : node.evidence,
|
|
84
|
-
metadata: input.metadata ? { ...(node.metadata || {}), ...input.metadata } : node.metadata
|
|
86
|
+
metadata: input.metadata ? { ...(node.metadata || {}), ...input.metadata } : node.metadata,
|
|
85
87
|
};
|
|
86
88
|
}
|
|
87
89
|
function validatePipelineContract(contract) {
|
|
88
90
|
if (contract.schemaVersion !== exports.PIPELINE_CONTRACT_SCHEMA_VERSION) {
|
|
89
|
-
throw contractError("invalid-contract-schema", `Pipeline contract ${contract.id || "(missing id)"} has unsupported schemaVersion`, {
|
|
90
|
-
details: { schemaVersion: contract.schemaVersion }
|
|
91
|
-
});
|
|
91
|
+
throw contractError("invalid-contract-schema", `Pipeline contract ${contract.id || "(missing id)"} has unsupported schemaVersion`, { details: { schemaVersion: contract.schemaVersion } });
|
|
92
92
|
}
|
|
93
93
|
if (!contract.id)
|
|
94
94
|
throw contractError("invalid-contract-id", "Pipeline contract id is required");
|
|
@@ -106,97 +106,81 @@ function validatePipelineContract(contract) {
|
|
|
106
106
|
}
|
|
107
107
|
if (contract.compatibility.minSchemaVersion > exports.STATE_NODE_SCHEMA_VERSION) {
|
|
108
108
|
throw contractError("incompatible-contract", `Pipeline contract ${contract.id} requires newer StateNode schema`, {
|
|
109
|
-
details: contract.compatibility
|
|
109
|
+
details: contract.compatibility,
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
}
|
|
113
|
-
function
|
|
113
|
+
/** `pathExists` defaults to a function that always returns `true` (no path
|
|
114
|
+
* ever "missing") ONLY when the caller genuinely has no filesystem to
|
|
115
|
+
* check against; every real caller (shell/) passes `fs.existsSync` so the
|
|
116
|
+
* `missing-artifact-path` gate behaves exactly like the old build. Kept as
|
|
117
|
+
* an explicit parameter (never a top-level `require("node:fs")`) so this
|
|
118
|
+
* stays a pure core/ module per v2/PLAN.md's core/shell split. */
|
|
119
|
+
function assertNodeSatisfiesContract(node, contract, stageId, pathExists = () => true) {
|
|
114
120
|
validatePipelineContract(contract);
|
|
115
121
|
const stage = contract.stages.find((candidate) => candidate.id === stageId);
|
|
116
122
|
if (!stage) {
|
|
117
|
-
throw contractError("unknown-contract-stage", `Pipeline contract ${contract.id} has no stage ${stageId}`, {
|
|
118
|
-
nodeId: node.id
|
|
119
|
-
});
|
|
123
|
+
throw contractError("unknown-contract-stage", `Pipeline contract ${contract.id} has no stage ${stageId}`, { nodeId: node.id });
|
|
120
124
|
}
|
|
121
125
|
if (!stage.acceptedInputKinds.includes(node.kind)) {
|
|
122
126
|
throw contractError("unexpected-node-kind", `Stage ${stage.id} does not accept node kind ${node.kind}`, {
|
|
123
127
|
nodeId: node.id,
|
|
124
|
-
details: { expected: stage.acceptedInputKinds, actual: node.kind }
|
|
128
|
+
details: { expected: stage.acceptedInputKinds, actual: node.kind },
|
|
125
129
|
});
|
|
126
130
|
}
|
|
127
131
|
if (!stage.acceptedInputStatuses.includes(node.status)) {
|
|
128
132
|
throw contractError("unexpected-node-status", `Stage ${stage.id} does not accept node status ${node.status}`, {
|
|
129
133
|
nodeId: node.id,
|
|
130
|
-
details: { expected: stage.acceptedInputStatuses, actual: node.status }
|
|
134
|
+
details: { expected: stage.acceptedInputStatuses, actual: node.status },
|
|
131
135
|
});
|
|
132
136
|
}
|
|
133
|
-
assertRequiredArtifacts(node, stage);
|
|
137
|
+
assertRequiredArtifacts(node, stage, pathExists);
|
|
134
138
|
assertRequiredEvidence(node, stage, contract);
|
|
135
139
|
assertVerifierGate(node, stage, contract);
|
|
136
140
|
}
|
|
137
|
-
function recordNodeError(node, error) {
|
|
141
|
+
function recordNodeError(node, error, now) {
|
|
142
|
+
const at = now || new Date().toISOString();
|
|
138
143
|
return {
|
|
139
144
|
...node,
|
|
140
145
|
status: "failed",
|
|
141
|
-
updatedAt:
|
|
142
|
-
errors: [
|
|
143
|
-
...node.errors,
|
|
144
|
-
{
|
|
145
|
-
...error,
|
|
146
|
-
at: error.at || new Date().toISOString(),
|
|
147
|
-
nodeId: error.nodeId || node.id
|
|
148
|
-
}
|
|
149
|
-
]
|
|
146
|
+
updatedAt: at,
|
|
147
|
+
errors: [...node.errors, { ...error, at: error.at || at, nodeId: error.nodeId || node.id }],
|
|
150
148
|
};
|
|
151
149
|
}
|
|
152
|
-
function linkStateNodes(parent, child) {
|
|
150
|
+
function linkStateNodes(parent, child, now) {
|
|
151
|
+
const at = now || new Date().toISOString();
|
|
153
152
|
return [
|
|
154
|
-
{
|
|
155
|
-
|
|
156
|
-
updatedAt: new Date().toISOString(),
|
|
157
|
-
children: unique([...parent.children, child.id])
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
...child,
|
|
161
|
-
updatedAt: new Date().toISOString(),
|
|
162
|
-
parents: unique([...child.parents, parent.id])
|
|
163
|
-
}
|
|
153
|
+
{ ...parent, updatedAt: at, children: unique([...parent.children, child.id]) },
|
|
154
|
+
{ ...child, updatedAt: at, parents: unique([...child.parents, parent.id]) },
|
|
164
155
|
];
|
|
165
156
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// for the common new-node case, and the resulting array — content AND order — is
|
|
172
|
-
// byte-identical to before (push appends at the end, replace keeps the slot), so
|
|
173
|
-
// the persisted state.json is unchanged. appendRunNode is the only writer of
|
|
174
|
-
// run.nodes, so the array reference stays stable across a run.
|
|
157
|
+
/** Upsert `node` into `run.nodes` IN PLACE (replace at the same index when
|
|
158
|
+
* the id exists, else push at the end — the array reference is stable).
|
|
159
|
+
* `persist` is a caller-supplied side effect (shell/node-store.ts's
|
|
160
|
+
* `writeRunNode`) so this pure module never imports fs itself. */
|
|
161
|
+
function appendRunNode(run, node, persist) {
|
|
175
162
|
const nodes = run.nodes || (run.nodes = []);
|
|
176
163
|
const index = nodes.findIndex((candidate) => candidate.id === node.id);
|
|
177
164
|
if (index >= 0)
|
|
178
165
|
nodes[index] = node;
|
|
179
166
|
else
|
|
180
167
|
nodes.push(node);
|
|
181
|
-
|
|
168
|
+
if (persist)
|
|
169
|
+
persist(run, node);
|
|
182
170
|
return node;
|
|
183
171
|
}
|
|
184
172
|
function upsertRunContract(run, contract) {
|
|
185
173
|
validatePipelineContract(contract);
|
|
186
174
|
const contracts = run.contracts || [];
|
|
187
175
|
const index = contracts.findIndex((candidate) => candidate.id === contract.id);
|
|
188
|
-
run.contracts =
|
|
189
|
-
index >= 0 ? contracts.map((candidate) => (candidate.id === contract.id ? contract : candidate)) : [...contracts, contract];
|
|
176
|
+
run.contracts = index >= 0 ? contracts.map((candidate) => (candidate.id === contract.id ? contract : candidate)) : [...contracts, contract];
|
|
190
177
|
return contract;
|
|
191
178
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
function artifactExists(artifact) {
|
|
199
|
-
return Boolean(artifact.path && node_fs_1.default.existsSync(artifact.path));
|
|
179
|
+
/** `Boolean(artifact.path && <exists>)` — the existence check itself is
|
|
180
|
+
* supplied by the caller (shell) so this stays pure; callers pass
|
|
181
|
+
* `fs.existsSync` in practice. */
|
|
182
|
+
function artifactExists(artifact, exists) {
|
|
183
|
+
return Boolean(artifact.path && exists(artifact.path));
|
|
200
184
|
}
|
|
201
185
|
function validateStage(contract, stage, seen) {
|
|
202
186
|
if (!stage.id)
|
|
@@ -216,20 +200,20 @@ function validateStage(contract, stage, seen) {
|
|
|
216
200
|
throw contractError("invalid-contract-stage-output", `Stage ${stage.id} producedOutputKind is required`);
|
|
217
201
|
}
|
|
218
202
|
}
|
|
219
|
-
function assertRequiredArtifacts(node, stage) {
|
|
203
|
+
function assertRequiredArtifacts(node, stage, pathExists) {
|
|
220
204
|
for (const required of stage.requiredArtifacts || []) {
|
|
221
205
|
const artifact = node.artifacts.find((candidate) => candidate.id === required || candidate.kind === required);
|
|
222
206
|
if (!artifact) {
|
|
223
207
|
throw contractError("missing-required-artifact", `Node ${node.id} is missing required artifact ${required}`, {
|
|
224
208
|
nodeId: node.id,
|
|
225
|
-
details: { requiredArtifact: required }
|
|
209
|
+
details: { requiredArtifact: required },
|
|
226
210
|
});
|
|
227
211
|
}
|
|
228
|
-
if (!artifactExists(artifact)) {
|
|
212
|
+
if (!artifactExists(artifact, pathExists)) {
|
|
229
213
|
throw contractError("missing-artifact-path", `Node ${node.id} artifact ${artifact.id} path does not exist`, {
|
|
230
214
|
nodeId: node.id,
|
|
231
215
|
path: artifact.path,
|
|
232
|
-
details: { artifactId: artifact.id }
|
|
216
|
+
details: { artifactId: artifact.id },
|
|
233
217
|
});
|
|
234
218
|
}
|
|
235
219
|
}
|
|
@@ -240,7 +224,7 @@ function assertRequiredEvidence(node, stage, contract) {
|
|
|
240
224
|
if ((requiredEvidence.length || contractRequiresEvidence) && !node.evidence.length) {
|
|
241
225
|
throw contractError("missing-required-evidence", `Node ${node.id} is missing required evidence`, {
|
|
242
226
|
nodeId: node.id,
|
|
243
|
-
details: { requiredEvidence }
|
|
227
|
+
details: { requiredEvidence },
|
|
244
228
|
});
|
|
245
229
|
}
|
|
246
230
|
for (const required of requiredEvidence) {
|
|
@@ -248,7 +232,7 @@ function assertRequiredEvidence(node, stage, contract) {
|
|
|
248
232
|
if (!evidence) {
|
|
249
233
|
throw contractError("missing-required-evidence", `Node ${node.id} is missing required evidence ${required}`, {
|
|
250
234
|
nodeId: node.id,
|
|
251
|
-
details: { requiredEvidence: required }
|
|
235
|
+
details: { requiredEvidence: required },
|
|
252
236
|
});
|
|
253
237
|
}
|
|
254
238
|
}
|
|
@@ -262,15 +246,16 @@ function assertVerifierGate(node, stage, contract) {
|
|
|
262
246
|
if (!acceptedStatuses.includes(node.status)) {
|
|
263
247
|
throw contractError("verifier-gate-blocked", `Stage ${stage.id} requires verifier status ${acceptedStatuses.join(", ")}`, {
|
|
264
248
|
nodeId: node.id,
|
|
265
|
-
details: { actual: node.status, accepted: acceptedStatuses }
|
|
249
|
+
details: { actual: node.status, accepted: acceptedStatuses },
|
|
266
250
|
});
|
|
267
251
|
}
|
|
268
252
|
if ((gate?.requiredEvidence || contract.evidencePolicy?.requireEvidence) && !node.evidence.length) {
|
|
269
|
-
throw contractError("verifier-gate-missing-evidence", `Stage ${stage.id} requires evidence before commit`, {
|
|
270
|
-
nodeId: node.id
|
|
271
|
-
});
|
|
253
|
+
throw contractError("verifier-gate-missing-evidence", `Stage ${stage.id} requires evidence before commit`, { nodeId: node.id });
|
|
272
254
|
}
|
|
273
255
|
}
|
|
256
|
+
/** The transition matrix: `same -> same` is always legal (checked first via
|
|
257
|
+
* `from === to`), plus the table below. `committed` is terminal (empty
|
|
258
|
+
* array). */
|
|
274
259
|
function isLegalTransition(from, to) {
|
|
275
260
|
if (from === to)
|
|
276
261
|
return true;
|
|
@@ -282,31 +267,23 @@ function isLegalTransition(from, to) {
|
|
|
282
267
|
blocked: ["pending", "failed"],
|
|
283
268
|
verified: ["committed", "rejected"],
|
|
284
269
|
rejected: ["pending", "failed"],
|
|
285
|
-
committed: []
|
|
270
|
+
committed: [],
|
|
286
271
|
};
|
|
287
272
|
return allowed[from].includes(to);
|
|
288
273
|
}
|
|
289
274
|
function contractError(code, message, options = {}) {
|
|
290
|
-
return new PipelineContractError({
|
|
291
|
-
code,
|
|
292
|
-
message,
|
|
293
|
-
...options
|
|
294
|
-
});
|
|
275
|
+
return new PipelineContractError({ code, message, ...options });
|
|
295
276
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
// minted without an id. We bind the id to a short sha256 of the node's stable
|
|
300
|
-
// content (kind + loopStage + canonical inputs/outputs/contract), so the same
|
|
301
|
-
// logical node yields a byte-identical id across runs and replay reaches the same
|
|
302
|
-
// fingerprint. Two nodes with identical content collapse to the same id by design.
|
|
277
|
+
/** Deterministic id fallback: no wall-clock, no random. A node minted
|
|
278
|
+
* without an explicit id gets `"<kind>-" + <first 16 hex of sha256(...)>`.
|
|
279
|
+
* Two nodes with the same content collapse to ONE id by design. */
|
|
303
280
|
function createNodeId(input) {
|
|
304
|
-
const digest = (0,
|
|
281
|
+
const digest = (0, hash_1.sha256)((0, hash_1.stableStringify)({
|
|
305
282
|
kind: input.kind,
|
|
306
283
|
loopStage: input.loopStage,
|
|
307
284
|
contractId: input.contractId ?? null,
|
|
308
285
|
inputs: input.inputs ?? null,
|
|
309
|
-
outputs: input.outputs ?? null
|
|
286
|
+
outputs: input.outputs ?? null,
|
|
310
287
|
}));
|
|
311
288
|
return `${input.kind}-${digest.replace("sha256:", "").slice(0, 16)}`;
|
|
312
289
|
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// core/state/types.ts — the state-kernel's own type set.
|
|
3
|
+
//
|
|
4
|
+
// MILESTONE 3 (v2/PLAN.md build order, step 3). Scoped to exactly what
|
|
5
|
+
// state-core.md's public surface needs: WorkflowRun and everything it
|
|
6
|
+
// carries, StateNode + its snapshot/replay/diff family, the pipeline
|
|
7
|
+
// contract shape, and the small persisted-record shapes validation.ts
|
|
8
|
+
// guards. Later milestones (multi-agent, blackboard, topology, worker,
|
|
9
|
+
// candidate, trust) add their OWN richer types under their own module —
|
|
10
|
+
// this file only carries the FIELD SET this milestone's schema/migration/
|
|
11
|
+
// node-lifecycle/snapshot code actually reads or writes, ported from the
|
|
12
|
+
// old build's src/types/*.ts. Optional subsystem state (multiAgent,
|
|
13
|
+
// blackboard, topologies, collaboration, workers, etc.) is typed loosely
|
|
14
|
+
// here (its own schemaVersion + array keys only) since normalizeRunState
|
|
15
|
+
// only ever fills defaults/shape-checks those fields at this milestone;
|
|
16
|
+
// the richer per-record shapes land with their owning milestone.
|
|
17
|
+
//
|
|
18
|
+
// Pure types — no runtime cost, no imports beyond each other.
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|