@tea-agent/loop-agent 0.42.0-next.15 → 0.42.0-next.17
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/CHANGELOG.md +13 -0
- package/dist/application/task-lifecycle/advance.js +9 -4
- package/dist/build-stamp.json +3 -3
- package/dist/commands/task-source-prepare.js +3 -1
- package/dist/executors/dag-pi-executor.js +825 -65
- package/dist/executors/shell-executor.js +103 -35
- package/dist/shared/dag-failure-category.js +6 -0
- package/dist/task/contract/apply.js +36 -2
- package/dist/task/source-prepare/parse-intent.js +7 -0
- package/dist/worker/console/chat/chat-event-store.js +4 -2
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CgXb0EVO.js → abnfDiagram-N423BO3Z-B0Q0nClf.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DN59MZqN.js → arc-DCPjC19G.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BUk3sWpn.js → architectureDiagram-T3A2C74G-CP5n9jmG.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-IH-cPBFE.js → blockDiagram-VBNYF7ZC-CmaWi_Wg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BJWf1nGo.js → c4Diagram-5PPSVZJV-Ct5tcMfZ.js} +1 -1
- package/dist/worker/console/static/assets/channel-DAS07MdS.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BCPb5H0y.js → chunk-2GRJ4B5K-C0osm1Zf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-Cfs3jeW2.js → chunk-2Q5K7J3B-CDWviED7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-BhQI1B_T.js → chunk-5RXB4S5H-mtSk1-j7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-Cc_m4gyV.js → chunk-5VM5RSS4-D5J9PHC7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-JPqDS3IV.js → chunk-6Q2QTUOP-Begg4WAa.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-AeGX6EAg.js → chunk-GF5L2VYU-sF1AEy7T.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-pEyTjekV.js → chunk-JWPE2WC7-BqbsWXZb.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Bi5UopKd.js → chunk-KBJHAD2P-BKJrPcJ6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-FkGFJxgQ.js → chunk-RYQCIY6F-CcwNMRho.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-D-7LGrV2.js → chunk-XXDRQBXY-BEJi3iIs.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cwk-5jiW.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CCEDNDaX.js → cose-bilkent-JH36ORCC-DRLgTvVu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DDd-t7fm.js → cynefin-VYW2F7L2-BoAVG3cs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-LWe4Ikzr.js → cynefinDiagram-MW4NZA55-Bzol06hR.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BBCZScc-.js → dagre-VZM6K2ZE-UmliJM77.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B6liRVig.js → diagram-7IWD3JNH-BWLcJkFo.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-DO_hIrpW.js → diagram-B4RE2ZJO-CgApOm-O.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-dmCy93uR.js → diagram-LBJQPF4R-Bh3RgTLs.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6mFTIxP.js → diagram-Q27KOJAE-Dsh-D5nE.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-DCteVUfY.js → diagram-UB23O5K3-Dkbbpcpb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DTv530VK.js → ebnfDiagram-BXEA7PRR-3EBaA3t3.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DCXDMxCr.js → erDiagram-JOGREHBK-Bq2Dc5ok.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-BwEBLOJh.js → flowDiagram-UKHOOZJN-De7y55ha.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Ck-Vymjm.js → ganttDiagram-PKOTCBZU-fLiDbQRh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Hqs6X_3L.js → gitGraphDiagram-DS77QQ5N-Du617mp1.js} +1 -1
- package/dist/worker/console/static/assets/{index-BmMi-Bve.js → index-C0O48S_P.js} +50 -50
- package/dist/worker/console/static/assets/index-CzKf4U8P.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Cjc6M9eg.js → infoDiagram-6WML65LV-BwmlF-XP.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-CPchYZMl.js → ishikawaDiagram-WSZJBQD7-zsGtS59u.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BABdJNNC.js → journeyDiagram-NVQOT4AX-DTTPTe3f.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-1oXhbM4j.js → kanban-definition-27J2QSJJ-BkXg9aP-.js} +1 -1
- package/dist/worker/console/static/assets/{linear-PTmQ9LkV.js → linear-7U2ue5IE.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-B6Lxil0V.js → mermaid.core-BUuGHmWO.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BUqjNGEw.js → mindmap-definition-FAOFIHXS-7q6fX0Sv.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-8D1N-orJ.js → pegDiagram-VL7TDLO6-CkQPJN55.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BlPMN9d9.js → pieDiagram-7S7Q4E2Y-CCal_tfX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-BIL5YDes.js → quadrantDiagram-CIZ2JOQS-B7bpxyAZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Dqmil3ie.js → railroadDiagram-AXF67PYL-CJmww7D-.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C_-Dp4mH.js → requirementDiagram-LRYGKXZP-D6ldWJEv.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C0s5-bQS.js → sankeyDiagram-W5VNT64P-VGh33I9e.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DiVCHPfi.js → sequenceDiagram-SI44F4Z6-CCkDrIjU.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-CxQ9WVvx.js → sizeCapture-X5ZJPWSS-NC8f5Otb.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-D9mFHA-Y.js → stateDiagram-OKZ733FA-_92ezZdF.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DyJg5gzW.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DDnC5l-M.js → swimlanes-SLNWSIFB-BdUCGtbP.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B1cw_0Uy.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-C8QySPC-.js → timeline-definition-Z64GVDOM-CdD1H8ia.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BeYdDiz6.js → vennDiagram-T6HMQDX7-Dkdk3oFo.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-D8hjFWLH.js → wardleyDiagram-T6FBY63Y-CKu2uPPY.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-ClqG_JGK.js → xychartDiagram-ELKLHX3M-ClqAnuG8.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/workflows/dag/dag-retry-schema.js +3 -0
- package/dist/workflows/dag/frontend-design-policy.js +1 -1
- package/dist/workflows/dag/frontend-risk.js +2 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +16 -1
- package/dist/workflows/dag/frontend-shape.js +16 -6
- package/dist/workflows/dag/frontend-test-execution-evidence.js +48 -0
- package/dist/workflows/dag/frontend-verification-trace.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +9 -10
- package/dist/workflows/dag/node-execution.js +124 -72
- package/dist/workflows/dag/rerun-feedback.js +1 -0
- package/dist/workflows/dag/rerun-plan.js +90 -4
- package/dist/workflows/dag/rerun-run.js +7 -0
- package/dist/workflows/dag/retry-policy.js +16 -10
- package/dist/workflows/dag/runner-exit-diagnostics.js +125 -0
- package/dist/workflows/dag/runner.js +67 -7
- package/dist/workflows/dag/structured-output-repair.js +4 -1
- package/dist/workflows/dag/validate.js +10 -8
- package/dist/workflows/dag/workspace-checkpoint.js +66 -0
- package/docs/templates/agent-dag.schema.json +2 -2
- package/package.json +1 -1
- package/skills/frontend-plan/SKILL.md +6 -1
- package/skills/frontend-plan/references/decision-contract.md +69 -18
- package/skills/frontend-plan/references/design-decisions.md +32 -0
- package/dist/worker/console/static/assets/channel-i1DjpDIw.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DgDRdE1V.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DgDRdE1V.js +0 -1
- package/dist/worker/console/static/assets/index-Dh0Gh4X0.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BqXQxfFC.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CXwWNRJl.js +0 -8
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { appendFileSync, mkdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { hostname } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
/**
|
|
5
|
+
* Runner exit-time diagnostics (silent-death forensics).
|
|
6
|
+
*
|
|
7
|
+
* A DAG runner can disappear without a crash report and without stderr (five
|
|
8
|
+
* reproductions during frontend recovery/long-plan runs, 2026-09-06): when a
|
|
9
|
+
* supervisor hard-kills the process or the event loop is wedged, no JS runs,
|
|
10
|
+
* so nothing is persisted and the run is only discoverable later as an
|
|
11
|
+
* orphaned RUNNING state with a frozen heartbeat. That leaves no evidence of
|
|
12
|
+
* WHERE the runner was when it died or whether the exit was even observable
|
|
13
|
+
* by Node.
|
|
14
|
+
*
|
|
15
|
+
* This module gives every abnormal death a durable trace next to state.json:
|
|
16
|
+
* - an `armed` line is written when the checkpoint starts executing;
|
|
17
|
+
* - a `process-exit` line is appended from the process `exit` hook with the
|
|
18
|
+
* exit code and a live state snapshot (status, terminalReason, RUNNING
|
|
19
|
+
* nodes, heartbeat staleness);
|
|
20
|
+
* - `uncaught-exception` / `unhandled-rejection` lines record the error
|
|
21
|
+
* before the process is allowed to crash exactly as it would have
|
|
22
|
+
* via uncaughtExceptionMonitor without changing the host's crash policy.
|
|
23
|
+
*
|
|
24
|
+
* On a clean terminal return the runner calls stop(), which removes every
|
|
25
|
+
* listener and deletes the journal, so completed runs carry no noise. A file
|
|
26
|
+
* left behind therefore means: diagnostics were armed, then the process went
|
|
27
|
+
* away through a path Node could not observe (`armed` line only → external
|
|
28
|
+
* SIGKILL/OOM/event-loop wedge) or through an observed abnormal exit (`armed`
|
|
29
|
+
* + one or more event lines → the reason is recorded in the file).
|
|
30
|
+
*
|
|
31
|
+
* Journal writes are strictly synchronous and best-effort: an append failure
|
|
32
|
+
* must never alter the runner's own crash/exit behavior.
|
|
33
|
+
*/
|
|
34
|
+
export const RUNNER_EXIT_DIAGNOSTICS_FILE = "runner-exit-diagnostics.jsonl";
|
|
35
|
+
function describeError(error) {
|
|
36
|
+
if (error instanceof Error) {
|
|
37
|
+
return { name: error.name, message: error.message };
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
return { message: String(error) };
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return { message: "<unstringifiable error>" };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function appendJournalLineSync(journalPath, record) {
|
|
47
|
+
try {
|
|
48
|
+
mkdirSync(path.dirname(journalPath), { recursive: true });
|
|
49
|
+
appendFileSync(journalPath, `${JSON.stringify(record)}\n`, "utf8");
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Best-effort: journaling must never crash or alter exit behavior.
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function removeJournalSync(journalPath) {
|
|
56
|
+
try {
|
|
57
|
+
unlinkSync(journalPath);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Tolerate a missing journal (e.g. nothing was ever written).
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
export function installRunnerExitDiagnostics(options) {
|
|
64
|
+
const journalPath = path.join(options.runDir, RUNNER_EXIT_DIAGNOSTICS_FILE);
|
|
65
|
+
const target = options.target ?? process;
|
|
66
|
+
const armedAt = Date.now();
|
|
67
|
+
let stopped = false;
|
|
68
|
+
const base = {
|
|
69
|
+
runId: options.runId,
|
|
70
|
+
title: options.title,
|
|
71
|
+
pid: process.pid,
|
|
72
|
+
hostname: hostname(),
|
|
73
|
+
};
|
|
74
|
+
const safeSnapshot = () => {
|
|
75
|
+
try {
|
|
76
|
+
return options.snapshot();
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { status: "<snapshot-error>", running: [] };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const record = (kind, extra) => ({
|
|
83
|
+
ts: new Date().toISOString(),
|
|
84
|
+
kind,
|
|
85
|
+
...base,
|
|
86
|
+
...(extra ?? {}),
|
|
87
|
+
state: safeSnapshot(),
|
|
88
|
+
});
|
|
89
|
+
appendJournalLineSync(journalPath, {
|
|
90
|
+
...record("armed"),
|
|
91
|
+
armedAt: new Date(armedAt).toISOString(),
|
|
92
|
+
});
|
|
93
|
+
const onProcessExit = (code) => {
|
|
94
|
+
if (stopped)
|
|
95
|
+
return;
|
|
96
|
+
appendJournalLineSync(journalPath, record("process-exit", {
|
|
97
|
+
code: typeof code === "number" ? code : process.exitCode ?? 0,
|
|
98
|
+
elapsedMs: Date.now() - armedAt,
|
|
99
|
+
}));
|
|
100
|
+
};
|
|
101
|
+
const onUncaughtException = (error, origin) => {
|
|
102
|
+
if (stopped)
|
|
103
|
+
return;
|
|
104
|
+
appendJournalLineSync(journalPath, record(origin === "unhandledRejection" ? "unhandled-rejection" : "uncaught-exception", {
|
|
105
|
+
error: describeError(error),
|
|
106
|
+
elapsedMs: Date.now() - armedAt,
|
|
107
|
+
}));
|
|
108
|
+
// Monitor listeners neither suppress fatal errors nor force a crash
|
|
109
|
+
// when the host has its own handlers or a nonfatal rejection policy.
|
|
110
|
+
};
|
|
111
|
+
target.on("exit", onProcessExit);
|
|
112
|
+
target.on("uncaughtExceptionMonitor", onUncaughtException);
|
|
113
|
+
return {
|
|
114
|
+
journalPath,
|
|
115
|
+
stop: () => {
|
|
116
|
+
if (stopped)
|
|
117
|
+
return;
|
|
118
|
+
stopped = true;
|
|
119
|
+
target.removeListener("exit", onProcessExit);
|
|
120
|
+
target.removeListener("uncaughtExceptionMonitor", onUncaughtException);
|
|
121
|
+
// Clean completion: leave completed runs free of diagnostics noise.
|
|
122
|
+
removeJournalSync(journalPath);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
@@ -32,11 +32,13 @@ import { assertFrontendTopologyBinding, buildFrontendTopologyBinding, computeFro
|
|
|
32
32
|
import { ensureFrontendShapeCapsuleRecord } from "./frontend-shape-capsule-store.js";
|
|
33
33
|
import { parseDagSpec, resolveModelForTask, } from "./types.js";
|
|
34
34
|
import { computeActionPayloadHash, } from "../../infrastructure/console/operation-store.js";
|
|
35
|
+
import { classifyFailureOwner, failureOwnerSchema } from "./frontend-recovery-capsule.js";
|
|
35
36
|
import { acquireDagRecoveryLease, bindDagRecoveryLeaseExecution, claimDagRecoveryLease, } from "./recovery-lease.js";
|
|
36
37
|
import { executeDynamicCondition } from "./dynamic-runtime/condition.js";
|
|
37
38
|
import { executeDynamicLoopUntil } from "./dynamic-runtime/loop-until.js";
|
|
38
39
|
import { executeDynamicMapExpansion } from "./dynamic-runtime/map.js";
|
|
39
40
|
import { executeDynamicReduction } from "./dynamic-runtime/reduction.js";
|
|
41
|
+
import { installRunnerExitDiagnostics } from "./runner-exit-diagnostics.js";
|
|
40
42
|
export { buildNodePrompt, buildNodePromptWithResolvedSkillInstructions };
|
|
41
43
|
export async function loadDagSpecFromFile(dagPath) {
|
|
42
44
|
const raw = JSON.parse(await readFile(dagPath, "utf-8"));
|
|
@@ -602,6 +604,36 @@ async function executeDagCheckpoint(input) {
|
|
|
602
604
|
const onSigInt = () => shutdownOnSignal("SIGINT");
|
|
603
605
|
process.once("SIGTERM", onSigTerm);
|
|
604
606
|
process.once("SIGINT", onSigInt);
|
|
607
|
+
// Exit-time diagnostics (silent-death forensics): write an `armed` line at
|
|
608
|
+
// checkpoint start and append process-exit / uncaught-exception /
|
|
609
|
+
// unhandled-rejection lines with a live state snapshot so an abnormal death
|
|
610
|
+
// (supervisor hard kill, wedged event loop, crash with lost stderr) leaves
|
|
611
|
+
// a durable trace next to state.json. Removed on clean terminal return.
|
|
612
|
+
const exitDiagnostics = installRunnerExitDiagnostics({
|
|
613
|
+
runDir,
|
|
614
|
+
runId: state.runId,
|
|
615
|
+
title: state.title,
|
|
616
|
+
snapshot: () => {
|
|
617
|
+
const heartbeatAt = state.runner?.heartbeatAt;
|
|
618
|
+
return {
|
|
619
|
+
status: state.status,
|
|
620
|
+
terminalReason: state.terminalReason,
|
|
621
|
+
failureCategory: state.failureCategory,
|
|
622
|
+
heartbeatAt,
|
|
623
|
+
heartbeatAgeMs: heartbeatAt === undefined
|
|
624
|
+
? undefined
|
|
625
|
+
: Math.max(0, Date.now() - Date.parse(heartbeatAt)),
|
|
626
|
+
running: Object.values(state.nodes)
|
|
627
|
+
.filter((node) => node.status === "RUNNING")
|
|
628
|
+
.map((node) => ({
|
|
629
|
+
id: node.id,
|
|
630
|
+
attempt: node.currentAttempt,
|
|
631
|
+
startedAt: node.startedAt,
|
|
632
|
+
lastMeaningfulProgressAt: node.lastMeaningfulProgressAt,
|
|
633
|
+
})),
|
|
634
|
+
};
|
|
635
|
+
},
|
|
636
|
+
});
|
|
605
637
|
try {
|
|
606
638
|
const tasksById = new Map(spec.tasks.map((task) => [task.id, task]));
|
|
607
639
|
const baseExecuteNode = input.executeNode ??
|
|
@@ -938,6 +970,7 @@ async function executeDagCheckpoint(input) {
|
|
|
938
970
|
finally {
|
|
939
971
|
clearInterval(heartbeatTimer);
|
|
940
972
|
interruptWatcher?.stop();
|
|
973
|
+
exitDiagnostics.stop();
|
|
941
974
|
process.removeListener("SIGTERM", onSigTerm);
|
|
942
975
|
process.removeListener("SIGINT", onSigInt);
|
|
943
976
|
}
|
|
@@ -1296,13 +1329,40 @@ export async function finalizeTerminalRunStatus(state, taskCount, runDir, cwd, f
|
|
|
1296
1329
|
if (verifyFailed) {
|
|
1297
1330
|
const continuationCount = state.frontendRecoveryState?.continuationCount ?? 0;
|
|
1298
1331
|
if (continuationCount < frontendRecoveryQuota) {
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1332
|
+
let recoveryOwner = classifyFailureOwner({
|
|
1333
|
+
nodeId: "frontend-verify-shell",
|
|
1334
|
+
rawFailureCategory: state.nodes["frontend-verify-shell"]?.failureCategory,
|
|
1335
|
+
});
|
|
1336
|
+
try {
|
|
1337
|
+
const verifyFailure = JSON.parse(await readFile(path.join(runDir, "contracts/frontend-verify-failure.json"), "utf8"));
|
|
1338
|
+
const owner = failureOwnerSchema.safeParse(verifyFailure.failureOwner);
|
|
1339
|
+
if (verifyFailure.schemaVersion === 1 &&
|
|
1340
|
+
verifyFailure.schemaId === "frontend-verify-failure-v1" &&
|
|
1341
|
+
verifyFailure.nodeId === "frontend-verify-shell" &&
|
|
1342
|
+
verifyFailure.failureCategory === state.nodes["frontend-verify-shell"]?.failureCategory &&
|
|
1343
|
+
owner.success) {
|
|
1344
|
+
recoveryOwner = owner.data;
|
|
1345
|
+
}
|
|
1346
|
+
else {
|
|
1347
|
+
recoveryOwner = "unknown";
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
catch {
|
|
1351
|
+
// The node's classified category remains the fallback; never turn
|
|
1352
|
+
// unknown/environment/configuration failures into writer work.
|
|
1353
|
+
}
|
|
1354
|
+
if (recoveryOwner === "implementation")
|
|
1355
|
+
recoveryTrigger = {
|
|
1356
|
+
requestId: state.runId,
|
|
1357
|
+
failureSource: "frontend-verify-shell",
|
|
1358
|
+
failureOwner: recoveryOwner,
|
|
1359
|
+
protocolFailureReason: "frontend verification failure",
|
|
1360
|
+
recoveryMode: "continuation",
|
|
1361
|
+
};
|
|
1362
|
+
else
|
|
1363
|
+
state.terminalReason = recoveryOwner === "environment"
|
|
1364
|
+
? "frontend-verification-wait-external"
|
|
1365
|
+
: `frontend-verification-${recoveryOwner}-requires-contract-review`;
|
|
1306
1366
|
}
|
|
1307
1367
|
}
|
|
1308
1368
|
}
|
|
@@ -112,7 +112,10 @@ export function isStructuredRepairableFailureCategory(category) {
|
|
|
112
112
|
return (category === "invalid-output" ||
|
|
113
113
|
category === "structured-output-truncated" ||
|
|
114
114
|
category === "output-too-large" ||
|
|
115
|
-
category === "empty-output"
|
|
115
|
+
category === "empty-output" ||
|
|
116
|
+
// A length-truncated structured turn with a non-empty candidate artifact
|
|
117
|
+
// still repairs best through the frozen artifact repair prompt.
|
|
118
|
+
category === "output-limit");
|
|
116
119
|
}
|
|
117
120
|
export function formatStructuredArtifactPointer(input) {
|
|
118
121
|
return [
|
|
@@ -3,7 +3,7 @@ import { resolveShellCommands } from "../../executors/shell-executor.js";
|
|
|
3
3
|
import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
4
4
|
import { resolveRepairTaskForGate } from "./repair-artifact.js";
|
|
5
5
|
import { topoSortToRanks } from "./topo.js";
|
|
6
|
-
import { isSafeReadOnlyPiRetryCandidate, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, CONTEXT_OVERFLOW_RETRY_CATEGORY, } from "./retry-policy.js";
|
|
6
|
+
import { isSafeReadOnlyPiRetryCandidate, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, INCOMPLETE_WRITE_SET_RETRY_CATEGORY, OUTPUT_LIMIT_RETRY_CATEGORY, WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY, WRITER_EMPTY_DIFF_RETRY_CATEGORY, CONTEXT_OVERFLOW_RETRY_CATEGORY, } from "./retry-policy.js";
|
|
7
7
|
import { isBackendTestCompletenessRetryCandidate } from "./backend-test-writer-completeness.js";
|
|
8
8
|
import { defaultFrontendTestLayout, frontendTestLayoutFromSpec, } from "./frontend-test-layout.js";
|
|
9
9
|
const GOVERNANCE_WARNING_TYPES = new Set([
|
|
@@ -630,27 +630,28 @@ function validateRetryPolicyTaskConfig(task, issues) {
|
|
|
630
630
|
const allowed = new Set([
|
|
631
631
|
WRITER_EMPTY_DIFF_RETRY_CATEGORY,
|
|
632
632
|
INCOMPLETE_WRITE_SET_RETRY_CATEGORY,
|
|
633
|
+
OUTPUT_LIMIT_RETRY_CATEGORY,
|
|
633
634
|
]);
|
|
634
635
|
const unsupported = task.retryPolicy.retryCategories.filter((category) => !allowed.has(category));
|
|
635
636
|
if (task.retryPolicy.maxAttempts < 2 ||
|
|
636
|
-
task.retryPolicy.maxAttempts >
|
|
637
|
+
task.retryPolicy.maxAttempts > 5 ||
|
|
637
638
|
unsupported.length > 0 ||
|
|
638
639
|
!categories.has(WRITER_EMPTY_DIFF_RETRY_CATEGORY)) {
|
|
639
640
|
issues.push({
|
|
640
641
|
type: "invalid-retry-policy",
|
|
641
|
-
message: `task ${task.id} backend-test writer retryPolicy must use 2..
|
|
642
|
+
message: `task ${task.id} backend-test writer retryPolicy must use 2..5 total attempts and only ${WRITER_EMPTY_DIFF_RETRY_CATEGORY}, ${INCOMPLETE_WRITE_SET_RETRY_CATEGORY}, and/or ${OUTPUT_LIMIT_RETRY_CATEGORY}`,
|
|
642
643
|
});
|
|
643
644
|
}
|
|
644
645
|
return;
|
|
645
646
|
}
|
|
646
647
|
if (isWriterEmptyDiffCandidate &&
|
|
647
648
|
(task.retryPolicy.maxAttempts !== 2 ||
|
|
648
|
-
task.retryPolicy.retryCategories.
|
|
649
|
-
task.retryPolicy.retryCategories
|
|
650
|
-
|
|
649
|
+
!task.retryPolicy.retryCategories.includes(WRITER_EMPTY_DIFF_RETRY_CATEGORY) ||
|
|
650
|
+
task.retryPolicy.retryCategories.some((category) => category !== WRITER_EMPTY_DIFF_RETRY_CATEGORY &&
|
|
651
|
+
category !== OUTPUT_LIMIT_RETRY_CATEGORY))) {
|
|
651
652
|
issues.push({
|
|
652
653
|
type: "invalid-retry-policy",
|
|
653
|
-
message: `task ${task.id} writer retryPolicy must use exactly two total attempts and
|
|
654
|
+
message: `task ${task.id} writer retryPolicy must use exactly two total attempts and ${WRITER_EMPTY_DIFF_RETRY_CATEGORY}, with optional ${OUTPUT_LIMIT_RETRY_CATEGORY}`,
|
|
654
655
|
});
|
|
655
656
|
return;
|
|
656
657
|
}
|
|
@@ -659,6 +660,7 @@ function validateRetryPolicyTaskConfig(task, issues) {
|
|
|
659
660
|
const allowedWriterTransportCategories = new Set([
|
|
660
661
|
WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY,
|
|
661
662
|
CONTEXT_OVERFLOW_RETRY_CATEGORY,
|
|
663
|
+
OUTPUT_LIMIT_RETRY_CATEGORY,
|
|
662
664
|
]);
|
|
663
665
|
const allAllowed = [...categories].every((category) => allowedWriterTransportCategories.has(category));
|
|
664
666
|
if (task.retryPolicy.maxAttempts !== 2 ||
|
|
@@ -667,7 +669,7 @@ function validateRetryPolicyTaskConfig(task, issues) {
|
|
|
667
669
|
!categories.has(WRITER_CLEAN_TIMEOUT_RETRY_CATEGORY)) {
|
|
668
670
|
issues.push({
|
|
669
671
|
type: "invalid-retry-policy",
|
|
670
|
-
message: `task ${task.id} writer transport retryPolicy must use exactly two total attempts and only writer-clean-timeout (plus optional context-overflow)`,
|
|
672
|
+
message: `task ${task.id} writer transport retryPolicy must use exactly two total attempts and only writer-clean-timeout (plus optional context-overflow/output-limit)`,
|
|
671
673
|
});
|
|
672
674
|
}
|
|
673
675
|
}
|
|
@@ -184,3 +184,69 @@ export function workspaceFingerprintsMatch(left, right) {
|
|
|
184
184
|
return false;
|
|
185
185
|
return left.fingerprint === right.fingerprint;
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Path-level drift between two checkpoints: paths whose content hash,
|
|
189
|
+
* porcelain state, or presence differs (changed paths plus stable Task
|
|
190
|
+
* Contract governance inputs). The whole-tree fingerprint also moves on HEAD
|
|
191
|
+
* commits or porcelain churn that leaves every path's content untouched;
|
|
192
|
+
* only content-level path diffs are actionable for scoped rerun eligibility.
|
|
193
|
+
* Returns undefined when either checkpoint lacks a path inventory so callers
|
|
194
|
+
* fail closed to the strict whole-fingerprint comparison.
|
|
195
|
+
*/
|
|
196
|
+
export function workspaceDriftedPaths(parent, current) {
|
|
197
|
+
// changedPaths is only the porcelain inventory; it cannot explain files
|
|
198
|
+
// already committed between two different HEADs. Never localize that drift.
|
|
199
|
+
if (!parent.gitHeadSha ||
|
|
200
|
+
!current.gitHeadSha ||
|
|
201
|
+
parent.gitHeadSha !== current.gitHeadSha) {
|
|
202
|
+
return undefined;
|
|
203
|
+
}
|
|
204
|
+
if (!Array.isArray(parent.changedPaths) || !Array.isArray(current.changedPaths)) {
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
// Porcelain v1 records only the rename destination in this checkpoint
|
|
208
|
+
// schema, so the removed source path cannot be proven safe for imported
|
|
209
|
+
// readers. Keep scoped reuse fail-closed until both sides expose complete
|
|
210
|
+
// rename lineage.
|
|
211
|
+
if (parent.changedPaths.some((entry) => entry.state === "renamed") ||
|
|
212
|
+
current.changedPaths.some((entry) => entry.state === "renamed")) {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
if ((parent.stableGovernanceInputs === undefined) !==
|
|
216
|
+
(current.stableGovernanceInputs === undefined)) {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
const drifted = new Set();
|
|
220
|
+
const parentByPath = new Map(parent.changedPaths.map((entry) => [entry.path, entry]));
|
|
221
|
+
const currentByPath = new Map(current.changedPaths.map((entry) => [entry.path, entry]));
|
|
222
|
+
for (const [pathKey, parentEntry] of parentByPath) {
|
|
223
|
+
const currentEntry = currentByPath.get(pathKey);
|
|
224
|
+
if (!currentEntry) {
|
|
225
|
+
drifted.add(pathKey);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (parentEntry.contentSha256 !== currentEntry.contentSha256 ||
|
|
229
|
+
parentEntry.state !== currentEntry.state ||
|
|
230
|
+
parentEntry.mode !== currentEntry.mode) {
|
|
231
|
+
drifted.add(pathKey);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
for (const pathKey of currentByPath.keys()) {
|
|
235
|
+
if (!parentByPath.has(pathKey))
|
|
236
|
+
drifted.add(pathKey);
|
|
237
|
+
}
|
|
238
|
+
if (parent.stableGovernanceInputs && current.stableGovernanceInputs) {
|
|
239
|
+
const parentGov = new Map(parent.stableGovernanceInputs.map((entry) => [entry.path, entry.contentSha256]));
|
|
240
|
+
const currentGov = new Map(current.stableGovernanceInputs.map((entry) => [entry.path, entry.contentSha256]));
|
|
241
|
+
for (const [pathKey, hash] of parentGov) {
|
|
242
|
+
const other = currentGov.get(pathKey);
|
|
243
|
+
if (other === undefined || other !== hash)
|
|
244
|
+
drifted.add(pathKey);
|
|
245
|
+
}
|
|
246
|
+
for (const [pathKey, hash] of currentGov) {
|
|
247
|
+
if (parentGov.get(pathKey) !== hash)
|
|
248
|
+
drifted.add(pathKey);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return [...drifted];
|
|
252
|
+
}
|
|
@@ -466,9 +466,9 @@
|
|
|
466
466
|
"retryCategories": {
|
|
467
467
|
"type": "array",
|
|
468
468
|
"items": {
|
|
469
|
-
"enum": ["timeout", "network", "rate-limit", "unavailable", "empty-output", "output-too-large", "protocol-invalid", "invalid-output", "writer-empty-diff", "incomplete-write-set", "writer-clean-timeout", "read-burst", "context-overflow"]
|
|
469
|
+
"enum": ["timeout", "network", "rate-limit", "unavailable", "empty-output", "output-limit", "output-too-large", "protocol-invalid", "invalid-output", "writer-empty-diff", "incomplete-write-set", "writer-clean-timeout", "read-burst", "context-overflow"]
|
|
470
470
|
},
|
|
471
|
-
"default": ["timeout", "network", "rate-limit", "unavailable", "empty-output"],
|
|
471
|
+
"default": ["timeout", "network", "rate-limit", "unavailable", "empty-output", "output-limit"],
|
|
472
472
|
"description": "Failure categories eligible for retry. quota/auth/write-guard are never eligible. invalid-output is allowed only on explicit structured/protocol policies; writer-empty-diff and incomplete-write-set are reserved for requireChangedFiles generation/completeness writers; writer-clean-timeout is reserved for standard bounded writers after explicit zero-write-tool-call and empty-diff evidence."
|
|
473
473
|
}
|
|
474
474
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ description: Use only for the tool-only frontend decision planning node.
|
|
|
4
4
|
references:
|
|
5
5
|
- path: references/decision-contract.md
|
|
6
6
|
required: true
|
|
7
|
-
maxChars:
|
|
7
|
+
maxChars: 5600
|
|
8
8
|
- path: references/design-decisions.md
|
|
9
9
|
required: true
|
|
10
10
|
maxChars: 3400
|
|
@@ -29,6 +29,11 @@ cite `evidencePath` of an existing file; greenfield is `decision=new`.
|
|
|
29
29
|
Bind every file in the Contract-owned `requiredDeliverables` inventory. It records
|
|
30
30
|
explicit source obligations, not every path mentioned by a requirement.
|
|
31
31
|
|
|
32
|
+
Cross-slice citations must close in the same session: when a requirement cites a
|
|
33
|
+
verification target committed in another slice's session, re-commit that target
|
|
34
|
+
here with the same id and `replace=true` (union backfill) before finalize. A
|
|
35
|
+
`duplicate` receipt is a correction invitation, not a prohibition.
|
|
36
|
+
|
|
32
37
|
The runtime owns protected skeleton fields, path/command validation, materialization,
|
|
33
38
|
and final schema checks. Finish with exactly one `finalize_plan`; return no Markdown
|
|
34
39
|
narrative. Record a genuine evidence gap when frozen facts are insufficient.
|
|
@@ -16,11 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
### Behavior target shape (canonical example)
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
plan validation — fix the mapping, do not resubmit unchanged. State names must be
|
|
23
|
-
copied verbatim from the declared `uiStates`; never invent synonyms.
|
|
19
|
+
Each behavior target names the requirements it proves and declared UI states it
|
|
20
|
+
exercises. Empty `requirementIds`, or empty `uiStates` when any state/interaction
|
|
21
|
+
is declared, is rejected. Copy state names verbatim; never invent synonyms.
|
|
24
22
|
|
|
25
23
|
```json
|
|
26
24
|
{
|
|
@@ -36,13 +34,70 @@ the task's declared scope. Capabilities the task source lists as non-goals (e.g.
|
|
|
36
34
|
responsive/mobile layout, animations) must not be promised here, and themes or
|
|
37
35
|
CSS conventions must not be claimed without a Scout-confirmed repository fact.
|
|
38
36
|
|
|
37
|
+
### UI state declaration shape (canonical example)
|
|
38
|
+
|
|
39
|
+
Use the exact state names referenced by behavior targets. Each applicable state
|
|
40
|
+
carries behavior, implementation targets, and verification target ids:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"uiStates": [
|
|
45
|
+
{
|
|
46
|
+
"name": "disabled-minus",
|
|
47
|
+
"applicable": true,
|
|
48
|
+
"expectedBehavior": "minus button disabled at count 0",
|
|
49
|
+
"implementationTargets": ["src/components/SmokeCounter.tsx"],
|
|
50
|
+
"verificationTargetIds": ["VT-COUNTER-LOCAL-CONSTRAINTS"]
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
"interactions": [
|
|
54
|
+
{
|
|
55
|
+
"name": "refresh",
|
|
56
|
+
"trigger": "User clicks refresh",
|
|
57
|
+
"expectedBehavior": "reloads runs",
|
|
58
|
+
"implementationTargets": ["src/App.tsx"],
|
|
59
|
+
"verificationTargetIds": ["VT-SMOKE-COUNTER-BEHAVIOR"]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Implementation target coverage (canonical example)
|
|
66
|
+
|
|
67
|
+
`targets.files` is the union of every requirement's `implementationTargets`; list
|
|
68
|
+
each required component, integration point, and sibling source. Admission adds
|
|
69
|
+
non-static verification target files to the writer writeSet separately.
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"id": "AC-FE-001",
|
|
74
|
+
"implementationTargets": [
|
|
75
|
+
"src/components/SmokeCounter.tsx",
|
|
76
|
+
"src/App.tsx"
|
|
77
|
+
],
|
|
78
|
+
"verificationTargetIds": [
|
|
79
|
+
"VT-SMOKE-COUNTER-BEHAVIOR",
|
|
80
|
+
"VT-COUNTER-LOCAL-CONSTRAINTS"
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Bidirectional VT cross-reference invariant
|
|
86
|
+
|
|
87
|
+
Every requirement → VT citation must close in both directions: the cited VT's
|
|
88
|
+
`requirementIds` must list the citing requirement back. The checker reports the
|
|
89
|
+
missing direction; fix it in-node by re-submitting the corrected VT entry with
|
|
90
|
+
the same id and `replace=true`. `replace` is a full replacement: the latest
|
|
91
|
+
submission under an id becomes the authoritative entry (a subset re-submission
|
|
92
|
+
is the new rule). A `duplicate` receipt is a correction invitation, not a
|
|
93
|
+
prohibition, and a reverse-reference backfill is not "re-deriving committed
|
|
94
|
+
facts".
|
|
95
|
+
|
|
39
96
|
## UX vocabulary (registry first)
|
|
40
97
|
|
|
41
|
-
- Record `record_state_registry` BEFORE
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
rename of an already-recorded concept (kebab-case behavior-domain names, e.g.
|
|
45
|
-
`planner-task-edit`, `focus-queue-move`).
|
|
98
|
+
- Record `record_state_registry` BEFORE `record_state_flow`: one global vocabulary.
|
|
99
|
+
Coverage may slice by requirement; UX must not. Use stable kebab-case behavior
|
|
100
|
+
names, never AC-number-derived names or renamed committed concepts.
|
|
46
101
|
- UI states use the contract's declared authoritative ids (`declaredUiStates`
|
|
47
102
|
in the plan input) when the source declares a state table.
|
|
48
103
|
- Retry slices see `committedUx` in the plan input: reuse those exact names.
|
|
@@ -50,14 +105,10 @@ CSS conventions must not be claimed without a Scout-confirmed repository fact.
|
|
|
50
105
|
## Global decisions
|
|
51
106
|
|
|
52
107
|
Keep route/component placement, Mock/data strategy, and dependency/deviation policy
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
`covers` — never one row per interaction. `decision=reuse-existing` REQUIRES
|
|
58
|
-
`evidencePath` naming the existing repo file that proves the reuse (the runtime
|
|
59
|
-
verifies the file exists); a path that does not exist yet is greenfield and must
|
|
60
|
-
use `decision=new`.
|
|
108
|
+
global. Use only frozen command labels and Scout-confirmed paths.
|
|
109
|
+
|
|
110
|
+
One component choice may cover many states/interactions via `covers`.
|
|
111
|
+
`reuse-existing` requires an existing-file `evidencePath`; greenfield uses `new`.
|
|
61
112
|
|
|
62
113
|
For Mock/API, keep the real request path enabled by default. Select only a strategy
|
|
63
114
|
allowed by the frozen task capability. Active Mock requires deterministic Mock-backed
|
|
@@ -15,3 +15,35 @@ Contract. Do not search specification roots or nearby code from Plan.
|
|
|
15
15
|
responsive, and accessibility behavior; use not-applicable only with a reason.
|
|
16
16
|
- Record styling strategy, deviations, conflicts, and unresolved gaps explicitly.
|
|
17
17
|
Never silently replace a spec-defined component or claim unread evidence.
|
|
18
|
+
|
|
19
|
+
### Component choice shape (canonical example)
|
|
20
|
+
|
|
21
|
+
`purpose` must exactly match a declared UI state or interaction name — it is the
|
|
22
|
+
coverage key the deterministic policy checks. Do not invent synonyms, and never
|
|
23
|
+
swap a task-mandated component for another name without recording a deviation.
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
[
|
|
27
|
+
{
|
|
28
|
+
"purpose": "disabled-minus",
|
|
29
|
+
"component": "SmokeCounter",
|
|
30
|
+
"decision": "new",
|
|
31
|
+
"sourceRequirementIds": ["AC-FE-001"],
|
|
32
|
+
"sourceFragmentId": "REQ-SRC-AC-FE-001",
|
|
33
|
+
"rationale": "task mandates a new SmokeCounter card on the home surface"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"purpose": "refresh",
|
|
37
|
+
"component": "RunFilterBar",
|
|
38
|
+
"decision": "reuse-existing",
|
|
39
|
+
"rationale": "reuses the Scout-confirmed filter bar for the refresh interaction"
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For `decision: "new"`, pass `sourceRequirementIds` + `sourceFragmentId` and let
|
|
45
|
+
the runtime derive the exact task-source citation — never hand-write a
|
|
46
|
+
`specReference`. A `reuse-existing` choice covers behavioural interactions
|
|
47
|
+
without one row per interaction; each applicable visual UI state still needs its
|
|
48
|
+
own `purpose` match (or an explicitly scoped stylingStrategy covering the visual
|
|
49
|
+
dimension).
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ag as o,ah as n}from"./mermaid.core-B6Lxil0V.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-AeGX6EAg.js";import{_ as i}from"./mermaid.core-B6Lxil0V.js";import"./chunk-5VM5RSS4-Cc_m4gyV.js";import"./chunk-XXDRQBXY-D-7LGrV2.js";import"./chunk-KBJHAD2P-Bi5UopKd.js";import"./chunk-2GRJ4B5K-BCPb5H0y.js";import"./index-BmMi-Bve.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-AeGX6EAg.js";import{_ as i}from"./mermaid.core-B6Lxil0V.js";import"./chunk-5VM5RSS4-Cc_m4gyV.js";import"./chunk-XXDRQBXY-D-7LGrV2.js";import"./chunk-KBJHAD2P-Bi5UopKd.js";import"./chunk-2GRJ4B5K-BCPb5H0y.js";import"./index-BmMi-Bve.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
|