@tea-agent/loop-agent 0.39.0-beta.2 → 0.39.0-beta.4
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/AGENTS.md +4 -1
- package/CHANGELOG.md +187 -92
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +16 -5
- package/dist/cli/program.js +16 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli.js +2 -1
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +429 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/executors/dag-pi-executor.js +1691 -29
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +51 -12
- package/dist/executors/pi-sdk-executor.js +180 -33
- package/dist/executors/shell-executor.js +1152 -171
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/shared/dag-prompt-override.js +28 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +32 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +41 -8
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +19 -5
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +60 -17
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +36 -0
- package/dist/worker/cli.js +31 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/routes.js +1 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/operation-store.js +226 -6
- package/dist/worker/console/operator-actions.js +263 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/routes.js +43 -2
- package/dist/worker/console/security.js +63 -9
- package/dist/worker/console/server.js +151 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-zsNyvGaH.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BDjZ5kE1.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-BkgaujnN.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-CqxOJi0j.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-C2JHLdRi.js} +1 -1
- package/dist/worker/console/static/assets/channel-BuqaGKAT.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-DF_YSvOS.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-D3Ort5Vo.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-XfAfZeUC.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-SLBibhf5.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-Dz29Ye_f.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-DB2G4H8V.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-CIoGkxdu.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-CG78FGkn.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-ZDHAvXPd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BKkkC4VA.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DNdSHjH9.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-SposUZsO.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-BSCtQpD7.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-DgFHrw9l.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-BVBasYbj.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-6r_4rN_c.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-JmuaB41P.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-xXvAY3sk.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-D2B3lY2o.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-tK54tMEX.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-CNtELzV-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-C1xKMKcW.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CZ7dlPMz.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BqGb42Xe.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-D_BlXEtt.js} +1 -1
- package/dist/worker/console/static/assets/{index-DTZOKgAn.js → index-D5zGX6fG.js} +68 -68
- package/dist/worker/console/static/assets/index-DBbhESQ_.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-Vce2vmIe.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-DDwVBAvI.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-y_Ew-wv4.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-CjXJFp1K.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-1itMst1Q.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-C-D3ZRUa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BiYywmZc.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-l2fydNWA.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-Dz0hNbYW.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-BujI_mLZ.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-pWe84beb.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-ByYu2frg.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-CoPnilFP.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-1Md2LnS0.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DqqqAoSH.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C4sGkf--.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8rmop_9.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-ByxJrUzW.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B0ky-kBM.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-D8MLgM0O.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DnBis-wZ.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-uuyYT7UO.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-BjgabhwK.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/node-input.js +74 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +36 -3
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +66 -0
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/format.js +23 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.js +4 -0
- package/dist/worker/observe/static/state.js +13 -2
- package/dist/worker/observe/static/styles.css +73 -0
- package/dist/worker/observe/static/views/dag-inspector.js +612 -8
- package/dist/worker/observe/static/views/dag.js +8 -1
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +20 -2
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/failure-category.js +12 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +366 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +378 -160
- package/dist/workflows/dag/frontend-prewrite-gate.js +343 -391
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +201 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +195 -8
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +665 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +425 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +26 -2
- package/dist/workflows/dag/frontend-test-case-manifest.js +5 -0
- package/dist/workflows/dag/frontend-test-case-quality.js +5 -13
- package/dist/workflows/dag/frontend-test-environment-probe.js +227 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +160 -28
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +68 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +439 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +97 -18
- package/dist/workflows/dag/frontend-writer-admission.js +260 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +572 -500
- package/dist/workflows/dag/node-execution.js +307 -70
- package/dist/workflows/dag/prompt.js +10 -18
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +58 -6
- package/dist/workflows/dag/retry-policy.js +228 -4
- package/dist/workflows/dag/runner.js +314 -119
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +235 -23
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +60 -10
- package/docs/init-surface.manifest.json +5 -0
- package/docs/operations/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +44 -0
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/harness.json +3 -3
- package/package.json +5 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-implementation/SKILL.md +4 -4
- package/skills/frontend-implementation/references/design-spec.md +11 -7
- package/skills/frontend-implementation/references/node-contracts.md +20 -11
- package/skills/loop-agent/references/command-reference.md +11 -4
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/skills/playwright-cli/SKILL.md +22 -0
- package/skills/playwright-cli-case-generator/SKILL.md +20 -1
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { DEFAULT_FRONTEND_SPEC_ROOTS, isFrontendSpecFilePath, isValidFrontendSpecRoot, OPENSPEC_SPEC_EXT_RE, } from "../../shared/openspec-spec.js";
|
|
3
3
|
import { campaignBudgetSchema, } from "../../application/evaluation/budget.js";
|
|
4
4
|
import { assertDagPromptSourceRule } from "./prompt-source.js";
|
|
5
|
-
import { dagRetryPolicySchema } from "./retry-policy.js";
|
|
5
|
+
import { dagRetryPolicySchema, TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE, } from "./retry-policy.js";
|
|
6
6
|
import { dagLivenessPolicySchema, } from "./liveness-policy.js";
|
|
7
7
|
import { dagOutputProtocolSchema } from "./output-protocol.js";
|
|
8
8
|
export const dagComplexitySchema = z.enum(["HIGH", "MED", "LOW"]);
|
|
@@ -10,6 +10,18 @@ export const dagNodeExecutorSchema = z.enum(["pi", "shell", "static"]);
|
|
|
10
10
|
export const CURSOR_DAG_EXECUTOR_REMOVED_ERROR = 'executor "cursor" is no longer supported; regenerate the DAG with Pi-only writers (implement-pi / repair-pi)';
|
|
11
11
|
export const CURSOR_EXECUTOR_MODELS_REMOVED_ERROR = "executorModels.cursor is no longer supported; use executorModels.pi only";
|
|
12
12
|
export const dagToolProfileSchema = z.enum(["read-only", "write"]);
|
|
13
|
+
/**
|
|
14
|
+
* Frozen Pi extension short ids for backend implementation DAGs (plan
|
|
15
|
+
* 2026-08-21 D2): the DAG carries only these ids; the executor resolves them
|
|
16
|
+
* against the Pi settings package inventory at run time. Adding a package
|
|
17
|
+
* requires changing this enum and the plan.
|
|
18
|
+
*/
|
|
19
|
+
export const dagPiExtensionIdSchema = z.enum(["pi-codegraph", "pi-lens"]);
|
|
20
|
+
/** Node-level explicit Pi extension allowlist (omitted = all off, R8.1). */
|
|
21
|
+
export const dagPiExtensionsSchema = z
|
|
22
|
+
.array(dagPiExtensionIdSchema)
|
|
23
|
+
.min(1)
|
|
24
|
+
.transform((ids) => Array.from(new Set(ids)));
|
|
13
25
|
/** Static command capabilities only; tasks cannot inject executables or shell prefixes. */
|
|
14
26
|
export const dagCommandCapabilitySchema = z.enum(["playwright-cli"]);
|
|
15
27
|
export const dagCommandPolicySchema = z.discriminatedUnion("mode", [
|
|
@@ -173,7 +185,16 @@ const dagFrontendNodeIdSchema = z
|
|
|
173
185
|
.regex(/^[a-z][a-z0-9-]*$/, "frontend node id must be kebab-case");
|
|
174
186
|
const openspecCandidatePathSchema = z
|
|
175
187
|
.string()
|
|
176
|
-
.refine((candidate) => !candidate.includes("\\") &&
|
|
188
|
+
.refine((candidate) => !candidate.includes("\\") &&
|
|
189
|
+
!candidate.startsWith("/") &&
|
|
190
|
+
!candidate.split("/").includes("..") &&
|
|
191
|
+
OPENSPEC_SPEC_EXT_RE.test(candidate), "openspec candidate must be a repo-relative supported specification file");
|
|
192
|
+
const openspecCandidateSnapshotSchema = z
|
|
193
|
+
.object({
|
|
194
|
+
path: openspecCandidatePathSchema,
|
|
195
|
+
sha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
196
|
+
})
|
|
197
|
+
.strict();
|
|
177
198
|
export const dagFrontendPrewriteGateSchema = z.object({
|
|
178
199
|
schemaVersion: z.literal(1),
|
|
179
200
|
planFromNodeId: dagFrontendNodeIdSchema,
|
|
@@ -192,7 +213,24 @@ export const dagFrontendPrewriteGateSchema = z.object({
|
|
|
192
213
|
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
193
214
|
requireSourceFreshness: z.literal(true),
|
|
194
215
|
implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
|
|
216
|
+
/**
|
|
217
|
+
* Generation-frozen roots that bound custom normative directories. Omitted
|
|
218
|
+
* legacy DAGs retain the canonical OpenSpec + ai_workspace roots.
|
|
219
|
+
*/
|
|
220
|
+
openspecSpecRoots: z
|
|
221
|
+
.array(z.string().refine((root) => isValidFrontendSpecRoot(root), "openspec spec root must be a normalized repo-relative directory"))
|
|
222
|
+
.optional(),
|
|
195
223
|
openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
|
|
224
|
+
/** Read-only selector output; omitted for scan-strict and legacy DAGs. */
|
|
225
|
+
openspecSelectionNodeId: dagFrontendNodeIdSchema.optional(),
|
|
226
|
+
/** Contract-declared and source-explicit paths that cannot be downgraded. */
|
|
227
|
+
openspecMandatoryPaths: z.array(openspecCandidatePathSchema).optional(),
|
|
228
|
+
/**
|
|
229
|
+
* Content fingerprints frozen with the generation-time OpenSpec scope. New
|
|
230
|
+
* DAGs use these to reject a plan whose cited specification changed after it
|
|
231
|
+
* was read; legacy DAGs without the field retain existence-only drift checks.
|
|
232
|
+
*/
|
|
233
|
+
openspecCandidateSnapshots: z.array(openspecCandidateSnapshotSchema).optional(),
|
|
196
234
|
/**
|
|
197
235
|
* Generation-frozen component/theme specification bucket. Derived from
|
|
198
236
|
* `designEvidence.classified.component ∪ theme ∪ rule.components` at DAG
|
|
@@ -237,14 +275,28 @@ export const dagFrontendPrewriteGateSchema = z.object({
|
|
|
237
275
|
scanStrict: z.array(openspecCandidatePathSchema).default([]),
|
|
238
276
|
})
|
|
239
277
|
.optional(),
|
|
278
|
+
}).superRefine((gate, context) => {
|
|
279
|
+
const roots = gate.openspecSpecRoots ?? DEFAULT_FRONTEND_SPEC_ROOTS;
|
|
280
|
+
const candidateGroups = [
|
|
281
|
+
["openspecCandidatePaths", gate.openspecCandidatePaths],
|
|
282
|
+
["openspecMandatoryPaths", gate.openspecMandatoryPaths],
|
|
283
|
+
["componentSpecCandidatePaths", gate.componentSpecCandidatePaths],
|
|
284
|
+
["openspecCandidateSources.declared", gate.openspecCandidateSources?.declared],
|
|
285
|
+
["openspecCandidateSources.taskSourceCited", gate.openspecCandidateSources?.taskSourceCited],
|
|
286
|
+
["openspecCandidateSources.scanStrict", gate.openspecCandidateSources?.scanStrict],
|
|
287
|
+
];
|
|
288
|
+
for (const [field, paths] of candidateGroups) {
|
|
289
|
+
for (const [index, candidate] of (paths ?? []).entries()) {
|
|
290
|
+
if (!isFrontendSpecFilePath(candidate, roots)) {
|
|
291
|
+
context.addIssue({
|
|
292
|
+
code: z.ZodIssueCode.custom,
|
|
293
|
+
path: [...field.split("."), index],
|
|
294
|
+
message: "openspec candidate must be a supported file under the generation-frozen openspec spec roots",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
240
299
|
});
|
|
241
|
-
export const dagFrontendLintBaselineSchema = z
|
|
242
|
-
.object({
|
|
243
|
-
schemaVersion: z.literal(1),
|
|
244
|
-
lintCommands: z.array(z.string().min(1)).min(1),
|
|
245
|
-
lintEvidence: dagShellVerifyEvidenceSchema,
|
|
246
|
-
})
|
|
247
|
-
.strict();
|
|
248
300
|
export const dagFrontendVerificationBundleSchema = z
|
|
249
301
|
.object({
|
|
250
302
|
schemaVersion: z.literal(1),
|
|
@@ -258,7 +310,9 @@ export const dagFrontendVerificationBundleSchema = z
|
|
|
258
310
|
behaviorEvidence: dagShellVerifyEvidenceSchema,
|
|
259
311
|
lintBaselineNodeId: dagFrontendNodeIdSchema.optional(),
|
|
260
312
|
writerNodeIds: z.array(dagFrontendNodeIdSchema).optional(),
|
|
261
|
-
|
|
313
|
+
/** M8: verify-shell only ever runs in initial mode; a failure is terminal
|
|
314
|
+
* (routes to recovery) and never selects a same-run repair branch. */
|
|
315
|
+
mode: z.literal("initial"),
|
|
262
316
|
})
|
|
263
317
|
.superRefine((bundle, context) => {
|
|
264
318
|
const groups = [
|
|
@@ -333,6 +387,135 @@ export const dagFrontendReviewContextSchema = z.object({
|
|
|
333
387
|
schemaVersion: z.literal(1),
|
|
334
388
|
requireBaseline: z.literal(true),
|
|
335
389
|
});
|
|
390
|
+
/**
|
|
391
|
+
* Deterministic frontend contract-import shell (micro topology). Re-validates
|
|
392
|
+
* the generation-frozen managed Contract's source/binding/schema freshness and
|
|
393
|
+
* emits a digest. It never invokes a model and never rewrites requirements —
|
|
394
|
+
* micro requires a pre-validated managed Contract, not natural-language intake.
|
|
395
|
+
*/
|
|
396
|
+
export const dagFrontendContractImportSchema = z.object({
|
|
397
|
+
schemaVersion: z.literal(1),
|
|
398
|
+
artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
|
|
399
|
+
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
400
|
+
requireSourceFreshness: z.literal(true),
|
|
401
|
+
});
|
|
402
|
+
/**
|
|
403
|
+
* Deterministic frontend design-policy shell (M8). Materializes the canonical
|
|
404
|
+
* implementation contract from the plan node and evaluates the design policy
|
|
405
|
+
* (Mock strategy, source freshness, component spec candidates, writeSet
|
|
406
|
+
* patterns, allowed dependencies). Runs BEFORE frontend-design-review-pi, so it
|
|
407
|
+
* never consumes a design verdict; the verdict is enforced by the writer
|
|
408
|
+
* admission shell instead. Replaces the prewrite-gate authorization path.
|
|
409
|
+
*/
|
|
410
|
+
export const dagFrontendDesignPolicySchema = z
|
|
411
|
+
.object({
|
|
412
|
+
schemaVersion: z.literal(1),
|
|
413
|
+
planFromNodeId: dagFrontendNodeIdSchema,
|
|
414
|
+
requiredRequirementIds: z
|
|
415
|
+
.array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/))
|
|
416
|
+
.default([]),
|
|
417
|
+
allowedMockStrategies: z
|
|
418
|
+
.array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
|
|
419
|
+
.min(1),
|
|
420
|
+
/** Frozen Mock verification command labels the DAG will actually execute. */
|
|
421
|
+
mockCommandLabels: z.array(z.string()).default([]),
|
|
422
|
+
artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
|
|
423
|
+
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
424
|
+
requireSourceFreshness: z.literal(true),
|
|
425
|
+
implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
|
|
426
|
+
/** Generation-frozen roots that bound custom normative directories. */
|
|
427
|
+
openspecSpecRoots: z
|
|
428
|
+
.array(z.string().refine((root) => isValidFrontendSpecRoot(root), "openspec spec root must be a normalized repo-relative directory"))
|
|
429
|
+
.optional(),
|
|
430
|
+
openspecCandidatePaths: z.array(openspecCandidatePathSchema).default([]),
|
|
431
|
+
/** Read-only selector output; omitted for scan-strict and legacy DAGs. */
|
|
432
|
+
openspecSelectionNodeId: dagFrontendNodeIdSchema.optional(),
|
|
433
|
+
/** Contract-declared and source-explicit paths that cannot be downgraded. */
|
|
434
|
+
openspecMandatoryPaths: z.array(openspecCandidatePathSchema).optional(),
|
|
435
|
+
openspecCandidateSnapshots: z
|
|
436
|
+
.array(openspecCandidateSnapshotSchema)
|
|
437
|
+
.optional(),
|
|
438
|
+
/**
|
|
439
|
+
* Generation-frozen component/theme specification bucket. Derived from
|
|
440
|
+
* `designEvidence.classified.component ∪ theme ∪ rule.components` at DAG
|
|
441
|
+
* generation. When non-empty and the canonical contract has UI-visible
|
|
442
|
+
* work, the design policy requires a non-empty `uiComponentChoices` and
|
|
443
|
+
* cross-checks each `specReference.path` against this set + the effective
|
|
444
|
+
* plan openspec-citations block.
|
|
445
|
+
*/
|
|
446
|
+
componentSpecCandidatePaths: z.array(openspecCandidatePathSchema).optional(),
|
|
447
|
+
/** OpenSpec gate policy (cited | scan-strict); generation always writes it. */
|
|
448
|
+
openspecPolicy: z.enum(["cited", "scan-strict"]).optional(),
|
|
449
|
+
/** Generation-frozen candidate source metadata (diagnostic). */
|
|
450
|
+
openspecCandidateSources: z
|
|
451
|
+
.object({
|
|
452
|
+
declared: z.array(openspecCandidatePathSchema).default([]),
|
|
453
|
+
taskSourceCited: z.array(openspecCandidatePathSchema).default([]),
|
|
454
|
+
scanStrict: z.array(openspecCandidatePathSchema).default([]),
|
|
455
|
+
})
|
|
456
|
+
.optional(),
|
|
457
|
+
/** Dependency identifiers authorized at generation time (empty = none). */
|
|
458
|
+
allowedDependencies: z.array(z.string()).default([]),
|
|
459
|
+
})
|
|
460
|
+
.superRefine((gate, context) => {
|
|
461
|
+
const roots = gate.openspecSpecRoots ?? DEFAULT_FRONTEND_SPEC_ROOTS;
|
|
462
|
+
const candidateGroups = [
|
|
463
|
+
["openspecCandidatePaths", gate.openspecCandidatePaths],
|
|
464
|
+
["openspecMandatoryPaths", gate.openspecMandatoryPaths],
|
|
465
|
+
["componentSpecCandidatePaths", gate.componentSpecCandidatePaths],
|
|
466
|
+
["openspecCandidateSources.declared", gate.openspecCandidateSources?.declared],
|
|
467
|
+
["openspecCandidateSources.taskSourceCited", gate.openspecCandidateSources?.taskSourceCited],
|
|
468
|
+
["openspecCandidateSources.scanStrict", gate.openspecCandidateSources?.scanStrict],
|
|
469
|
+
];
|
|
470
|
+
for (const [field, paths] of candidateGroups) {
|
|
471
|
+
for (const [index, candidate] of (paths ?? []).entries()) {
|
|
472
|
+
if (!isFrontendSpecFilePath(candidate, roots)) {
|
|
473
|
+
context.addIssue({
|
|
474
|
+
code: z.ZodIssueCode.custom,
|
|
475
|
+
path: [...field.split("."), index],
|
|
476
|
+
message: "openspec candidate must be a supported file under the generation-frozen openspec spec roots",
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
/**
|
|
483
|
+
* Deterministic frontend writer-admission shell (M8). Runs after
|
|
484
|
+
* frontend-design-review-pi: freezes the writer baseline, derives the concrete
|
|
485
|
+
* writeSet, folds the design-policy result + design verdict into admission,
|
|
486
|
+
* and writes contracts/frontend-writer-admission-result.json
|
|
487
|
+
* (schemaId frontend-writer-admission-shell-v1).
|
|
488
|
+
*/
|
|
489
|
+
export const dagFrontendWriterAdmissionShellSchema = z
|
|
490
|
+
.object({
|
|
491
|
+
schemaVersion: z.literal(1),
|
|
492
|
+
/** Design verdict source (frontend-design-review-pi); pass is enforced.
|
|
493
|
+
* Omitted in small topology, which has no design review node. */
|
|
494
|
+
designReviewFromNodeId: dagFrontendNodeIdSchema.optional(),
|
|
495
|
+
/** Frozen command labels the verification bundle will execute. */
|
|
496
|
+
frozenCommandLabels: z.array(z.string()).default([]),
|
|
497
|
+
allowedMockStrategies: z
|
|
498
|
+
.array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
|
|
499
|
+
.min(1),
|
|
500
|
+
/** Writer-preceding lint commands; baseline is materialized here (R5). */
|
|
501
|
+
lintCommands: z.array(z.string().min(1)).optional(),
|
|
502
|
+
lintEvidence: dagShellVerifyEvidenceSchema.optional(),
|
|
503
|
+
})
|
|
504
|
+
.superRefine((admission, context) => {
|
|
505
|
+
if ((admission.lintCommands?.length ?? 0) > 0 && !admission.lintEvidence) {
|
|
506
|
+
context.addIssue({
|
|
507
|
+
code: z.ZodIssueCode.custom,
|
|
508
|
+
path: ["lintEvidence"],
|
|
509
|
+
message: "lintEvidence is required when lintCommands are present",
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
/** Deterministic frontend closeout shell (M8). Renders the closeout from
|
|
514
|
+
* committed facts only (review context + typed review terminal + trace). */
|
|
515
|
+
export const dagFrontendCloseoutSchema = z.object({
|
|
516
|
+
schemaVersion: z.literal(1),
|
|
517
|
+
reviewFromNodeId: dagFrontendNodeIdSchema,
|
|
518
|
+
});
|
|
336
519
|
export const ENV_VAR_NAME_PATTERN = /^[A-Z_][A-Z0-9_]*$/;
|
|
337
520
|
export const dagVersionSchema = z
|
|
338
521
|
.union([z.literal(1), z.literal(2), z.literal(3), z.literal(4)])
|
|
@@ -380,7 +563,14 @@ export const dagWriteGuardPolicySchema = z.enum(["full", "tools-only"]);
|
|
|
380
563
|
*/
|
|
381
564
|
export const dagWriterOutcomePolicySchema = z
|
|
382
565
|
.object({
|
|
383
|
-
|
|
566
|
+
/**
|
|
567
|
+
* Protocol face: `implementation-outcome-v1` keeps the legacy
|
|
568
|
+
* IMPLEMENTATION_OUTCOME first-line protocol (backend/README/module
|
|
569
|
+
* writers + frontend-repair-pi); `frontend-facts-v1` derives the status
|
|
570
|
+
* from mechanical facts (write-tool events / run delta / write guard /
|
|
571
|
+
* requirement coverage / focused-check) and never reads the first line.
|
|
572
|
+
*/
|
|
573
|
+
type: z.enum(["implementation-outcome-v1", "frontend-facts-v1"]),
|
|
384
574
|
/** Generation writers may forbid already-satisfied/no-diff completion. */
|
|
385
575
|
requireChangedFiles: z.boolean().optional(),
|
|
386
576
|
})
|
|
@@ -427,6 +617,8 @@ export const dagBackendTestPipelineSchema = z.enum([
|
|
|
427
617
|
"ingest-backend-test-gap",
|
|
428
618
|
]);
|
|
429
619
|
export const dagFrontendBrowserToolPreflightSchema = z.object({}).strict();
|
|
620
|
+
export const dagFrontendTestStandardScenariosSchema = z.object({}).strict();
|
|
621
|
+
export const dagFrontendTestEnvironmentProbeSchema = z.object({}).strict();
|
|
430
622
|
export const dagFrontendTestEvidenceValidationSchema = z.object({}).strict();
|
|
431
623
|
export const dagFrontendTestCaseChecklistSchema = z.object({}).strict();
|
|
432
624
|
export const dagFrontendTestHtmlReportSchema = z.object({}).strict();
|
|
@@ -511,12 +703,17 @@ export const dagShellConfigSchema = z.object({
|
|
|
511
703
|
.optional(),
|
|
512
704
|
requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
|
|
513
705
|
jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
|
|
706
|
+
frontendDesignPolicy: dagFrontendDesignPolicySchema.optional(),
|
|
707
|
+
frontendWriterAdmission: dagFrontendWriterAdmissionShellSchema.optional(),
|
|
514
708
|
frontendPrewriteGate: dagFrontendPrewriteGateSchema.optional(),
|
|
515
|
-
frontendLintBaseline: dagFrontendLintBaselineSchema.optional(),
|
|
516
709
|
frontendVerificationBundle: dagFrontendVerificationBundleSchema.optional(),
|
|
517
710
|
frontendReviewContext: dagFrontendReviewContextSchema.optional(),
|
|
711
|
+
frontendContractImport: dagFrontendContractImportSchema.optional(),
|
|
712
|
+
frontendCloseout: dagFrontendCloseoutSchema.optional(),
|
|
518
713
|
frontendTestCaseChecklist: dagFrontendTestCaseChecklistSchema.optional(),
|
|
519
714
|
frontendBrowserToolPreflight: dagFrontendBrowserToolPreflightSchema.optional(),
|
|
715
|
+
frontendTestStandardScenarios: dagFrontendTestStandardScenariosSchema.optional(),
|
|
716
|
+
frontendTestEnvironmentProbe: dagFrontendTestEnvironmentProbeSchema.optional(),
|
|
520
717
|
frontendTestEvidenceValidation: dagFrontendTestEvidenceValidationSchema.optional(),
|
|
521
718
|
finalWriteSetApprovalGate: dagFinalWriteSetApprovalGateSchema.optional(),
|
|
522
719
|
frontendTestL5Report: z.object({}).strict().optional(),
|
|
@@ -749,6 +946,13 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
749
946
|
role: dagRoleSchema.optional(),
|
|
750
947
|
skills: z.array(z.string()).optional(),
|
|
751
948
|
toolProfile: dagToolProfileSchema.optional(),
|
|
949
|
+
/**
|
|
950
|
+
* Explicit Pi extension allowlist (backend implementation DAGs only):
|
|
951
|
+
* frozen short ids resolved at run time against the Pi settings package
|
|
952
|
+
* inventory. Omitted = extension discovery stays fully closed (R8.1).
|
|
953
|
+
* Only `executor: "pi"` nodes may declare it (validated fail-closed).
|
|
954
|
+
*/
|
|
955
|
+
piExtensions: dagPiExtensionsSchema.optional(),
|
|
752
956
|
/** Default deny: file write (toolProfile=write) does not grant command execution. */
|
|
753
957
|
commandPolicy: dagCommandPolicySchema.optional(),
|
|
754
958
|
writePolicy: dagWritePolicySchema.optional(),
|
|
@@ -782,10 +986,6 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
782
986
|
* this run-owned skeleton before strict contract validation.
|
|
783
987
|
*/
|
|
784
988
|
skeleton: z.record(z.string(), z.unknown()).optional(),
|
|
785
|
-
/** Frontend plan/revision citation fence is only needed for a
|
|
786
|
-
* generation-frozen OpenSpec candidate set. Undefined preserves the
|
|
787
|
-
* historical required-fence protocol. */
|
|
788
|
-
citationsRequired: z.boolean().optional(),
|
|
789
989
|
})
|
|
790
990
|
.strict()
|
|
791
991
|
.superRefine((value, ctx) => {
|
|
@@ -800,12 +1000,6 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
800
1000
|
}
|
|
801
1001
|
})
|
|
802
1002
|
.optional(),
|
|
803
|
-
/**
|
|
804
|
-
* Opt-in upstream context mode for nodes that must consume a validated,
|
|
805
|
-
* hash-bound artifact rather than a potentially truncated text preview.
|
|
806
|
-
* The default remains preview mode for every existing DAG.
|
|
807
|
-
*/
|
|
808
|
-
artifactFirstUpstreamNodeIds: z.array(z.string()).optional(),
|
|
809
1003
|
/**
|
|
810
1004
|
* Fail-closed outcome/diff consistency contract for bounded Pi writers.
|
|
811
1005
|
* The writer must begin with IMPLEMENTATION_OUTCOME: changed,
|
|
@@ -836,6 +1030,14 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
836
1030
|
forbiddenPaths: z.array(z.string()).optional().default([]),
|
|
837
1031
|
decisionGate: dagDecisionGateSchema.optional(),
|
|
838
1032
|
retryPolicy: dagRetryPolicySchema.optional(),
|
|
1033
|
+
/**
|
|
1034
|
+
* Generation-path stamp for implicit same-run transient retry. Only the
|
|
1035
|
+
* three target templates stamp canonical final verify shells and
|
|
1036
|
+
* exclusive `implement-pi`. Zod-declared so parseDagSpec does not strip it.
|
|
1037
|
+
*/
|
|
1038
|
+
transientRetryProfile: z
|
|
1039
|
+
.literal(TARGET_TEMPLATE_TRANSIENT_RETRY_PROFILE)
|
|
1040
|
+
.optional(),
|
|
839
1041
|
/** Optional per-node adaptive liveness override (merged over defaults). */
|
|
840
1042
|
livenessPolicy: dagLivenessPolicySchema,
|
|
841
1043
|
dynamicExpansion: dagDynamicExpansionSchema.optional(),
|
|
@@ -1056,6 +1258,9 @@ export const FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION = 1;
|
|
|
1056
1258
|
* Phase 3a parents converge first, so `rollback-pending` is not produced and
|
|
1057
1259
|
* was removed by AC-1; the phase starts at `child-staging` and ends at
|
|
1058
1260
|
* `settled`.
|
|
1261
|
+
*
|
|
1262
|
+
* @deprecated Read-only compatibility only; the M6 operation controller no
|
|
1263
|
+
* longer uses this phase as the authoritative recovery entry point.
|
|
1059
1264
|
*/
|
|
1060
1265
|
export const frontendRecoveryPhaseSchema = z.enum([
|
|
1061
1266
|
"child-staging",
|
|
@@ -1075,6 +1280,9 @@ export const frontendRecoveryPhaseSchema = z.enum([
|
|
|
1075
1280
|
* runner's `(existing?.revision ?? 0) + 1` CAS write (the schema only bounds it
|
|
1076
1281
|
* non-negative). `childRunId` may be absent through `child-activating` and must
|
|
1077
1282
|
* be non-empty from `child-running` onward.
|
|
1283
|
+
*
|
|
1284
|
+
* @deprecated Read-only compatibility only; the runner no longer writes this
|
|
1285
|
+
* intent (the M6 operation controller supervises recovery instead).
|
|
1078
1286
|
*/
|
|
1079
1287
|
export const frontendRecoveryStateSchema = z
|
|
1080
1288
|
.object({
|
|
@@ -1146,6 +1354,10 @@ export const frontendRecoveryEvidenceRefSchema = z
|
|
|
1146
1354
|
* stays permissive (failureClass optional, evidenceRefs may be empty) so it
|
|
1147
1355
|
* accepts the runner's actual phase-3a products, which do not always carry a
|
|
1148
1356
|
* failure class or evidence reference.
|
|
1357
|
+
*
|
|
1358
|
+
* @deprecated Read-only compatibility only; the M6 operation controller writes
|
|
1359
|
+
* `recovered`/`auto-recovery-blocked` results directly and never uses this
|
|
1360
|
+
* schema as the authoritative recovery settlement.
|
|
1149
1361
|
*/
|
|
1150
1362
|
export const frontendRecoveryResultSchema = z
|
|
1151
1363
|
.object({
|
|
@@ -38,10 +38,6 @@ export function relocateNodeArtifactPaths(node, oldRunDir, newRunDir) {
|
|
|
38
38
|
node.stdoutArtifactPath = rewrite(node.stdoutArtifactPath);
|
|
39
39
|
node.assistantArtifactPath = rewrite(node.assistantArtifactPath);
|
|
40
40
|
node.structuredArtifactPath = rewrite(node.structuredArtifactPath);
|
|
41
|
-
if (node.frontendPlanOutput) {
|
|
42
|
-
node.frontendPlanOutput.citationsArtifactPath = rewrite(node.frontendPlanOutput.citationsArtifactPath);
|
|
43
|
-
node.frontendPlanOutput.rawCandidatePath = rewrite(node.frontendPlanOutput.rawCandidatePath);
|
|
44
|
-
}
|
|
45
41
|
node.nodeRecordPath = rewrite(node.nodeRecordPath);
|
|
46
42
|
}
|
|
47
43
|
export function relocateConvergenceArtifactPaths(convergence, oldRunDir, newRunDir) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_DAG_EXECUTOR_MODELS, ENV_VAR_NAME_PATTERN, dagCommandPolicyAllows, resolveDagCommandPolicy, } from "./types.js";
|
|
1
|
+
import { DEFAULT_DAG_EXECUTOR_MODELS, ENV_VAR_NAME_PATTERN, dagCommandPolicyAllows, dagPiExtensionIdSchema, resolveDagCommandPolicy, } from "./types.js";
|
|
2
2
|
import { resolveShellCommands } from "../../executors/shell-executor.js";
|
|
3
3
|
import { pathMatchesPattern } from "../../shared/git-progress.js";
|
|
4
4
|
import { resolveRepairTaskForGate } from "./repair-artifact.js";
|
|
@@ -463,8 +463,12 @@ function validateShellTaskConfig(task, spec, issues) {
|
|
|
463
463
|
if (commands.length === 0 &&
|
|
464
464
|
!shell.jsonArtifactGate &&
|
|
465
465
|
!shell.backendTestPipeline &&
|
|
466
|
-
!shell.
|
|
466
|
+
!shell.frontendDesignPolicy &&
|
|
467
|
+
!shell.frontendWriterAdmission &&
|
|
468
|
+
!shell.frontendCloseout &&
|
|
467
469
|
!shell.frontendBrowserToolPreflight &&
|
|
470
|
+
!shell.frontendTestStandardScenarios &&
|
|
471
|
+
!shell.frontendTestEnvironmentProbe &&
|
|
468
472
|
!shell.frontendVerificationBundle &&
|
|
469
473
|
!shell.frontendReviewContext &&
|
|
470
474
|
!shell.frontendTestCaseChecklist &&
|
|
@@ -534,18 +538,26 @@ function validateShellTaskConfig(task, spec, issues) {
|
|
|
534
538
|
}
|
|
535
539
|
}
|
|
536
540
|
}
|
|
537
|
-
if (shell.
|
|
538
|
-
const sourceNodeIds = [
|
|
539
|
-
shell.frontendPrewriteGate.planFromNodeId,
|
|
540
|
-
...shell.frontendPrewriteGate.planFallbackFromNodeIds,
|
|
541
|
-
shell.frontendPrewriteGate.reviewFromNodeId,
|
|
542
|
-
...shell.frontendPrewriteGate.reviewFallbackFromNodeIds,
|
|
543
|
-
];
|
|
541
|
+
if (shell.frontendDesignPolicy) {
|
|
542
|
+
const sourceNodeIds = [shell.frontendDesignPolicy.planFromNodeId];
|
|
544
543
|
for (const sourceNodeId of new Set(sourceNodeIds)) {
|
|
545
544
|
if (!task.depends_on.includes(sourceNodeId)) {
|
|
546
545
|
issues.push({
|
|
547
546
|
type: "missing-dependency",
|
|
548
|
-
message: `shell task ${task.id}
|
|
547
|
+
message: `shell task ${task.id} frontendDesignPolicy source ${sourceNodeId} must be a direct dependency`,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
if (shell.frontendWriterAdmission) {
|
|
553
|
+
const sourceNodeIds = shell.frontendWriterAdmission.designReviewFromNodeId
|
|
554
|
+
? [shell.frontendWriterAdmission.designReviewFromNodeId]
|
|
555
|
+
: [];
|
|
556
|
+
for (const sourceNodeId of new Set(sourceNodeIds)) {
|
|
557
|
+
if (!task.depends_on.includes(sourceNodeId)) {
|
|
558
|
+
issues.push({
|
|
559
|
+
type: "missing-dependency",
|
|
560
|
+
message: `shell task ${task.id} frontendWriterAdmission source ${sourceNodeId} must be a direct dependency`,
|
|
549
561
|
});
|
|
550
562
|
}
|
|
551
563
|
}
|
|
@@ -953,6 +965,7 @@ export function validateDagSpec(spec) {
|
|
|
953
965
|
validateCommandPolicyTaskConfig(task, issues);
|
|
954
966
|
validateDynamicChildCommandPolicy(task, issues);
|
|
955
967
|
validateProjectGovernanceTaskConfig(task, spec, issues);
|
|
968
|
+
validatePiExtensionsTaskConfig(task, issues);
|
|
956
969
|
validateFailureAwareDependsOn(task, spec, issues);
|
|
957
970
|
}
|
|
958
971
|
validateSameRankWriteSetConflicts(spec, ranks, issues);
|
|
@@ -963,6 +976,43 @@ export function validateDagSpec(spec) {
|
|
|
963
976
|
}
|
|
964
977
|
return issues;
|
|
965
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* Fail-closed `piExtensions` validation (plan 2026-08-21 D1/D2):
|
|
981
|
+
* - only `executor: "pi"` nodes may declare it (shell/static reject);
|
|
982
|
+
* - unknown ids outside the frozen enum are rejected at parse time by zod,
|
|
983
|
+
* and here for task objects that bypass schema parsing;
|
|
984
|
+
* - closeout nodes must stay fully closed (extension-free) — the two-bucket
|
|
985
|
+
* generator never assigns it there, so any appearance is contract drift.
|
|
986
|
+
*/
|
|
987
|
+
function validatePiExtensionsTaskConfig(task, issues) {
|
|
988
|
+
const ids = task.piExtensions;
|
|
989
|
+
if (ids === undefined)
|
|
990
|
+
return;
|
|
991
|
+
if (task.executor !== "pi") {
|
|
992
|
+
issues.push({
|
|
993
|
+
type: "invalid-pi-extensions-config",
|
|
994
|
+
message: `task ${task.id} piExtensions requires executor "pi" (found ${task.executor})`,
|
|
995
|
+
});
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const allowed = new Set(dagPiExtensionIdSchema.options);
|
|
999
|
+
for (const id of ids) {
|
|
1000
|
+
if (!allowed.has(id)) {
|
|
1001
|
+
issues.push({
|
|
1002
|
+
type: "invalid-pi-extensions-config",
|
|
1003
|
+
message: `task ${task.id} piExtensions has unknown id "${id}"; allowed: ${[
|
|
1004
|
+
...allowed,
|
|
1005
|
+
].join(", ")}`,
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (task.id === "closeout-pi" && ids.length > 0) {
|
|
1010
|
+
issues.push({
|
|
1011
|
+
type: "invalid-pi-extensions-config",
|
|
1012
|
+
message: "closeout-pi must stay extension-free (read buckets exclude closeout)",
|
|
1013
|
+
});
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
966
1016
|
function validateProjectGovernanceTaskConfig(task, spec, issues) {
|
|
967
1017
|
if (task.governanceStandardReview) {
|
|
968
1018
|
if (task.executor !== "pi" ||
|
|
@@ -50,6 +50,9 @@
|
|
|
50
50
|
"scripts/kb-graph-materialize.mjs",
|
|
51
51
|
"scripts/kb-graph-promote.mjs",
|
|
52
52
|
"scripts/kb-graph-incremental-prepare.mjs",
|
|
53
|
+
"scripts/next-info.mjs",
|
|
54
|
+
"scripts/next-publish-gate.mjs",
|
|
55
|
+
"scripts/release-source-binding.mjs",
|
|
53
56
|
"docs/templates/agent-dag.schema.json",
|
|
54
57
|
"examples/example-dag.json",
|
|
55
58
|
"skills/agent-worker/SKILL.md",
|
|
@@ -277,6 +280,7 @@
|
|
|
277
280
|
"harness.json",
|
|
278
281
|
"package.json",
|
|
279
282
|
"src/commands/init.ts",
|
|
283
|
+
"src/commands/init-model-catalog.ts",
|
|
280
284
|
"src/commands/client-recovery.ts",
|
|
281
285
|
"src/commands/init-upgrade.ts",
|
|
282
286
|
"src/cli.ts",
|
|
@@ -302,6 +306,7 @@
|
|
|
302
306
|
"description": "Changes that may alter target-project initialization behavior, default DAG role skills, skill resolution, or package/init contracts.",
|
|
303
307
|
"patterns": [
|
|
304
308
|
"src/commands/init.ts",
|
|
309
|
+
"src/commands/init-model-catalog.ts",
|
|
305
310
|
"src/commands/client-recovery.ts",
|
|
306
311
|
"src/commands/init-upgrade.ts",
|
|
307
312
|
"src/cli.ts",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
- [`local-development-environment.md`](local-development-environment.md):本地环境、Windows 差异和常见排障。
|
|
8
8
|
- [`branch-merge-guideline.md`](branch-merge-guideline.md):按风险选择合并模式并留下 source-SHA 证据。
|
|
9
9
|
- [`github-collaboration.md`](github-collaboration.md):仓库内 GitHub 协作约定及发布通道。
|
|
10
|
-
- [`version-and-release-management.md`](version-and-release-management.md):版本发布管理——next / release-x.y / latest
|
|
10
|
+
- [`version-and-release-management.md`](version-and-release-management.md):版本发布管理——next / release-x.y / latest 渠道、移动式 `npm-next` ref、`inspect next` 查询入口、workflow 与 promote 手册。
|
|
11
11
|
- [`production-readiness.md`](production-readiness.md):交付前的生产就绪判断。
|
|
12
12
|
- [`agent-worker-production-readiness.md`](agent-worker-production-readiness.md):agent-worker Feature/Task Pool 外层编排的 Production Readiness v1 操作合同。
|
|
13
13
|
- [`backend-test-jacoco-coverage.md`](backend-test-jacoco-coverage.md):Java 被测服务挂载 JaCoCo tcpserver agent 的 Maven、`java -jar` 与 Docker 操作手册。
|
|
@@ -130,6 +130,18 @@
|
|
|
130
130
|
"toolProfile": {
|
|
131
131
|
"enum": ["read-only", "write"]
|
|
132
132
|
},
|
|
133
|
+
"piExtensionId": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"enum": ["pi-codegraph", "pi-lens"],
|
|
136
|
+
"description": "Frozen Pi extension short id resolved at run time against the Pi settings package inventory. Adding a package requires changing the plan and this enum."
|
|
137
|
+
},
|
|
138
|
+
"piExtensions": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"minItems": 1,
|
|
141
|
+
"uniqueItems": true,
|
|
142
|
+
"items": { "$ref": "#/$defs/piExtensionId" },
|
|
143
|
+
"description": "Node-level explicit Pi extension allowlist (backend implementation DAGs only). Omitted = extension discovery stays fully closed (R8.1). Only executor 'pi' nodes may declare it."
|
|
144
|
+
},
|
|
133
145
|
"role": {
|
|
134
146
|
"enum": ["planner", "scout", "implementer", "reviewer", "supervisor", "verifier", "closeout"]
|
|
135
147
|
},
|
|
@@ -356,6 +368,31 @@
|
|
|
356
368
|
"properties": { "schemaVersion": { "const": 1 }, "requireBaseline": { "const": true } }
|
|
357
369
|
},
|
|
358
370
|
"frontendTestCaseChecklist": { "type": "object", "additionalProperties": false },
|
|
371
|
+
"frontendBrowserToolPreflight": { "type": "object", "additionalProperties": false },
|
|
372
|
+
"frontendTestStandardScenarios": { "type": "object", "additionalProperties": false },
|
|
373
|
+
"frontendTestEnvironmentProbe": { "type": "object", "additionalProperties": false },
|
|
374
|
+
"frontendTestCaseManifest": {
|
|
375
|
+
"type": "object",
|
|
376
|
+
"additionalProperties": false,
|
|
377
|
+
"properties": {
|
|
378
|
+
"maxCases": { "type": "integer", "minimum": 1 },
|
|
379
|
+
"declaredAcIds": { "type": "array", "items": { "type": "string" } }
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
"frontendTestResultFinalize": {
|
|
383
|
+
"type": "object",
|
|
384
|
+
"additionalProperties": false,
|
|
385
|
+
"properties": {
|
|
386
|
+
"declaredAcIds": { "type": "array", "items": { "type": "string" } }
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
"frontendTestReports": {
|
|
390
|
+
"type": "object",
|
|
391
|
+
"additionalProperties": false,
|
|
392
|
+
"properties": {
|
|
393
|
+
"l5": { "type": "boolean" }
|
|
394
|
+
}
|
|
395
|
+
},
|
|
359
396
|
"frontendTestEvidenceValidation": { "type": "object", "additionalProperties": false },
|
|
360
397
|
"frontendTestHtmlReport": { "type": "object", "additionalProperties": false },
|
|
361
398
|
"backendTestPipeline": {
|
|
@@ -383,6 +420,12 @@
|
|
|
383
420
|
{ "required": ["frontendVerificationBundle"] },
|
|
384
421
|
{ "required": ["frontendReviewContext"] },
|
|
385
422
|
{ "required": ["frontendTestCaseChecklist"] },
|
|
423
|
+
{ "required": ["frontendBrowserToolPreflight"] },
|
|
424
|
+
{ "required": ["frontendTestStandardScenarios"] },
|
|
425
|
+
{ "required": ["frontendTestEnvironmentProbe"] },
|
|
426
|
+
{ "required": ["frontendTestCaseManifest"] },
|
|
427
|
+
{ "required": ["frontendTestResultFinalize"] },
|
|
428
|
+
{ "required": ["frontendTestReports"] },
|
|
386
429
|
{ "required": ["frontendTestEvidenceValidation"] },
|
|
387
430
|
{ "required": ["frontendTestHtmlReport"] },
|
|
388
431
|
{ "required": ["backendTestPipeline"] }
|
|
@@ -485,6 +528,7 @@
|
|
|
485
528
|
"items": { "type": "string", "minLength": 1 }
|
|
486
529
|
},
|
|
487
530
|
"toolProfile": { "$ref": "#/$defs/toolProfile" },
|
|
531
|
+
"piExtensions": { "$ref": "#/$defs/piExtensions" },
|
|
488
532
|
"governanceStandardReview": {
|
|
489
533
|
"type": "boolean",
|
|
490
534
|
"description": "Explicitly opts this node into writer-change-scoped AGENTS.md and repository-local code-standard review. Never inferred from node id or role."
|
|
@@ -66,12 +66,11 @@
|
|
|
66
66
|
".harness/**",
|
|
67
67
|
"artifacts/**"
|
|
68
68
|
],
|
|
69
|
-
"outputContract": "Write testcase/frontend/rag/standard-scenarios.v1.json for generate-time standard scenario coverage.",
|
|
70
|
-
"subtask_prompt": "
|
|
69
|
+
"outputContract": "Write testcase/frontend/rag/standard-scenarios.v1.json for generate-time standard scenario coverage. Copy docs/templates or harness.json governanceRoot templates (including ai_workspace/loop-agent/templates) when present; otherwise write the minimal STD-FE-SMOKE-ENTRY fallback.",
|
|
70
|
+
"subtask_prompt": "Prepare frontend-test package: materialize standard-scenarios.v1.json into the RAG package from docs/templates, governanceRoot/templates, or the init-projected ai_workspace/loop-agent/templates path.",
|
|
71
71
|
"shell": {
|
|
72
|
-
"commands": [
|
|
73
|
-
|
|
74
|
-
],
|
|
72
|
+
"commands": [],
|
|
73
|
+
"frontendTestStandardScenarios": {},
|
|
75
74
|
"cwd": ".",
|
|
76
75
|
"timeoutMs": 60000
|
|
77
76
|
}
|
|
@@ -120,12 +119,11 @@
|
|
|
120
119
|
".harness/**",
|
|
121
120
|
"artifacts/**"
|
|
122
121
|
],
|
|
123
|
-
"outputContract": "Fail-closed environment preflight: absolute non-production baseUrl + curl HTTP reachability; writes environmentProbe facts; unreachable => blockedReason frontend-base-url-unreachable (
|
|
124
|
-
"subtask_prompt": "Parse the concrete baseUrl selected by retrieve-frontend-test-context-pi from testcase/frontend/rag/context.md. Reject production, non-http(s), credentials, query and fragment. Probe with curl (HEAD then GET fallback; connect/max-time; no auth/cookie). 2xx/3xx => reachable and continue. 4xx/5xx/DNS/timeout/
|
|
122
|
+
"outputContract": "Fail-closed environment preflight: absolute non-production baseUrl + curl HTTP reachability; writes environmentProbe facts; unreachable => blockedReason frontend-base-url-unreachable with errorClass (connection-refused / dns-unresolved / connect-timeout / http-N). Node ERROR so generate/map do not run. Does not start the app.",
|
|
123
|
+
"subtask_prompt": "Parse the concrete baseUrl selected by retrieve-frontend-test-context-pi from testcase/frontend/rag/context.md. Reject production, non-http(s), credentials, query and fragment. Probe with curl (HEAD then GET fallback; connect/max-time; no auth/cookie). 2xx/3xx => reachable and continue. Connection refused records errorClass=connection-refused and tells the operator to start the local app then rerun from this node. 4xx/5xx/DNS/timeout/TLS => blockedReason frontend-base-url-unreachable. Missing curl => blockedReason curl-unavailable. Do not start the app.",
|
|
125
124
|
"shell": {
|
|
126
|
-
"commands": [
|
|
127
|
-
|
|
128
|
-
],
|
|
125
|
+
"commands": [],
|
|
126
|
+
"frontendTestEnvironmentProbe": {},
|
|
129
127
|
"cwd": ".",
|
|
130
128
|
"timeoutMs": 60000
|
|
131
129
|
}
|
|
@@ -17,5 +17,5 @@ Do not claim the environment is reachable until preflight completes. Preflight d
|
|
|
17
17
|
|
|
18
18
|
## Standard scenario coverage
|
|
19
19
|
|
|
20
|
-
- Copy or reference `docs/templates/frontend-test-standard-scenarios.v1.json` into `testcase/frontend/rag/standard-scenarios.v1.json` when available.
|
|
20
|
+
- Copy or reference `docs/templates/frontend-test-standard-scenarios.v1.json`, `harness.json` `governanceRoot`/templates, or the init-projected `ai_workspace/loop-agent/templates/frontend-test-standard-scenarios.v1.json` into `testcase/frontend/rag/standard-scenarios.v1.json` when available.
|
|
21
21
|
- Add `## Standard scenario coverage` to coverage-map.md with planned/n/a for each must scenario.
|