cool-workflow 0.1.98 → 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} +2 -2
- 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 +2 -0
- package/docs/cli-mcp-parity.7.md +280 -219
- package/docs/contract-migration-tooling.7.md +2 -0
- package/docs/control-plane-scheduling.7.md +2 -0
- package/docs/durable-state-and-locking.7.md +2 -0
- package/docs/evidence-adoption-reasoning-chain.7.md +2 -0
- package/docs/execution-backends.7.md +2 -0
- package/docs/multi-agent-cli-mcp-surface.7.md +2 -0
- package/docs/multi-agent-eval-replay-harness.7.md +2 -0
- package/docs/multi-agent-operator-ux.7.md +2 -0
- package/docs/node-snapshot-diff-replay.7.md +2 -0
- package/docs/observability-cost-accounting.7.md +2 -0
- package/docs/project-index.md +132 -71
- package/docs/real-execution-backends.7.md +2 -0
- package/docs/release-and-migration.7.md +2 -0
- package/docs/release-tooling.7.md +2 -0
- package/docs/run-registry-control-plane.7.md +2 -0
- package/docs/run-retention-reclamation.7.md +23 -0
- package/docs/state-explosion-management.7.md +2 -0
- package/docs/team-collaboration.7.md +2 -0
- package/docs/web-desktop-workbench.7.md +2 -0
- package/manifest/plugin.manifest.json +1 -1
- package/manifest/source-context-profiles.json +9 -13
- package/package.json +1 -1
- 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/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 +2 -2
- 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 -1189
- package/dist/capability-registry.js +0 -885
- package/dist/cli/command-surface.js +0 -494
- 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/ledger.js +0 -169
- 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 -864
- package/dist/error-feedback.js +0 -419
- package/dist/evidence-grounding.js +0 -184
- package/dist/execution-backend/agent.js +0 -354
- 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/ledger.js +0 -313
- package/dist/loop-expansion.js +0 -60
- package/dist/mcp/tool-call.js +0 -470
- package/dist/mcp/tool-definitions.js +0 -1066
- 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 -650
- 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 -925
- 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 -815
- 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 -192
- 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,527 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// core/state/state-explosion/graph.ts — buildCompactGraph + collapse rules.
|
|
3
|
+
//
|
|
4
|
+
// MILESTONE 4. Byte-exact port of the collapse-rule MACHINERY in the old
|
|
5
|
+
// build's src/state-explosion.ts (buildCompactGraph and everything it
|
|
6
|
+
// calls: collapseRuleFor, shouldCollapseKind, criticalPathNodeIds,
|
|
7
|
+
// bfsNeighborhood, filterByView, finalizeGraphRecord), PLUS a faithful
|
|
8
|
+
// port of `buildMultiAgentOperatorGraph`'s (src/multi-agent-operator-ux.ts)
|
|
9
|
+
// node/edge construction for every run-level array this milestone's state
|
|
10
|
+
// kernel actually carries: `tasks`, `dispatches`, `workers`,
|
|
11
|
+
// `candidates`/`candidateSelections`, `commits`, `feedback` (real fields
|
|
12
|
+
// on `WorkflowRun`, per core/state/types.ts — `candidates`/
|
|
13
|
+
// `candidateSelections`/`feedback`/`workers` are `unknown[]` there, so
|
|
14
|
+
// `runToGraphView` below defines the same minimal structural types for
|
|
15
|
+
// them that digest.ts uses for blackboard records: matching the old
|
|
16
|
+
// build's field usage exactly, degrading to empty when no milestone has
|
|
17
|
+
// written a record yet).
|
|
18
|
+
//
|
|
19
|
+
// DELIBERATE SCOPE CUT: the old build's `full` graph ALSO folds in
|
|
20
|
+
// `buildTopologyGraph`/`buildMultiAgentGraph`/`buildBlackboardGraph` (the
|
|
21
|
+
// topology/multi-agent/blackboard sub-graphs) and `deriveDependencies`'s
|
|
22
|
+
// edges — both are built entirely from `run.topologies`/`run.multiAgent`
|
|
23
|
+
// RECORD shapes that do not exist yet (milestone 9). `runToGraphView`
|
|
24
|
+
// leaves an explicit extension point (see its own comment) so milestone 9
|
|
25
|
+
// adds those sub-graphs and dependency edges additively, without this
|
|
26
|
+
// file's collapse-rule machinery changing at all.
|
|
27
|
+
//
|
|
28
|
+
// Evidence: SPEC/state-core.md "buildCompactGraph(...)", "State-explosion
|
|
29
|
+
// collapse rules"; v2/PLAN.md byte-compat item 9;
|
|
30
|
+
// src/multi-agent-operator-ux.ts:153-227 (buildMultiAgentOperatorGraph).
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.byId = exports.GRAPH_VIEWS = void 0;
|
|
33
|
+
exports.runToGraphView = runToGraphView;
|
|
34
|
+
exports.buildCompactGraph = buildCompactGraph;
|
|
35
|
+
exports.runToGraphViewFromWorkflowRun = runToGraphViewFromWorkflowRun;
|
|
36
|
+
exports.buildCompactGraphFromView = buildCompactGraphFromView;
|
|
37
|
+
const size_1 = require("./size");
|
|
38
|
+
const helpers_1 = require("./helpers");
|
|
39
|
+
Object.defineProperty(exports, "byId", { enumerable: true, get: function () { return helpers_1.byId; } });
|
|
40
|
+
const runtime_1 = require("../../multi-agent/runtime");
|
|
41
|
+
const coordinator_1 = require("../../multi-agent/coordinator");
|
|
42
|
+
const topology_1 = require("../../multi-agent/topology");
|
|
43
|
+
exports.GRAPH_VIEWS = [
|
|
44
|
+
"full",
|
|
45
|
+
"compact",
|
|
46
|
+
"critical-path",
|
|
47
|
+
"failures",
|
|
48
|
+
"evidence",
|
|
49
|
+
"trust",
|
|
50
|
+
"topology",
|
|
51
|
+
"blackboard",
|
|
52
|
+
"candidate",
|
|
53
|
+
"commit-gate",
|
|
54
|
+
];
|
|
55
|
+
/** Local re-import of `fingerprintStrings` kept file-scoped (not
|
|
56
|
+
* re-exported) since callers should import it from core/hash.ts or
|
|
57
|
+
* helpers.ts directly. */
|
|
58
|
+
const hash_1 = require("../../hash");
|
|
59
|
+
function scorePath(runId, candidateId, scoreId) {
|
|
60
|
+
return `${runId}/candidates/${candidateId}/scores/${scoreId}.json`;
|
|
61
|
+
}
|
|
62
|
+
/** Faithful port of `buildMultiAgentOperatorGraph`'s node/edge
|
|
63
|
+
* construction (src/multi-agent-operator-ux.ts:153-227), scoped to the
|
|
64
|
+
* run-level arrays this milestone's state kernel carries: `tasks`,
|
|
65
|
+
* `dispatches`, `workers`, `candidates`/`candidateSelections`, `commits`,
|
|
66
|
+
* `feedback`. The topology/multi-agent/blackboard sub-graphs
|
|
67
|
+
* (`buildTopologyGraph`/`buildMultiAgentGraph`/`buildBlackboardGraph`)
|
|
68
|
+
* and `deriveDependencies`'s edges are a milestone-9 concern (built
|
|
69
|
+
* entirely from `run.topologies`/`run.multiAgent` record shapes that do
|
|
70
|
+
* not exist yet) — a later milestone adds those nodes/edges into the
|
|
71
|
+
* same `addNode`/`addEdge` accumulators additively, without any other
|
|
72
|
+
* part of this file changing. */
|
|
73
|
+
function runToGraphView(run) {
|
|
74
|
+
const nodes = new Map();
|
|
75
|
+
const edges = [];
|
|
76
|
+
const addNode = (id, kind, status, label, pathValue) => {
|
|
77
|
+
if (!id)
|
|
78
|
+
return;
|
|
79
|
+
nodes.set(id, { id, kind, status, label, path: pathValue });
|
|
80
|
+
};
|
|
81
|
+
const addEdge = (from, to, label) => {
|
|
82
|
+
if (!from || !to)
|
|
83
|
+
return;
|
|
84
|
+
edges.push({ from, to, label });
|
|
85
|
+
};
|
|
86
|
+
addNode(`${run.id}:run`, "multi-agent-run-root", run.loopStage, run.id, run.paths.state);
|
|
87
|
+
for (const task of run.tasks || []) {
|
|
88
|
+
addNode(`${run.id}:task:${task.id}`, "task", task.status, task.id, task.taskPath);
|
|
89
|
+
addEdge(`${run.id}:run`, `${run.id}:task:${task.id}`, "owns");
|
|
90
|
+
addEdge(`${run.id}:task:${task.id}`, task.dispatchId ? `${run.id}:dispatch:${task.dispatchId}` : undefined, "dispatches");
|
|
91
|
+
addEdge(`${run.id}:task:${task.id}`, task.resultNodeId, "reports");
|
|
92
|
+
addEdge(`${run.id}:task:${task.id}`, task.verifierNodeId, "gates");
|
|
93
|
+
}
|
|
94
|
+
for (const dispatch of run.dispatches || []) {
|
|
95
|
+
addNode(`${run.id}:dispatch:${dispatch.id}`, "dispatch", "completed", dispatch.id, dispatch.manifestPath);
|
|
96
|
+
for (const workerId of dispatch.workerIds || [])
|
|
97
|
+
addEdge(`${run.id}:dispatch:${dispatch.id}`, `${run.id}:worker:${workerId}`, "dispatches");
|
|
98
|
+
}
|
|
99
|
+
for (const worker of run.workers || []) {
|
|
100
|
+
addNode(`${run.id}:worker:${worker.id}`, "worker", worker.status, worker.id, worker.inputPath);
|
|
101
|
+
addEdge(`${run.id}:worker:${worker.id}`, worker.resultNodeId, "reports");
|
|
102
|
+
addEdge(`${run.id}:worker:${worker.id}`, worker.output?.verifierNodeId, "gates");
|
|
103
|
+
for (const feedbackId of worker.feedbackIds || [])
|
|
104
|
+
addEdge(`${run.id}:worker:${worker.id}`, `${run.id}:feedback:${feedbackId}`, "blocks");
|
|
105
|
+
}
|
|
106
|
+
for (const candidate of run.candidates || []) {
|
|
107
|
+
const candidateId = `${run.id}:candidate:${candidate.id}`;
|
|
108
|
+
addNode(candidateId, "candidate", candidate.status, candidate.id, candidate.resultPath);
|
|
109
|
+
addEdge(candidate.workerId ? `${run.id}:worker:${candidate.workerId}` : candidate.resultNodeId, candidateId, "reports");
|
|
110
|
+
addEdge(candidate.verifierNodeId, candidateId, "gates");
|
|
111
|
+
for (const scoreId of candidate.scores || []) {
|
|
112
|
+
const nodeId = `${run.id}:score:${scoreId}`;
|
|
113
|
+
addNode(nodeId, "score", "completed", scoreId, scorePath(run.id, candidate.id, scoreId));
|
|
114
|
+
addEdge(candidateId, nodeId, "scores");
|
|
115
|
+
}
|
|
116
|
+
for (const feedbackId of candidate.feedbackIds || [])
|
|
117
|
+
addEdge(candidateId, `${run.id}:feedback:${feedbackId}`, "blocks");
|
|
118
|
+
}
|
|
119
|
+
for (const selection of run.candidateSelections || []) {
|
|
120
|
+
const nodeId = `${run.id}:selection:${selection.id}`;
|
|
121
|
+
addNode(nodeId, "selection", "accepted", selection.id, selection.rankingPath);
|
|
122
|
+
addEdge(`${run.id}:candidate:${selection.candidateId}`, nodeId, "selects");
|
|
123
|
+
if (selection.scoreId)
|
|
124
|
+
addEdge(`${run.id}:score:${selection.scoreId}`, nodeId, "selects");
|
|
125
|
+
addEdge(selection.verifierNodeId, nodeId, "gates");
|
|
126
|
+
}
|
|
127
|
+
for (const commit of run.commits || []) {
|
|
128
|
+
const nodeId = commit.stateNodeId || `${run.id}:commit:${commit.id}`;
|
|
129
|
+
addNode(nodeId, "commit", commit.verifierGated ? "committed" : "checkpoint", commit.id, commit.snapshotPath);
|
|
130
|
+
addEdge(commit.selectionId ? `${run.id}:selection:${commit.selectionId}` : undefined, nodeId, "commits");
|
|
131
|
+
addEdge(commit.verifierNodeId, nodeId, "gates");
|
|
132
|
+
}
|
|
133
|
+
for (const feedback of run.feedback || []) {
|
|
134
|
+
addNode(`${run.id}:feedback:${feedback.id}`, "feedback", feedback.status, `${feedback.severity} ${feedback.classification}`);
|
|
135
|
+
addEdge(feedback.nodeId, `${run.id}:feedback:${feedback.id}`, "blocks");
|
|
136
|
+
addEdge(feedback.taskId ? `${run.id}:task:${feedback.taskId}` : undefined, `${run.id}:feedback:${feedback.id}`, "blocks");
|
|
137
|
+
}
|
|
138
|
+
const edgeSeen = new Set();
|
|
139
|
+
const dedupedEdges = edges.filter((edge) => {
|
|
140
|
+
const key = `${edge.from}\0${edge.to}\0${edge.label || ""}`;
|
|
141
|
+
if (edgeSeen.has(key))
|
|
142
|
+
return false;
|
|
143
|
+
edgeSeen.add(key);
|
|
144
|
+
return true;
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
nodes: [...nodes.values()].sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id)),
|
|
148
|
+
edges: dedupedEdges.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || (a.label || "").localeCompare(b.label || "")),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/** `buildCompactGraph(run, view, options)` — builds the graph view via
|
|
152
|
+
* `runToGraphView`, then delegates to `buildCompactGraphFromView`. */
|
|
153
|
+
function buildCompactGraph(run, view = "compact", options = {}) {
|
|
154
|
+
return buildCompactGraphFromView(run.id, runToGraphView(run), view, options);
|
|
155
|
+
}
|
|
156
|
+
/** Adapts a real `WorkflowRun` (whose `workers`/`candidates`/
|
|
157
|
+
* `candidateSelections`/`feedback` are `unknown[]`-typed at this
|
|
158
|
+
* milestone — see core/state/types.ts's header note) into
|
|
159
|
+
* `RunToGraphViewInput`'s typed shape. The ONE cast point every shell/
|
|
160
|
+
* cli caller goes through, instead of repeating the cast at each call
|
|
161
|
+
* site; degrades to empty arrays today (genuinely the only value those
|
|
162
|
+
* fields can hold before their owning milestone writes real records) and
|
|
163
|
+
* needs no change once real records exist. */
|
|
164
|
+
function runToGraphViewFromWorkflowRun(run) {
|
|
165
|
+
const base = runToGraphView(run);
|
|
166
|
+
// Fold in the multi-agent, blackboard, and topology sub-graphs (the
|
|
167
|
+
// milestone-9 extension point promised in this file's header note). These
|
|
168
|
+
// add the high-volume, low-signal node kinds the collapse rules exist for
|
|
169
|
+
// (blackboard-message/context/snapshot, agent-membership/role) plus the
|
|
170
|
+
// multi-agent-run/group/fanout/fanin roots the critical path is keyed on.
|
|
171
|
+
// Byte-behavior port of the old build's runToGraphView, which merged the
|
|
172
|
+
// same three sub-graphs via summarizeMultiAgentOperator. Node `path` values
|
|
173
|
+
// are display-only (never affect counts/collapse), so the blackboard graph
|
|
174
|
+
// gets lightweight path stubs here.
|
|
175
|
+
const multiAgent = (0, runtime_1.buildMultiAgentGraph)(run);
|
|
176
|
+
const blackboardState = run.blackboard || (0, coordinator_1.emptyBlackboardState)();
|
|
177
|
+
const blackboard = (0, coordinator_1.buildBlackboardGraph)(run.id, blackboardState, (kind, id) => `${run.paths.runDir}/blackboard/${kind}/${id}.json`, `${run.paths.runDir}/blackboard/messages.jsonl`);
|
|
178
|
+
const topologyRuns = (run.topologies?.runs || []);
|
|
179
|
+
const topology = (0, topology_1.buildTopologyGraphFromRuns)(run.id, topologyRuns, (id) => `${run.paths.runDir}/topologies/${id}.json`);
|
|
180
|
+
const nodes = new Map();
|
|
181
|
+
for (const node of [...base.nodes, ...multiAgent.nodes, ...blackboard.nodes, ...topology.nodes]) {
|
|
182
|
+
if (!nodes.has(node.id))
|
|
183
|
+
nodes.set(node.id, node);
|
|
184
|
+
}
|
|
185
|
+
const edges = [];
|
|
186
|
+
const edgeSeen = new Set();
|
|
187
|
+
for (const edge of [...base.edges, ...multiAgent.edges, ...blackboard.edges, ...topology.edges]) {
|
|
188
|
+
const key = `${edge.from}\0${edge.to}\0${edge.label || ""}`;
|
|
189
|
+
if (edgeSeen.has(key))
|
|
190
|
+
continue;
|
|
191
|
+
edgeSeen.add(key);
|
|
192
|
+
edges.push(edge);
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
nodes: [...nodes.values()].sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id)),
|
|
196
|
+
edges: edges.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || (a.label || "").localeCompare(b.label || "")),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
function collapseRuleFor() {
|
|
200
|
+
return {
|
|
201
|
+
bucketBy: (node, parentOf) => {
|
|
202
|
+
switch (node.kind) {
|
|
203
|
+
case "blackboard-message":
|
|
204
|
+
return "messages";
|
|
205
|
+
case "blackboard-context":
|
|
206
|
+
return "contexts";
|
|
207
|
+
case "agent-membership": {
|
|
208
|
+
const parent = parentOf(node.id);
|
|
209
|
+
return `memberships:${parent ? parent.split(":").pop() : "unscoped"}`;
|
|
210
|
+
}
|
|
211
|
+
case "worker":
|
|
212
|
+
return "workers";
|
|
213
|
+
case "score":
|
|
214
|
+
return "scores";
|
|
215
|
+
case "blackboard-snapshot":
|
|
216
|
+
return "snapshots";
|
|
217
|
+
default:
|
|
218
|
+
return `${node.kind}`;
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
/** Collapsible kinds ONLY (v2/PLAN.md byte-compat item 9): high-volume,
|
|
224
|
+
* low-individual-signal. `decisions, artifacts, fanins, candidates,
|
|
225
|
+
* selections, commits, feedback` are NEVER collapsed so failures,
|
|
226
|
+
* evidence, policy, and judge rationale stay visible. */
|
|
227
|
+
function shouldCollapseKind(kind) {
|
|
228
|
+
return [
|
|
229
|
+
"blackboard-message",
|
|
230
|
+
"blackboard-context",
|
|
231
|
+
"agent-membership",
|
|
232
|
+
"worker",
|
|
233
|
+
"score",
|
|
234
|
+
"blackboard-snapshot",
|
|
235
|
+
"agent-role",
|
|
236
|
+
].includes(kind);
|
|
237
|
+
}
|
|
238
|
+
/** Critical-path node ids. The run root, plus the multi-agent-run/group/
|
|
239
|
+
* fanout/fanin roots, the candidate/selection reasoning chain, and every
|
|
240
|
+
* verifier-gated commit — derived from the merged graph's own node kinds
|
|
241
|
+
* (the sub-graphs `runToGraphViewFromWorkflowRun` now folds in). Byte-
|
|
242
|
+
* behavior port of the old build's criticalPathNodeIds. The two extension
|
|
243
|
+
* points (`reasoningCriticalIds`, `linkedFailureIds`) still feed in extra
|
|
244
|
+
* ids without reshaping this function. */
|
|
245
|
+
function criticalPathNodeIds(runId, options, nodes = []) {
|
|
246
|
+
const ids = [`${runId}:run`, ...(options.linkedFailureIds || [])];
|
|
247
|
+
for (const node of nodes) {
|
|
248
|
+
switch (node.kind) {
|
|
249
|
+
case "multi-agent-run":
|
|
250
|
+
case "agent-group":
|
|
251
|
+
case "agent-fanout":
|
|
252
|
+
case "agent-fanin":
|
|
253
|
+
case "candidate":
|
|
254
|
+
case "selection":
|
|
255
|
+
ids.push(node.id);
|
|
256
|
+
break;
|
|
257
|
+
case "commit":
|
|
258
|
+
if (node.status === "committed")
|
|
259
|
+
ids.push(node.id);
|
|
260
|
+
break;
|
|
261
|
+
default:
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return (0, helpers_1.unique)(ids);
|
|
266
|
+
}
|
|
267
|
+
function bfsNeighborhood(focus, nodes, edges, depth) {
|
|
268
|
+
const adjacency = new Map();
|
|
269
|
+
for (const edge of edges) {
|
|
270
|
+
if (!adjacency.has(edge.from))
|
|
271
|
+
adjacency.set(edge.from, new Set());
|
|
272
|
+
if (!adjacency.has(edge.to))
|
|
273
|
+
adjacency.set(edge.to, new Set());
|
|
274
|
+
adjacency.get(edge.from).add(edge.to);
|
|
275
|
+
adjacency.get(edge.to).add(edge.from);
|
|
276
|
+
}
|
|
277
|
+
const keep = new Set([focus]);
|
|
278
|
+
let frontier = new Set([focus]);
|
|
279
|
+
for (let level = 0; level < Math.max(0, depth); level += 1) {
|
|
280
|
+
const next = new Set();
|
|
281
|
+
for (const id of frontier) {
|
|
282
|
+
for (const neighbor of adjacency.get(id) || []) {
|
|
283
|
+
if (!keep.has(neighbor)) {
|
|
284
|
+
keep.add(neighbor);
|
|
285
|
+
next.add(neighbor);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
frontier = next;
|
|
290
|
+
}
|
|
291
|
+
return keep;
|
|
292
|
+
}
|
|
293
|
+
function expansionCommandFor(runId, key) {
|
|
294
|
+
if (key === "messages" || key.startsWith("thread"))
|
|
295
|
+
return `node scripts/cw.js blackboard message list ${runId}`;
|
|
296
|
+
if (key.startsWith("memberships"))
|
|
297
|
+
return `node scripts/cw.js multi-agent graph ${runId} --view full --json`;
|
|
298
|
+
return `node scripts/cw.js multi-agent graph ${runId} --view full --focus ${key} --json`;
|
|
299
|
+
}
|
|
300
|
+
function filterByView(runId, view, full, protectedIds) {
|
|
301
|
+
const keepKinds = (kinds) => {
|
|
302
|
+
const ids = new Set();
|
|
303
|
+
for (const node of full.nodes) {
|
|
304
|
+
if (kinds.includes(node.kind) || protectedIds.has(node.id))
|
|
305
|
+
ids.add(node.id);
|
|
306
|
+
}
|
|
307
|
+
return ids;
|
|
308
|
+
};
|
|
309
|
+
let ids;
|
|
310
|
+
switch (view) {
|
|
311
|
+
case "failures": {
|
|
312
|
+
ids = new Set();
|
|
313
|
+
for (const node of full.nodes)
|
|
314
|
+
if ((0, helpers_1.isProtectedStatus)(node.status))
|
|
315
|
+
ids.add(node.id);
|
|
316
|
+
ids.add(`${runId}:run`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case "evidence":
|
|
320
|
+
ids = keepKinds([
|
|
321
|
+
"multi-agent-run-root",
|
|
322
|
+
"blackboard",
|
|
323
|
+
"blackboard-topic",
|
|
324
|
+
"blackboard-artifact",
|
|
325
|
+
"blackboard-message",
|
|
326
|
+
"agent-membership",
|
|
327
|
+
"agent-fanin",
|
|
328
|
+
"candidate",
|
|
329
|
+
"selection",
|
|
330
|
+
"commit",
|
|
331
|
+
]);
|
|
332
|
+
break;
|
|
333
|
+
case "trust":
|
|
334
|
+
ids = keepKinds(["multi-agent-run-root", "blackboard", "coordinator-decision", "agent-fanin", "candidate", "selection", "commit"]);
|
|
335
|
+
break;
|
|
336
|
+
case "topology":
|
|
337
|
+
ids = keepKinds(["multi-agent-run-root", "topology", "multi-agent-run", "agent-group", "agent-role", "agent-fanout", "agent-fanin"]);
|
|
338
|
+
break;
|
|
339
|
+
case "blackboard":
|
|
340
|
+
ids = keepKinds([
|
|
341
|
+
"multi-agent-run-root",
|
|
342
|
+
"blackboard",
|
|
343
|
+
"blackboard-topic",
|
|
344
|
+
"blackboard-message",
|
|
345
|
+
"blackboard-context",
|
|
346
|
+
"blackboard-artifact",
|
|
347
|
+
"blackboard-snapshot",
|
|
348
|
+
"coordinator-decision",
|
|
349
|
+
]);
|
|
350
|
+
break;
|
|
351
|
+
case "candidate":
|
|
352
|
+
ids = keepKinds(["multi-agent-run-root", "candidate", "score", "selection", "worker", "agent-fanin"]);
|
|
353
|
+
break;
|
|
354
|
+
case "commit-gate":
|
|
355
|
+
ids = keepKinds(["multi-agent-run-root", "selection", "commit", "candidate", "agent-fanin"]);
|
|
356
|
+
break;
|
|
357
|
+
default:
|
|
358
|
+
ids = new Set(full.nodes.map((n) => n.id));
|
|
359
|
+
}
|
|
360
|
+
const nodes = full.nodes.filter((node) => ids.has(node.id));
|
|
361
|
+
const edges = full.edges.filter((edge) => ids.has(edge.from) && ids.has(edge.to));
|
|
362
|
+
return { nodes, edges };
|
|
363
|
+
}
|
|
364
|
+
function finalizeGraphRecord(runId, view, options, full, built) {
|
|
365
|
+
const collapsedNodeCount = built.syntheticNodes.reduce((acc, syn) => acc + syn.collapsedNodeCount, 0);
|
|
366
|
+
const collapsedEdgeCount = built.syntheticNodes.reduce((acc, syn) => acc + syn.collapsedEdgeCount, 0);
|
|
367
|
+
const blockedReasons = (0, helpers_1.unique)(built.syntheticNodes.filter((s) => s.blockedReason).map((s) => s.blockedReason));
|
|
368
|
+
return {
|
|
369
|
+
schemaVersion: 1,
|
|
370
|
+
runId,
|
|
371
|
+
id: `graph-${view}${options.focus ? `:focus:${(0, helpers_1.slug)(options.focus)}` : ""}`,
|
|
372
|
+
scope: "run",
|
|
373
|
+
view,
|
|
374
|
+
focus: options.focus,
|
|
375
|
+
depth: options.depth,
|
|
376
|
+
fullNodeCount: full.nodes.length,
|
|
377
|
+
fullEdgeCount: full.edges.length,
|
|
378
|
+
compactNodeCount: built.nodes.length,
|
|
379
|
+
compactEdgeCount: built.edges.length,
|
|
380
|
+
collapsedNodeCount,
|
|
381
|
+
collapsedEdgeCount,
|
|
382
|
+
syntheticNodes: built.syntheticNodes,
|
|
383
|
+
criticalPath: built.critical,
|
|
384
|
+
blockedReasons,
|
|
385
|
+
nodes: built.nodes,
|
|
386
|
+
edges: built.edges,
|
|
387
|
+
sourceRecordIds: full.nodes.map((n) => n.id).sort(),
|
|
388
|
+
sourceFingerprint: (0, hash_1.fingerprintStrings)(full.nodes.map((n) => `${n.id}:${n.status}`)),
|
|
389
|
+
includedCount: built.nodes.length,
|
|
390
|
+
omittedCount: collapsedNodeCount,
|
|
391
|
+
importantRefs: built.critical,
|
|
392
|
+
evidenceRefs: [],
|
|
393
|
+
trustAuditEventRefs: [],
|
|
394
|
+
generatedAt: options.now || new Date().toISOString(),
|
|
395
|
+
status: "valid",
|
|
396
|
+
deterministic: true,
|
|
397
|
+
nextAction: collapsedNodeCount > 0
|
|
398
|
+
? `node scripts/cw.js multi-agent graph ${runId} --view full --json`
|
|
399
|
+
: `node scripts/cw.js multi-agent graph ${runId} --view ${view} --json`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
/** The collapse-rule core, generic over any `{ nodes, edges }` graph view
|
|
403
|
+
* (see the file header on why this milestone feeds it the run's task/
|
|
404
|
+
* dispatch/worker/candidate/commit/feedback graph via `runToGraphView`,
|
|
405
|
+
* and why a later milestone's fuller graph — with topology/multi-agent/
|
|
406
|
+
* blackboard sub-graphs folded in — can reuse this unchanged). */
|
|
407
|
+
function buildCompactGraphFromView(runId, full, view = "compact", options = {}) {
|
|
408
|
+
const thresholds = options.thresholds || size_1.DEFAULT_STATE_EXPLOSION_THRESHOLDS;
|
|
409
|
+
const critical = criticalPathNodeIds(runId, options, full.nodes);
|
|
410
|
+
const protectedIds = new Set(critical);
|
|
411
|
+
// Failures, blocked, rejected, conflicting nodes are always preserved.
|
|
412
|
+
for (const node of full.nodes) {
|
|
413
|
+
if ((0, helpers_1.isProtectedStatus)(node.status))
|
|
414
|
+
protectedIds.add(node.id);
|
|
415
|
+
}
|
|
416
|
+
// Reasoning-critical nodes are on the critical path and must never be
|
|
417
|
+
// collapsed into a synthetic summary node (v2/PLAN.md byte-compat item 9).
|
|
418
|
+
for (const id of options.reasoningCriticalIds || [])
|
|
419
|
+
protectedIds.add(id);
|
|
420
|
+
const parents = (0, helpers_1.parentMap)(full.edges);
|
|
421
|
+
const parentOf = (id) => parents.get(id);
|
|
422
|
+
let scopeNodes = full.nodes;
|
|
423
|
+
let scopeEdges = full.edges;
|
|
424
|
+
if (view !== "full" && view !== "compact" && view !== "critical-path") {
|
|
425
|
+
const filtered = filterByView(runId, view, full, protectedIds);
|
|
426
|
+
scopeNodes = filtered.nodes;
|
|
427
|
+
scopeEdges = filtered.edges;
|
|
428
|
+
}
|
|
429
|
+
// Focus + depth: keep nodes within BFS depth of focus; collapse the rest.
|
|
430
|
+
let focusKeep;
|
|
431
|
+
if (options.focus) {
|
|
432
|
+
focusKeep = bfsNeighborhood(options.focus, scopeNodes, scopeEdges, options.depth ?? 1);
|
|
433
|
+
for (const id of focusKeep)
|
|
434
|
+
protectedIds.add(id);
|
|
435
|
+
}
|
|
436
|
+
const collapseEnabled = view === "compact" || view === "critical-path" || Boolean(options.focus);
|
|
437
|
+
if (view === "full" || !collapseEnabled) {
|
|
438
|
+
// No collapse: emit scoped graph verbatim (still records provenance +
|
|
439
|
+
// critical path).
|
|
440
|
+
return finalizeGraphRecord(runId, view, options, full, {
|
|
441
|
+
nodes: scopeNodes.map((node) => ({ ...node })),
|
|
442
|
+
edges: scopeEdges.map((edge) => ({ ...edge })),
|
|
443
|
+
syntheticNodes: [],
|
|
444
|
+
critical,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
// Determine collapse buckets per node.
|
|
448
|
+
const rule = collapseRuleFor();
|
|
449
|
+
const keep = new Set();
|
|
450
|
+
const buckets = new Map();
|
|
451
|
+
for (const node of scopeNodes) {
|
|
452
|
+
if (protectedIds.has(node.id) || (focusKeep && focusKeep.has(node.id))) {
|
|
453
|
+
keep.add(node.id);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (view === "critical-path") {
|
|
457
|
+
// Collapse everything not on the critical path into one bucket per kind.
|
|
458
|
+
const bucketKey = `critical-context:${node.kind}`;
|
|
459
|
+
buckets.set(bucketKey, [...(buckets.get(bucketKey) || []), node.id]);
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
if (!shouldCollapseKind(node.kind)) {
|
|
463
|
+
keep.add(node.id);
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
const bucketKey = rule.bucketBy(node, parentOf);
|
|
467
|
+
buckets.set(bucketKey, [...(buckets.get(bucketKey) || []), node.id]);
|
|
468
|
+
}
|
|
469
|
+
// Buckets smaller than the collapse threshold stay expanded (unless
|
|
470
|
+
// critical-path — v2/PLAN.md byte-compat item 9).
|
|
471
|
+
const synthetic = [];
|
|
472
|
+
const collapsedNodeIds = new Map(); // sourceNodeId -> syntheticId
|
|
473
|
+
for (const [bucketKey, ids] of [...buckets.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
474
|
+
if (view !== "critical-path" && ids.length < thresholds.collapseBucket) {
|
|
475
|
+
for (const id of ids)
|
|
476
|
+
keep.add(id);
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
const members = scopeNodes.filter((node) => ids.includes(node.id));
|
|
480
|
+
const internalEdges = scopeEdges.filter((edge) => ids.includes(edge.from) && ids.includes(edge.to));
|
|
481
|
+
const syntheticId = `${runId}:summary:${(0, helpers_1.slug)(bucketKey)}`;
|
|
482
|
+
const dominant = (0, helpers_1.dominantStatus)(members.map((m) => m.status));
|
|
483
|
+
const blocked = members.find((m) => (0, helpers_1.isProtectedStatus)(m.status));
|
|
484
|
+
synthetic.push({
|
|
485
|
+
id: syntheticId,
|
|
486
|
+
kind: "summary",
|
|
487
|
+
label: `${bucketKey} (${ids.length} collapsed)`,
|
|
488
|
+
status: dominant,
|
|
489
|
+
collapsedNodeCount: ids.length,
|
|
490
|
+
collapsedEdgeCount: internalEdges.length,
|
|
491
|
+
sourceIds: [...ids].sort(),
|
|
492
|
+
dominantStatus: dominant,
|
|
493
|
+
blockedReason: blocked ? `${blocked.kind} ${blocked.id} is ${blocked.status}` : undefined,
|
|
494
|
+
expansionCommand: expansionCommandFor(runId, bucketKey),
|
|
495
|
+
});
|
|
496
|
+
for (const id of ids)
|
|
497
|
+
collapsedNodeIds.set(id, syntheticId);
|
|
498
|
+
}
|
|
499
|
+
const redirect = (id) => collapsedNodeIds.get(id) || id;
|
|
500
|
+
const nodes = [];
|
|
501
|
+
for (const node of scopeNodes) {
|
|
502
|
+
if (keep.has(node.id))
|
|
503
|
+
nodes.push({ ...node });
|
|
504
|
+
}
|
|
505
|
+
for (const syn of synthetic) {
|
|
506
|
+
nodes.push({ id: syn.id, kind: "summary", label: syn.label, status: syn.status, synthetic: syn });
|
|
507
|
+
}
|
|
508
|
+
const edgeSeen = new Set();
|
|
509
|
+
const edges = [];
|
|
510
|
+
for (const edge of scopeEdges) {
|
|
511
|
+
const from = redirect(edge.from);
|
|
512
|
+
const to = redirect(edge.to);
|
|
513
|
+
if (from === to)
|
|
514
|
+
continue; // edge fully internal to a synthetic node
|
|
515
|
+
const edgeKey = `${from}\0${to}\0${edge.label || ""}`;
|
|
516
|
+
if (edgeSeen.has(edgeKey))
|
|
517
|
+
continue;
|
|
518
|
+
edgeSeen.add(edgeKey);
|
|
519
|
+
edges.push({ from, to, label: edge.label });
|
|
520
|
+
}
|
|
521
|
+
return finalizeGraphRecord(runId, view, options, full, {
|
|
522
|
+
nodes: nodes.sort((a, b) => a.kind.localeCompare(b.kind) || a.id.localeCompare(b.id)),
|
|
523
|
+
edges: edges.sort((a, b) => a.from.localeCompare(b.from) || a.to.localeCompare(b.to) || (a.label || "").localeCompare(b.label || "")),
|
|
524
|
+
syntheticNodes: synthetic.sort((a, b) => a.id.localeCompare(b.id)),
|
|
525
|
+
critical,
|
|
526
|
+
});
|
|
527
|
+
}
|
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// core/state/state-explosion/helpers.ts — pure, stateless helpers for the
|
|
3
|
+
// state-explosion derived-index layer.
|
|
4
|
+
//
|
|
5
|
+
// MILESTONE 4. Byte-exact port of the old build's
|
|
6
|
+
// src/state-explosion/helpers.ts. `fingerprintStrings`/`fingerprintRecords`
|
|
7
|
+
// live in core/hash.ts (the one hash module, per v2/PLAN.md's byte-compat
|
|
8
|
+
// item 2) and are re-exported here so every importer of this file keeps
|
|
9
|
+
// the same surface the old build had.
|
|
10
|
+
//
|
|
11
|
+
// BYTE-COMPAT ITEM 3 [load-bearing]: this file's `unique` DROPS falsy
|
|
12
|
+
// values AND SORTS its output. This is the kernel-side, SORTING variant —
|
|
13
|
+
// a later milestone's topology/candidate/host-decide code has its OWN
|
|
14
|
+
// separate `unique` (dedup-only, unsorted) that must NEVER be merged with
|
|
15
|
+
// this one; collapsing them changes persisted record order and eval
|
|
16
|
+
// parity (v2/PLAN.md byte-compat item 3, Open risk 2). See
|
|
17
|
+
// core/state/state-node.ts's own local `unique` for the sibling that must
|
|
18
|
+
// stay separate.
|
|
19
|
+
//
|
|
20
|
+
// Evidence: SPEC/state-core.md "Helpers (src/state-explosion/helpers.ts)".
|
|
2
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
22
|
exports.fingerprintStrings = exports.fingerprintRecords = void 0;
|
|
4
23
|
exports.isProtectedStatus = isProtectedStatus;
|
|
@@ -11,12 +30,16 @@ exports.unique = unique;
|
|
|
11
30
|
exports.byId = byId;
|
|
12
31
|
exports.truncate = truncate;
|
|
13
32
|
exports.slug = slug;
|
|
14
|
-
const
|
|
15
|
-
Object.defineProperty(exports, "fingerprintRecords", { enumerable: true, get: function () { return
|
|
16
|
-
Object.defineProperty(exports, "fingerprintStrings", { enumerable: true, get: function () { return
|
|
33
|
+
const hash_1 = require("../../hash");
|
|
34
|
+
Object.defineProperty(exports, "fingerprintRecords", { enumerable: true, get: function () { return hash_1.fingerprintRecords; } });
|
|
35
|
+
Object.defineProperty(exports, "fingerprintStrings", { enumerable: true, get: function () { return hash_1.fingerprintStrings; } });
|
|
36
|
+
/** True for `failed`, `blocked`, `rejected`, `conflicting` — the never-
|
|
37
|
+
* collapse status set (v2/PLAN.md byte-compat item 9). */
|
|
17
38
|
function isProtectedStatus(status) {
|
|
18
39
|
return ["failed", "blocked", "rejected", "conflicting"].includes(status);
|
|
19
40
|
}
|
|
41
|
+
/** Priority order `failed, blocked, rejected, conflicting, running,
|
|
42
|
+
* pending`; else the first status; else `"completed"`. */
|
|
20
43
|
function dominantStatus(statuses) {
|
|
21
44
|
for (const priority of ["failed", "blocked", "rejected", "conflicting", "running", "pending"]) {
|
|
22
45
|
if (statuses.includes(priority))
|
|
@@ -24,6 +47,8 @@ function dominantStatus(statuses) {
|
|
|
24
47
|
}
|
|
25
48
|
return statuses[0] || "completed";
|
|
26
49
|
}
|
|
50
|
+
/** First edge in wins — `Map<childId, parentId>` built by iterating edges
|
|
51
|
+
* in order and only setting an id the first time it is seen as a `to`. */
|
|
27
52
|
function parentMap(edges) {
|
|
28
53
|
const parents = new Map();
|
|
29
54
|
for (const edge of edges) {
|
|
@@ -32,6 +57,7 @@ function parentMap(edges) {
|
|
|
32
57
|
}
|
|
33
58
|
return parents;
|
|
34
59
|
}
|
|
60
|
+
/** Key-sorted `JSON.stringify` — used for deterministic eval lines. */
|
|
35
61
|
function stableLine(value) {
|
|
36
62
|
return JSON.stringify(sortKeys(value));
|
|
37
63
|
}
|
|
@@ -50,16 +76,21 @@ function sortKeys(value) {
|
|
|
50
76
|
function stripRunId(run, id) {
|
|
51
77
|
return id.startsWith(`${run.id}:`) ? id.slice(run.id.length + 1) : id;
|
|
52
78
|
}
|
|
79
|
+
/** DROPS falsy values, then sorts (default string sort). This is the
|
|
80
|
+
* kernel-side SORTING `unique` — see the file header's byte-compat note;
|
|
81
|
+
* never merge with an unsorted dedup-only sibling. */
|
|
53
82
|
function unique(values) {
|
|
54
83
|
return Array.from(new Set(values.filter(Boolean))).sort();
|
|
55
84
|
}
|
|
56
85
|
function byId(a, b) {
|
|
57
86
|
return a.id.localeCompare(b.id);
|
|
58
87
|
}
|
|
88
|
+
/** Whitespace-collapsed; over 80 chars becomes the first 77 chars + `...`. */
|
|
59
89
|
function truncate(value) {
|
|
60
90
|
const single = value.replace(/\s+/g, " ").trim();
|
|
61
91
|
return single.length > 80 ? `${single.slice(0, 77)}...` : single;
|
|
62
92
|
}
|
|
93
|
+
/** Chars outside `[a-zA-Z0-9._:-]` become `-`. */
|
|
63
94
|
function slug(value) {
|
|
64
95
|
return value.replace(/[^a-zA-Z0-9._:-]/g, "-");
|
|
65
96
|
}
|