@tea-agent/loop-agent 0.44.0-next.1 → 0.44.0-next.11
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 +379 -416
- package/dist/application/evaluation/budget.js +21 -0
- package/dist/application/evaluation/corpus-hash.js +10 -15
- package/dist/application/evaluation/corpus.js +2 -1
- package/dist/application/evaluation/frontend-browser-acceptance.js +77 -0
- package/dist/application/evaluation/frontend-corpus-browser-acceptance.js +175 -0
- package/dist/application/evaluation/frontend-gateway-evidence.js +226 -0
- package/dist/application/evaluation/frontend-pair-registration.js +143 -0
- package/dist/application/evaluation/frontend-paired-summary.js +150 -0
- package/dist/application/evaluation/frontend-run-observation.js +144 -0
- package/dist/application/evaluation/frontend-shared-evidence.js +105 -0
- package/dist/application/evaluation/types.js +45 -12
- package/dist/application/task-lifecycle/observe.js +26 -1
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +11 -0
- package/dist/cli/program.js +4 -0
- package/dist/commands/task-advance.js +3 -5
- package/dist/commands/task-source-prepare.js +13 -1
- package/dist/executors/dag-pi/guards.js +4 -0
- package/dist/executors/dag-pi/model-config.js +30 -0
- package/dist/executors/dag-pi/plan/fact-adoption.js +55 -0
- package/dist/{workflows/dag/frontend-plan-completeness.js → executors/dag-pi/plan/facts.js} +22 -28
- package/dist/executors/dag-pi/plan/finalize-tools.js +259 -0
- package/dist/executors/dag-pi/plan/ledger-contract.js +55 -0
- package/dist/executors/dag-pi/plan/record-tools.js +861 -0
- package/dist/executors/dag-pi/plan/schema.js +129 -0
- package/dist/executors/dag-pi/plan/session.js +1 -0
- package/dist/executors/dag-pi/plan/tool-helpers.js +16 -0
- package/dist/executors/dag-pi/sessions/index.js +6 -0
- package/dist/executors/dag-pi/sessions/plan-batches.js +280 -0
- package/dist/executors/dag-pi/sessions/plan-prompts.js +367 -0
- package/dist/executors/dag-pi/sessions/scout-parallel.js +197 -0
- package/dist/executors/dag-pi/sessions/segmented-plan.js +1184 -0
- package/dist/executors/dag-pi/sessions/writer-evidence.js +60 -0
- package/dist/executors/dag-pi/tools/contract-tools.js +515 -0
- package/dist/executors/dag-pi/tools/design-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/plan-decision-tools.js +529 -0
- package/dist/executors/dag-pi/tools/review-terminal-tools.js +159 -0
- package/dist/executors/dag-pi/tools/scout-evidence-tools.js +270 -0
- package/dist/executors/dag-pi-executor.js +733 -5782
- package/dist/executors/pi-executor.js +6 -1
- package/dist/executors/pi-sdk-executor.js +112 -19
- package/dist/executors/shell-executor.js +88 -12
- package/dist/executors/shell-presets.js +1 -1
- package/dist/executors/shell-write-guard.js +26 -3
- package/dist/infrastructure/console/operation-store.js +48 -14
- package/dist/infrastructure/evaluation/corpus-store.js +37 -12
- package/dist/shared/file-identity.js +23 -0
- package/dist/shared/git-workspace-status.js +482 -0
- package/dist/shared/operator/capabilities.js +10 -5
- package/dist/shared/path-safety.js +2 -2
- package/dist/shared/wizard-local-telemetry.js +51 -0
- package/dist/task/config-types.js +22 -8
- package/dist/task/contract/project.js +13 -0
- package/dist/task/contract/schema.js +2 -7
- package/dist/task/frontend-preflight.js +1 -1
- package/dist/task/source-prepare/parse-intent.js +124 -5
- package/dist/task/source-prepare/prepare.js +6 -1
- package/dist/task/source-prepare/semantic-intake.js +18 -3
- package/dist/task/source-prepare/source-execution.js +65 -0
- package/dist/task/source-prepare/source-fidelity-pi.js +22 -9
- package/dist/task/source-prepare/source-provider-budget.js +290 -0
- package/dist/worker/console/chat/assistant-content.js +5 -34
- package/dist/worker/console/chat/chat-event-store.js +62 -10
- package/dist/worker/console/chat/context-insights.js +723 -0
- package/dist/worker/console/chat/conversation-nodes.js +383 -0
- package/dist/worker/console/chat/db-connections.js +854 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +1 -1
- package/dist/worker/console/chat/pi-runtime/custom-tools/sql-tools.js +655 -0
- package/dist/worker/console/chat/pi-runtime.js +445 -36
- package/dist/worker/console/chat/provider-error.js +0 -12
- package/dist/worker/console/chat/repo-browser.js +52 -4
- package/dist/worker/console/chat/routes.js +577 -72
- package/dist/worker/console/chat/sdd-data-alignment.js +8 -31
- package/dist/worker/console/chat/session-store.js +147 -19
- package/dist/worker/console/chat/shortcuts.js +7 -0
- package/dist/worker/console/chat/timeline-message-order.js +22 -0
- package/dist/worker/console/chat/tools.js +34 -0
- package/dist/worker/console/chat/turn-process.js +237 -66
- package/dist/worker/console/chat/user-turn-outline.js +19 -0
- package/dist/worker/console/console-update-runtime.js +2 -2
- package/dist/worker/console/dag-confirmation.js +3 -0
- package/dist/worker/console/dag-execution-receipt.js +3 -0
- package/dist/worker/console/operator-actions.js +32 -3
- package/dist/worker/console/operator-user-error.js +28 -1
- package/dist/worker/console/pi-readiness.js +4 -4
- package/dist/worker/console/prd-intake-bridge.js +5 -12
- package/dist/worker/console/recovery-cta.js +14 -14
- package/dist/worker/console/server.js +5 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-8-j6y-sd.js → abnfDiagram-N423BO3Z-r1NAPU_5.js} +1 -1
- package/dist/worker/console/static/assets/{arc-eoQiMvuk.js → arc-dT5T4sVL.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-C4A3uMcI.js → architectureDiagram-T3A2C74G-CP_cOq46.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D4zD2F-Q.js → blockDiagram-VBNYF7ZC-sToEZxEf.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-j1RkJziL.js → c4Diagram-5PPSVZJV-DSxwwi2m.js} +1 -1
- package/dist/worker/console/static/assets/channel-CyBY_yGk.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-YBHmhik1.js → chunk-2GRJ4B5K-B9fh0IkF.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-COfWyo9P.js → chunk-2Q5K7J3B-CAUNl-A6.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-I99OUkHY.js → chunk-5RXB4S5H-BomeqbNX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-XKxoJNJ4.js → chunk-5VM5RSS4-BNAuTOLE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DLT_cYx1.js → chunk-6Q2QTUOP-DfzjRD5y.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CxcKZ9mV.js → chunk-GF5L2VYU-C3uE-yc2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-V1EqXdjY.js → chunk-JWPE2WC7-C3sQMMYr.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DebTtdFp.js → chunk-KBJHAD2P-D_AtEYQL.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-B-ivNRDf.js → chunk-RYQCIY6F-DW4TwhKv.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-DC_Ds11b.js → chunk-XXDRQBXY-CTQDmJcp.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-D5lN4E_E.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-sWEqKwIb.js → cose-bilkent-JH36ORCC-iMqPuBsL.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BXa_dcu4.js → cynefin-VYW2F7L2-DktIuDWs.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-C-Mle84F.js → cynefinDiagram-MW4NZA55-BgVnjyEs.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CXPDBITe.js → dagre-VZM6K2ZE-CR25E4bu.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CC-WJQfa.js → diagram-7IWD3JNH-CPM2h5HY.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CDAV5Vs4.js → diagram-B4RE2ZJO-DqF0x2g1.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-CmFiAcNz.js → diagram-LBJQPF4R-G8BYp2eb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DPBZHuyn.js → diagram-Q27KOJAE-BbC4kT_D.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-jUlm_Ds3.js → diagram-UB23O5K3-CKZwZ-G5.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DWhQ3mfY.js → ebnfDiagram-BXEA7PRR-kJDmheia.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-Tr2gMqet.js → erDiagram-JOGREHBK-DFTVHyxB.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DJjVQHPA.js → flowDiagram-UKHOOZJN-BqdN5_os.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-D74dQ4u0.js → ganttDiagram-PKOTCBZU-SYDjVakh.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js → gitGraphDiagram-DS77QQ5N-Bf8bNpGA.js} +1 -1
- package/dist/worker/console/static/assets/index-B28Onmfy.css +1 -0
- package/dist/worker/console/static/assets/index-CTMZsDmh.js +486 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-ILCbxJyb.js → infoDiagram-6WML65LV-DIeAmJ45.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DeuWBQ89.js → ishikawaDiagram-WSZJBQD7-oikqUAlo.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CF5ih8Fk.js → journeyDiagram-NVQOT4AX-mKEF_xYT.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C7yOSuRO.js → kanban-definition-27J2QSJJ-BdWJHPnN.js} +1 -1
- package/dist/worker/console/static/assets/{linear-BDZ9riWi.js → linear-DGRlieMH.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-7pKqYtpZ.js → mermaid.core-4P9yPC9c.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-LeJDybSU.js → mindmap-definition-FAOFIHXS-5PTSkos6.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BJvT3pMD.js → pegDiagram-VL7TDLO6-DONIFpG5.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-_rGqpMan.js → pieDiagram-7S7Q4E2Y-D_Z4S70M.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DPcSv5aA.js → quadrantDiagram-CIZ2JOQS-CAsjh8iY.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Drxx4hkJ.js → railroadDiagram-AXF67PYL-Cx4Dd6ac.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BCTUdU4z.js → requirementDiagram-LRYGKXZP-cQb7mwyH.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-B6wzbZmB.js → sankeyDiagram-W5VNT64P-Cd-DhUdf.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BGt8d3QQ.js → sequenceDiagram-SI44F4Z6-AlKYRgIk.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-7nlhJKo7.js → sizeCapture-X5ZJPWSS-CUy_ET4e.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Cv2stqMA.js → stateDiagram-OKZ733FA-CTuVe6Sa.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-BsWhs8O7.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-qDAo4Yc1.js → swimlanes-SLNWSIFB-ChFLJb-v.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CJcioYGH.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CKbDNKTr.js → timeline-definition-Z64GVDOM-DZZeiAPr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-DI-9EHic.js → vennDiagram-T6HMQDX7-t4O91cZU.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-BrzzRDpX.js → wardleyDiagram-T6FBY63Y-CV5qGHZu.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-BclH5hGh.js → xychartDiagram-ELKLHX3M-DyXbytUI.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -1
- package/dist/worker/console/static-src/operator-chat/animation-frame-batcher.js +38 -0
- package/dist/worker/console/static-src/operator-chat/chat-link.js +20 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +186 -5
- package/dist/worker/console/static-src/operator-chat/chat-stream-continuity.js +101 -0
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +18 -3
- package/dist/worker/console/static-src/operator-chat/context-insights-store.js +64 -0
- package/dist/worker/console/static-src/operator-chat/details-rail-surfaces.js +98 -0
- package/dist/worker/console/static-src/operator-chat/file-palette-nav.js +81 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +17 -3
- package/dist/worker/console/static-src/operator-chat/process-label.js +27 -5
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +16 -7
- package/dist/worker/console/static-src/operator-chat/transcript-mode.js +21 -0
- package/dist/worker/console/static-src/operator-chat/turn-group-equality.js +4 -1
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +13 -5
- package/dist/worker/console/static-src/operator-chat/turn-state-projection.js +166 -0
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +56 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +211 -40
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +124 -27
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +88 -20
- package/dist/worker/console/static-src/operator-chat/useComposer.js +80 -6
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +13 -3
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +36 -17
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +18 -0
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +1 -1
- package/dist/worker/console/workspace-context.js +2 -0
- package/dist/worker/console/workspace-initialization.js +3 -3
- package/dist/worker/observability/read-model.js +23 -2
- package/dist/worker/observe/node-context-insights.js +479 -0
- package/dist/worker/observe/static/console-theme.css +174 -0
- package/dist/worker/observe/static/console-theme.js +36 -0
- package/dist/worker/observe/static/operator-chrome.css +85 -0
- package/dist/worker/observe/static/operator-chrome.js +51 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +3 -2
- package/dist/worker/scheduler/scheduled-goal-evidence.js +66 -3
- package/dist/worker/scheduler/scheduled-goal-store.js +10 -0
- package/dist/workflows/dag/budget-enforcement.js +31 -2
- package/dist/workflows/dag/checkpoint.js +686 -0
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-contract-facts.js +11 -8
- package/dist/workflows/dag/frontend-design-policy.js +6 -6
- package/dist/workflows/dag/frontend-durable-tools.js +90 -70
- package/dist/workflows/dag/frontend-implementation-contract.js +146 -57
- package/dist/workflows/dag/frontend-plan-canary.js +66 -16
- package/dist/workflows/dag/frontend-plan-decision-contract.js +13 -21
- package/dist/workflows/dag/frontend-plan-progress.js +92 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -5
- package/dist/workflows/dag/frontend-recovery-run.js +51 -46
- package/dist/workflows/dag/frontend-repair-assertions.js +36 -0
- package/dist/workflows/dag/frontend-review-context.js +29 -1
- package/dist/workflows/dag/frontend-review-scopes.js +92 -2
- package/dist/workflows/dag/frontend-risk.js +8 -3
- package/dist/workflows/dag/frontend-root-observation.js +261 -0
- package/dist/workflows/dag/frontend-session-budget.js +52 -39
- package/dist/workflows/dag/frontend-session-context.js +10 -0
- package/dist/workflows/dag/frontend-test-execution-evidence.js +206 -48
- package/dist/workflows/dag/frontend-typed-event-store.js +11 -1
- package/dist/workflows/dag/frontend-verification-trace.js +6 -0
- package/dist/workflows/dag/frontend-writer-admission.js +3 -1
- package/dist/workflows/dag/hybrid/sources.js +1812 -0
- package/dist/workflows/dag/hybrid/templates/backend-test.js +1807 -0
- package/dist/workflows/dag/hybrid/templates/frontend-test.js +702 -0
- package/dist/workflows/dag/hybrid/templates/frontend.js +1529 -0
- package/dist/workflows/dag/hybrid/templates/index.js +8 -0
- package/dist/workflows/dag/hybrid/templates/kg-bootstrap.js +449 -0
- package/dist/workflows/dag/hybrid/templates/knowledge-sync.js +495 -0
- package/dist/workflows/dag/hybrid/templates/shared.js +101 -0
- package/dist/workflows/dag/hybrid/templates/standard.js +265 -0
- package/dist/workflows/dag/hybrid/types.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +57 -7106
- package/dist/workflows/dag/node-execution.js +11 -11
- package/dist/workflows/dag/output-protocol.js +1 -0
- package/dist/workflows/dag/rerun-feedback.js +35 -4
- package/dist/workflows/dag/rerun-task.js +62 -66
- package/dist/workflows/dag/runner.js +38 -1159
- package/dist/workflows/dag/terminal-status.js +502 -0
- package/dist/workflows/dag/types.js +15 -8
- package/dist/workflows/loop/benchmark.js +11 -8
- package/docs/README.md +1 -1
- package/docs/architecture/dag-execution.md +1 -1
- package/docs/architecture/runtime-boundaries.md +1 -1
- package/docs/governance/harness-methodology-verification.md +11 -0
- package/docs/templates/backend-test-dag.json +5 -4
- package/docs/templates/frontend-implementation-contract.schema.json +31 -11
- package/package.json +6 -4
- package/skills/frontend-design-review/SKILL.md +8 -11
- package/skills/frontend-design-review/references/review-checklist.md +3 -4
- package/skills/frontend-review/SKILL.md +5 -1
- package/skills/loop-agent/references/command-reference.md +8 -0
- package/skills/loop-agent/references/hybrid-dag.md +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +0 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +0 -1
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +0 -469
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +0 -8
|
@@ -0,0 +1,495 @@
|
|
|
1
|
+
import { assertValidDagSpec } from "../../validate.js";
|
|
2
|
+
import { DEFAULT_DAG_EXECUTOR_MODELS, DEFAULT_DAG_OUTPUT_LANGUAGE, parseDagSpec } from "../../types.js";
|
|
3
|
+
import { HYBRID_DEFAULTS, STANDARD_GLOBAL_CONSTRAINTS, buildSourceContextBlock, buildVerifyEvidence, extractObjective, extractSuccessCriteria } from "../sources.js";
|
|
4
|
+
import { commonForbiddenPaths, commonReadOnlyPaths, stampGeneratedArtifactBindings, } from "./shared.js";
|
|
5
|
+
export const KNOWLEDGE_SYNC_DEFAULTS = {
|
|
6
|
+
...HYBRID_DEFAULTS,
|
|
7
|
+
writePolicy: "read-only",
|
|
8
|
+
};
|
|
9
|
+
export const KNOWLEDGE_SYNC_SKILLS_BY_ROLE = {
|
|
10
|
+
planner: ["loop-agent"],
|
|
11
|
+
scout: [],
|
|
12
|
+
implementer: ["verification-before-completion"],
|
|
13
|
+
reviewer: ["requesting-code-review"],
|
|
14
|
+
verifier: ["verification-before-completion", "systematic-debugging"],
|
|
15
|
+
closeout: ["loop-agent", "verification-before-completion"],
|
|
16
|
+
};
|
|
17
|
+
export function buildMultiPerspectiveReviewAggregateScript(fromNodeIds, label) {
|
|
18
|
+
if (fromNodeIds.length === 0) {
|
|
19
|
+
throw new Error("multi-perspective aggregate requires at least one review node id");
|
|
20
|
+
}
|
|
21
|
+
const idsLiteral = JSON.stringify([...fromNodeIds]);
|
|
22
|
+
const labelLiteral = JSON.stringify(label);
|
|
23
|
+
return [
|
|
24
|
+
"node",
|
|
25
|
+
"-e",
|
|
26
|
+
JSON.stringify([
|
|
27
|
+
"const fs=require('fs');",
|
|
28
|
+
"const path=require('path');",
|
|
29
|
+
`const ids=${idsLiteral};`,
|
|
30
|
+
`const label=${labelLiteral};`,
|
|
31
|
+
"const runDir=process.env.HARNESS_DAG_RUN_DIR;",
|
|
32
|
+
"if(!runDir){ console.error(label+': missing HARNESS_DAG_RUN_DIR'); process.exit(1); }",
|
|
33
|
+
"function normalize(line){",
|
|
34
|
+
" const t=String(line).trim();",
|
|
35
|
+
" const m=t.match(/^\\*{1,3}\\s*(VERDICT:[^*]+?)\\s*\\*{1,3}$/);",
|
|
36
|
+
" return (m?m[1]:t).trim();",
|
|
37
|
+
"}",
|
|
38
|
+
"function firstVerdict(text){",
|
|
39
|
+
" for (const line of String(text||'').split(/\\r?\\n/)) {",
|
|
40
|
+
" const n=normalize(line);",
|
|
41
|
+
" if(/^VERDICT:/.test(n)) return n;",
|
|
42
|
+
" }",
|
|
43
|
+
" return '';",
|
|
44
|
+
"}",
|
|
45
|
+
"const failures=[];",
|
|
46
|
+
"for (const id of ids) {",
|
|
47
|
+
" const file=path.join(runDir, id+'.json');",
|
|
48
|
+
" if(!fs.existsSync(file)){ failures.push(id+': missing JSON '+file); continue; }",
|
|
49
|
+
" let raw; try { raw=JSON.parse(fs.readFileSync(file,'utf8')); } catch(e){ failures.push(id+': invalid JSON'); continue; }",
|
|
50
|
+
" const verdict=firstVerdict(raw.assistantText ?? raw.stdout ?? '');",
|
|
51
|
+
" if(verdict!=='VERDICT: pass') failures.push(id+': '+(verdict||'missing VERDICT line'));",
|
|
52
|
+
" else console.log(id+': VERDICT: pass');",
|
|
53
|
+
"}",
|
|
54
|
+
"if(failures.length){ console.error(label+' blocked:\\n'+failures.join('\\n')); process.exit(1); }",
|
|
55
|
+
"console.log(label+': all perspectives VERDICT: pass ('+ids.length+')');",
|
|
56
|
+
].join("")),
|
|
57
|
+
].join(" ");
|
|
58
|
+
}
|
|
59
|
+
export function buildMultiPerspectiveReviewNodes(input) {
|
|
60
|
+
const reviewNodes = input.perspectives.map((p) => ({
|
|
61
|
+
id: `${input.nodePrefix}${p.id}-pi`,
|
|
62
|
+
depends_on: [...input.dependsOn],
|
|
63
|
+
role: "reviewer",
|
|
64
|
+
executor: "pi",
|
|
65
|
+
complexity: "HIGH",
|
|
66
|
+
writePolicy: "read-only",
|
|
67
|
+
allowedPaths: input.allowedPaths,
|
|
68
|
+
forbiddenPaths: commonForbiddenPaths(input.sources),
|
|
69
|
+
outputContract: `Plain Markdown; first non-empty line is VERDICT: pass or VERDICT: request-revision. Perspective: ${p.perspective}. No file writes.`,
|
|
70
|
+
subtask_prompt: [
|
|
71
|
+
`You are the **${p.perspective}** reviewer in a multi-perspective review panel.`,
|
|
72
|
+
"Other perspectives run in parallel; do not assume their conclusions. Stay in your role.",
|
|
73
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
74
|
+
"Any Critical or Important finding in your domain must force VERDICT: request-revision.",
|
|
75
|
+
"Structure: VERDICT line, then Findings (Critical/Important/Minor), then Checked Items, then Residual Risks.",
|
|
76
|
+
"Cite concrete paths/ids as evidence. Read-only: do not modify files.",
|
|
77
|
+
...input.sharedBrief,
|
|
78
|
+
"Focus for this perspective:",
|
|
79
|
+
...p.focus.map((line) => `- ${line}`),
|
|
80
|
+
buildSourceContextBlock(input.sources),
|
|
81
|
+
].join("\n\n"),
|
|
82
|
+
}));
|
|
83
|
+
const reviewIds = reviewNodes.map((n) => n.id);
|
|
84
|
+
if (input.structuredVerdictGate) {
|
|
85
|
+
const aggregateNodeId = `${input.nodePrefix}aggregate-pi`;
|
|
86
|
+
const aggregateNode = {
|
|
87
|
+
id: aggregateNodeId,
|
|
88
|
+
depends_on: reviewIds,
|
|
89
|
+
role: "reviewer",
|
|
90
|
+
executor: "pi",
|
|
91
|
+
complexity: "HIGH",
|
|
92
|
+
writePolicy: "read-only",
|
|
93
|
+
allowedPaths: input.allowedPaths,
|
|
94
|
+
forbiddenPaths: commonForbiddenPaths(input.sources),
|
|
95
|
+
outputContract: "Plain Markdown; first non-empty line is exactly VERDICT: pass only when every upstream perspective emitted VERDICT: pass, otherwise VERDICT: request-revision. No file writes.",
|
|
96
|
+
subtask_prompt: [
|
|
97
|
+
`Aggregate ${input.gateLabel} without changing any review finding.`,
|
|
98
|
+
`Inspect every upstream result: ${reviewIds.join(", ")}.`,
|
|
99
|
+
"First non-empty line must be exactly VERDICT: pass only when every upstream first verdict is VERDICT: pass; otherwise use VERDICT: request-revision.",
|
|
100
|
+
"List each upstream node and its observed verdict as evidence. Do not invent or override a missing/request-revision verdict.",
|
|
101
|
+
"Read-only: do not modify files.",
|
|
102
|
+
].join("\n\n"),
|
|
103
|
+
};
|
|
104
|
+
const gateNode = {
|
|
105
|
+
id: input.gateId,
|
|
106
|
+
depends_on: [aggregateNodeId],
|
|
107
|
+
role: "verifier",
|
|
108
|
+
executor: "shell",
|
|
109
|
+
complexity: "LOW",
|
|
110
|
+
writePolicy: "read-only",
|
|
111
|
+
allowedPaths: commonReadOnlyPaths(input.sources),
|
|
112
|
+
forbiddenPaths: commonForbiddenPaths(input.sources),
|
|
113
|
+
outputContract: `Structured verdict gate: exit 0 only when ${aggregateNodeId} emits VERDICT: pass.`,
|
|
114
|
+
subtask_prompt: `Structured gate for ${input.gateLabel}: all perspectives must pass before downstream apply/promote.`,
|
|
115
|
+
shell: {
|
|
116
|
+
commands: [],
|
|
117
|
+
verdictGate: {
|
|
118
|
+
fromNodeId: aggregateNodeId,
|
|
119
|
+
accept: ["pass"],
|
|
120
|
+
routingAccept: ["request-revision"],
|
|
121
|
+
source: "verdict-line",
|
|
122
|
+
lineMode: "first-verdict-line",
|
|
123
|
+
label: input.gateLabel,
|
|
124
|
+
},
|
|
125
|
+
cwd: ".",
|
|
126
|
+
timeoutMs: 60000,
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
return [...reviewNodes, aggregateNode, gateNode];
|
|
130
|
+
}
|
|
131
|
+
const aggregateScript = buildMultiPerspectiveReviewAggregateScript(reviewIds, input.gateLabel);
|
|
132
|
+
const gateNode = {
|
|
133
|
+
id: input.gateId,
|
|
134
|
+
depends_on: reviewIds,
|
|
135
|
+
role: "verifier",
|
|
136
|
+
executor: "shell",
|
|
137
|
+
complexity: "LOW",
|
|
138
|
+
writePolicy: "read-only",
|
|
139
|
+
allowedPaths: commonReadOnlyPaths(input.sources),
|
|
140
|
+
forbiddenPaths: commonForbiddenPaths(input.sources),
|
|
141
|
+
outputContract: `Deterministic multi-perspective gate: exit 0 only when every review node among ${reviewIds.join(", ")} emits VERDICT: pass.`,
|
|
142
|
+
subtask_prompt: `Aggregate gate for ${input.gateLabel}: all perspectives must pass before downstream apply/promote.`,
|
|
143
|
+
shell: {
|
|
144
|
+
commands: [aggregateScript],
|
|
145
|
+
verifyEvidence: buildVerifyEvidence({
|
|
146
|
+
phase: "final",
|
|
147
|
+
quota: "full",
|
|
148
|
+
commandSource: "inline",
|
|
149
|
+
fallbackCommands: [aggregateScript],
|
|
150
|
+
finalFullRequired: true,
|
|
151
|
+
commandTimeoutMs: 60_000,
|
|
152
|
+
}),
|
|
153
|
+
cwd: ".",
|
|
154
|
+
timeoutMs: 60000,
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
return [...reviewNodes, gateNode];
|
|
158
|
+
}
|
|
159
|
+
export const KNOWLEDGE_SYNC_FEATURE_ID_RE = /^F-[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
160
|
+
export function assertSafeKnowledgeSyncFeatureId(featureId) {
|
|
161
|
+
const trimmed = featureId.trim();
|
|
162
|
+
if (!KNOWLEDGE_SYNC_FEATURE_ID_RE.test(trimmed)) {
|
|
163
|
+
throw new Error(`knowledge-sync featureId must match F-<id> (letters/digits/._- only); got ${JSON.stringify(featureId)}`);
|
|
164
|
+
}
|
|
165
|
+
if (trimmed.includes("..") ||
|
|
166
|
+
trimmed.includes("/") ||
|
|
167
|
+
trimmed.includes("\\")) {
|
|
168
|
+
throw new Error(`knowledge-sync featureId must not contain path segments: ${featureId}`);
|
|
169
|
+
}
|
|
170
|
+
return trimmed;
|
|
171
|
+
}
|
|
172
|
+
export function resolveKnowledgeSyncFeatureId(sources) {
|
|
173
|
+
const candidates = [];
|
|
174
|
+
const fromConfig = sources.taskConfig.featureId?.trim();
|
|
175
|
+
if (fromConfig)
|
|
176
|
+
candidates.push(fromConfig);
|
|
177
|
+
for (const line of sources.taskConfig.hardConstraints ?? []) {
|
|
178
|
+
const match = line.match(/(?:featureId|feature_id)\s*[=:]\s*(F-[A-Za-z0-9][A-Za-z0-9._-]*)/i);
|
|
179
|
+
if (match?.[1])
|
|
180
|
+
candidates.push(match[1]);
|
|
181
|
+
}
|
|
182
|
+
const fromRequirement = sources.requirementMarkdown?.match(/\b(F-\d{4}-\d{2,})\b/);
|
|
183
|
+
if (fromRequirement?.[1])
|
|
184
|
+
candidates.push(fromRequirement[1]);
|
|
185
|
+
if (KNOWLEDGE_SYNC_FEATURE_ID_RE.test(sources.taskId)) {
|
|
186
|
+
candidates.push(sources.taskId);
|
|
187
|
+
}
|
|
188
|
+
for (const candidate of candidates) {
|
|
189
|
+
try {
|
|
190
|
+
return assertSafeKnowledgeSyncFeatureId(candidate);
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// try next candidate
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
throw new Error([
|
|
197
|
+
"knowledge-sync requires an explicit featureId bound to features/<featureId>/.",
|
|
198
|
+
'Set task.json "featureId": "F-YYYY-NNN", or hardConstraints entry featureId=F-…,',
|
|
199
|
+
"or put F-YYYY-NNN in 需求.md, or use a taskId that is already an F-* id.",
|
|
200
|
+
].join(" "));
|
|
201
|
+
}
|
|
202
|
+
export function knowledgeSyncWriteSet(featureId) {
|
|
203
|
+
const id = assertSafeKnowledgeSyncFeatureId(featureId);
|
|
204
|
+
return [
|
|
205
|
+
`features/${id}/testing/**`,
|
|
206
|
+
`features/${id}/requirement-delta.md`,
|
|
207
|
+
"docs/test-reports/**",
|
|
208
|
+
];
|
|
209
|
+
}
|
|
210
|
+
export function knowledgeSyncPendingWriteSet(featureId) {
|
|
211
|
+
const id = assertSafeKnowledgeSyncFeatureId(featureId);
|
|
212
|
+
return [`features/${id}/testing/sync/pending/**`];
|
|
213
|
+
}
|
|
214
|
+
export function knowledgeSyncPointerWriteSet(featureId) {
|
|
215
|
+
const id = assertSafeKnowledgeSyncFeatureId(featureId);
|
|
216
|
+
return [
|
|
217
|
+
`features/${id}/testing/runs/**`,
|
|
218
|
+
`features/${id}/testing/sync/applied/**`,
|
|
219
|
+
];
|
|
220
|
+
}
|
|
221
|
+
export function knowledgeSyncDraftRelPath(featureId) {
|
|
222
|
+
const id = assertSafeKnowledgeSyncFeatureId(featureId);
|
|
223
|
+
return `features/${id}/testing/sync/pending/knowledge-sync-draft.json`;
|
|
224
|
+
}
|
|
225
|
+
export function buildKnowledgeSyncCollectNode(sources, featureId) {
|
|
226
|
+
return {
|
|
227
|
+
id: "knowledge-sync-collect-pi",
|
|
228
|
+
depends_on: [],
|
|
229
|
+
role: "planner",
|
|
230
|
+
executor: "pi",
|
|
231
|
+
complexity: "MED",
|
|
232
|
+
writePolicy: "read-only",
|
|
233
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
234
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
235
|
+
outputContract: "Plain Markdown inventory of final-verification evidence, acceptance criteria, cases, defects, and requirement-delta candidates; no file writes.",
|
|
236
|
+
subtask_prompt: [
|
|
237
|
+
"Collect inputs for knowledge-sync after final verification.",
|
|
238
|
+
`Bound featureId for this run: ${featureId} (directory features/${featureId}/).`,
|
|
239
|
+
`Read task source (需求.md, 执行约束.md, references), features/${featureId}/ testing assets when present, and upstream final verification / pytest / retrospect evidence paths.`,
|
|
240
|
+
"Produce a concise inventory covering: featureId/taskId/runId/headSha, AC list, reviewed cases, automation bindings, open defects, requirement-delta risk, and missing evidence.",
|
|
241
|
+
"Do not claim final verification passed without shell evidence. Read-only: do not modify repository files.",
|
|
242
|
+
buildSourceContextBlock(sources),
|
|
243
|
+
].join("\n\n"),
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
export function buildKnowledgeSyncDraftNode(sources, featureId) {
|
|
247
|
+
const pending = knowledgeSyncPendingWriteSet(featureId);
|
|
248
|
+
const draftPath = knowledgeSyncDraftRelPath(featureId);
|
|
249
|
+
return {
|
|
250
|
+
id: "knowledge-sync-draft-pi",
|
|
251
|
+
depends_on: ["knowledge-sync-collect-pi"],
|
|
252
|
+
role: "implementer",
|
|
253
|
+
executor: "pi",
|
|
254
|
+
toolProfile: "write",
|
|
255
|
+
complexity: "HIGH",
|
|
256
|
+
writePolicy: "exclusive",
|
|
257
|
+
writeSet: pending,
|
|
258
|
+
allowedPaths: pending,
|
|
259
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
260
|
+
subtask_prompt: [
|
|
261
|
+
"Using knowledge-sync-collect-pi inventory and final verification evidence, write the pending knowledge-sync draft for this Feature only.",
|
|
262
|
+
`featureId MUST be exactly ${featureId}. Write only under features/${featureId}/testing/sync/pending/.`,
|
|
263
|
+
`Create ${draftPath} (schemaVersion 1) and features/${featureId}/testing/sync/pending/knowledge-sync-draft.md human summary.`,
|
|
264
|
+
"Draft payload must include: acceptanceVerdict, coverageMatrix, caseIndex, automationMap, defects, requirementDelta (or null), retrospectiveRef, evidencePointers, and operations[] with op/target/risk/reason.",
|
|
265
|
+
`Every operations[].target path must stay under features/${featureId}/ or docs/test-reports/.`,
|
|
266
|
+
"Mark requirement/acceptance body edits as risk=high. Keep raw shell logs as path pointers only.",
|
|
267
|
+
"If final verification is not pass, still write draft but set gates.blockIfFinalVerificationNotPass accordingly and do not invent pass results.",
|
|
268
|
+
"Stay within writeSet. Do not write other features/**, src/**, .harness/**, or knowledge/testing/standards/**.",
|
|
269
|
+
buildSourceContextBlock(sources),
|
|
270
|
+
].join("\n\n"),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
export function buildKnowledgeSyncValidateNode(sources, featureId) {
|
|
274
|
+
const draftPath = knowledgeSyncDraftRelPath(featureId);
|
|
275
|
+
const validateScript = [
|
|
276
|
+
"node",
|
|
277
|
+
"-e",
|
|
278
|
+
JSON.stringify([
|
|
279
|
+
"const fs=require('fs');",
|
|
280
|
+
"const path=require('path');",
|
|
281
|
+
`const featureId=${JSON.stringify(featureId)};`,
|
|
282
|
+
`const file=${JSON.stringify(draftPath)};`,
|
|
283
|
+
"if(!fs.existsSync(file)){ console.error('knowledge-sync validate: missing draft at '+file); process.exit(1); }",
|
|
284
|
+
"let draft; try { draft=JSON.parse(fs.readFileSync(file,'utf8')); } catch(e){ console.error('invalid JSON',file,e.message); process.exit(1); }",
|
|
285
|
+
"let failed=false;",
|
|
286
|
+
"if(draft.schemaVersion!==1){ console.error(file+': schemaVersion must be 1'); failed=true; }",
|
|
287
|
+
"for (const key of ['featureId','taskId','runId','finalVerification','operations','payload','gates']) {",
|
|
288
|
+
" if(draft[key]===undefined||draft[key]===null){ console.error(file+': missing '+key); failed=true; }",
|
|
289
|
+
"}",
|
|
290
|
+
"if(draft.featureId!==featureId){ console.error(file+': featureId must be '+featureId+' got '+draft.featureId); failed=true; }",
|
|
291
|
+
"if(!Array.isArray(draft.operations)||draft.operations.length===0){ console.error(file+': operations must be non-empty array'); failed=true; }",
|
|
292
|
+
"const prefix='features/'+featureId+'/';",
|
|
293
|
+
"for (const op of draft.operations||[]) {",
|
|
294
|
+
" const t=op&&op.target; if(typeof t!=='string'){ console.error('op missing target'); failed=true; continue; }",
|
|
295
|
+
" const ok=t===prefix.slice(0,-1)||t.startsWith(prefix)||t.startsWith('docs/test-reports/');",
|
|
296
|
+
" if(!ok){ console.error('op.target outside feature bind: '+t); failed=true; }",
|
|
297
|
+
"}",
|
|
298
|
+
"const highRisk=(draft.operations||[]).some((op)=>op && op.risk==='high');",
|
|
299
|
+
"if(draft.gates && draft.gates.blockIfFinalVerificationNotPass && draft.finalVerification!=='pass' && draft.finalVerification!=='pass-with-waivers'){",
|
|
300
|
+
" console.error(file+': finalVerification is not pass'); failed=true;",
|
|
301
|
+
"}",
|
|
302
|
+
"if(highRisk && draft.gates && draft.gates.requireHumanIfHighRisk){",
|
|
303
|
+
" console.log(file+': high-risk ops present; human approval required before/while apply');",
|
|
304
|
+
"}",
|
|
305
|
+
"if(failed) process.exit(1);",
|
|
306
|
+
"console.log('knowledge-sync validate ok '+file);",
|
|
307
|
+
].join("")),
|
|
308
|
+
].join(" ");
|
|
309
|
+
return {
|
|
310
|
+
id: "knowledge-sync-validate-shell",
|
|
311
|
+
depends_on: ["knowledge-sync-draft-pi"],
|
|
312
|
+
role: "verifier",
|
|
313
|
+
executor: "shell",
|
|
314
|
+
complexity: "LOW",
|
|
315
|
+
writePolicy: "read-only",
|
|
316
|
+
allowedPaths: [
|
|
317
|
+
...commonReadOnlyPaths(sources),
|
|
318
|
+
`features/${featureId}/testing/sync/pending/**`,
|
|
319
|
+
],
|
|
320
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
321
|
+
outputContract: `Deterministic validation of ${draftPath} (schema, featureId bind, operation targets, finalVerification gate); no worktree writes.`,
|
|
322
|
+
subtask_prompt: `Validate only ${draftPath} for featureId=${featureId} before apply.`,
|
|
323
|
+
shell: {
|
|
324
|
+
commands: [validateScript],
|
|
325
|
+
verifyEvidence: buildVerifyEvidence({
|
|
326
|
+
phase: "final",
|
|
327
|
+
quota: "full",
|
|
328
|
+
commandSource: "inline",
|
|
329
|
+
fallbackCommands: [validateScript],
|
|
330
|
+
finalFullRequired: true,
|
|
331
|
+
commandTimeoutMs: 120_000,
|
|
332
|
+
}),
|
|
333
|
+
cwd: ".",
|
|
334
|
+
timeoutMs: 120000,
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
export const KNOWLEDGE_SYNC_MULTI_REVIEW_PERSPECTIVES = [
|
|
339
|
+
{
|
|
340
|
+
id: "qa",
|
|
341
|
+
perspective: "QA / acceptance",
|
|
342
|
+
focus: [
|
|
343
|
+
"acceptanceVerdict and AC coverage vs evidencePointers",
|
|
344
|
+
"caseIndex completeness and non-invented pass results",
|
|
345
|
+
"defects registry consistency with open issues",
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
id: "domain",
|
|
350
|
+
perspective: "domain / product",
|
|
351
|
+
focus: [
|
|
352
|
+
"requirement-delta risk and silent requirement rewrites",
|
|
353
|
+
"operations[] targets stay under the bound featureId",
|
|
354
|
+
"business meaning of coverage/matrix changes",
|
|
355
|
+
],
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
id: "evidence",
|
|
359
|
+
perspective: "evidence / audit",
|
|
360
|
+
focus: [
|
|
361
|
+
"finalVerification authority is shell evidence, not prose",
|
|
362
|
+
"high-risk ops and gates.requireHumanIfHighRisk",
|
|
363
|
+
"draft schema fields and pointer-only log policy",
|
|
364
|
+
],
|
|
365
|
+
},
|
|
366
|
+
];
|
|
367
|
+
export function buildKnowledgeSyncMultiReviewNodes(sources, featureId) {
|
|
368
|
+
return buildMultiPerspectiveReviewNodes({
|
|
369
|
+
sources,
|
|
370
|
+
dependsOn: ["knowledge-sync-validate-shell"],
|
|
371
|
+
nodePrefix: "knowledge-sync-review-",
|
|
372
|
+
gateId: "knowledge-sync-multi-review-gate-shell",
|
|
373
|
+
gateLabel: "knowledge-sync multi-perspective review",
|
|
374
|
+
perspectives: KNOWLEDGE_SYNC_MULTI_REVIEW_PERSPECTIVES,
|
|
375
|
+
allowedPaths: [
|
|
376
|
+
...commonReadOnlyPaths(sources),
|
|
377
|
+
`features/${featureId}/**`,
|
|
378
|
+
"docs/test-reports/**",
|
|
379
|
+
],
|
|
380
|
+
sharedBrief: [
|
|
381
|
+
`Bound featureId: ${featureId}. Only review draft/ops for this Feature.`,
|
|
382
|
+
`Primary draft path: ${knowledgeSyncDraftRelPath(featureId)}.`,
|
|
383
|
+
"Apply is blocked until all perspectives pass. Do not approve fabricated verification pass.",
|
|
384
|
+
],
|
|
385
|
+
structuredVerdictGate: true,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
export function buildKnowledgeSyncApplyNode(sources, featureId) {
|
|
389
|
+
const writeSet = knowledgeSyncWriteSet(featureId);
|
|
390
|
+
const draftPath = knowledgeSyncDraftRelPath(featureId);
|
|
391
|
+
return {
|
|
392
|
+
id: "knowledge-sync-apply-pi",
|
|
393
|
+
depends_on: ["knowledge-sync-multi-review-gate-shell"],
|
|
394
|
+
role: "implementer",
|
|
395
|
+
executor: "pi",
|
|
396
|
+
toolProfile: "write",
|
|
397
|
+
complexity: "HIGH",
|
|
398
|
+
writePolicy: "exclusive",
|
|
399
|
+
writeSet,
|
|
400
|
+
allowedPaths: writeSet,
|
|
401
|
+
forbiddenPaths: [
|
|
402
|
+
...commonForbiddenPaths(sources),
|
|
403
|
+
"src/**",
|
|
404
|
+
"knowledge/testing/standards/**",
|
|
405
|
+
"conftest.py",
|
|
406
|
+
"pytest.ini",
|
|
407
|
+
"pyproject.toml",
|
|
408
|
+
],
|
|
409
|
+
subtask_prompt: [
|
|
410
|
+
"Apply the validated knowledge-sync draft into the Feature testing knowledge base (L1).",
|
|
411
|
+
`Bound featureId: ${featureId}. Only touch features/${featureId}/ and docs/test-reports/** (writeSet is exclusive).`,
|
|
412
|
+
`From ${draftPath}, upsert under features/${featureId}/:`,
|
|
413
|
+
"- testing/acceptance-verdict.yaml",
|
|
414
|
+
"- testing/coverage-matrix.yaml",
|
|
415
|
+
"- testing/cases/** and index.yaml when payload includes caseIndex",
|
|
416
|
+
"- testing/automation-map.yaml",
|
|
417
|
+
"- testing/defects/registry.yaml and open.md",
|
|
418
|
+
"- testing/closeout-testing.md and retrospectives when provided",
|
|
419
|
+
`- features/${featureId}/requirement-delta.md only as draft/proposal unless operations mark approved product change`,
|
|
420
|
+
"Never modify other features/**, knowledge/testing/standards/**, src/**, or pytest framework files.",
|
|
421
|
+
"Do not invent AC pass without evidencePointers. Keep raw logs as pointers only.",
|
|
422
|
+
"If high-risk requirement changes lack approval, write requirement-delta.md as draft and do not merge into requirement.md/acceptance.yaml.",
|
|
423
|
+
"Stay within writeSet.",
|
|
424
|
+
buildSourceContextBlock(sources),
|
|
425
|
+
].join("\n\n"),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
export function buildKnowledgeSyncPointerNode(sources, featureId) {
|
|
429
|
+
const writeSet = knowledgeSyncPointerWriteSet(featureId);
|
|
430
|
+
return {
|
|
431
|
+
id: "knowledge-sync-pointer-pi",
|
|
432
|
+
depends_on: ["knowledge-sync-apply-pi"],
|
|
433
|
+
role: "closeout",
|
|
434
|
+
executor: "pi",
|
|
435
|
+
toolProfile: "write",
|
|
436
|
+
complexity: "MED",
|
|
437
|
+
writePolicy: "exclusive",
|
|
438
|
+
writeSet,
|
|
439
|
+
allowedPaths: writeSet,
|
|
440
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
441
|
+
subtask_prompt: [
|
|
442
|
+
"Write knowledge-sync audit pointers after apply.",
|
|
443
|
+
`Update features/${featureId}/testing/runs/latest.md with runId, headSha, finalVerification, maturity, evidence paths, and applied artifact refs.`,
|
|
444
|
+
`Write features/${featureId}/testing/sync/applied/KS-<timestamp>.json summarizing applied operations and source draft hash/path.`,
|
|
445
|
+
"Do not rewrite acceptance-verdict or cases here unless only adding pointer fields already applied.",
|
|
446
|
+
"Stay within writeSet. Do not touch other features. Read-only regarding src/** and .harness/**.",
|
|
447
|
+
buildSourceContextBlock(sources),
|
|
448
|
+
].join("\n\n"),
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
export function buildKnowledgeSyncHybridDag(sources) {
|
|
452
|
+
const { taskConfig } = sources;
|
|
453
|
+
const featureId = resolveKnowledgeSyncFeatureId(sources);
|
|
454
|
+
const globalConstraints = [
|
|
455
|
+
...taskConfig.hardConstraints,
|
|
456
|
+
...(sources.constraintMarkdown
|
|
457
|
+
? [`See 执行约束.md in task source (${sources.taskId})`]
|
|
458
|
+
: []),
|
|
459
|
+
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
460
|
+
`knowledge-sync-dag is bound to featureId=${featureId}; writes only features/${featureId}/testing/**, features/${featureId}/requirement-delta.md, and docs/test-reports/**.`,
|
|
461
|
+
"knowledge-sync must not modify other features/**, src/**, .harness/**, knowledge/testing/standards/**, or pytest framework files.",
|
|
462
|
+
"Apply is blocked unless knowledge-sync-validate-shell and multi-perspective review gate pass; final verification evidence remains the completion authority.",
|
|
463
|
+
"Multi-perspective review: QA/acceptance, domain/product, and evidence/audit must each emit VERDICT: pass before apply.",
|
|
464
|
+
"High-risk requirement/acceptance body changes require human approval via requirement-delta; do not silently rewrite requirement.md.",
|
|
465
|
+
"Raw shell logs stay as path pointers; knowledge base stores stable facts only.",
|
|
466
|
+
"Prefer structured YAML/Markdown L1 knowledge pack over vector-store-only writes.",
|
|
467
|
+
];
|
|
468
|
+
const multiReview = buildKnowledgeSyncMultiReviewNodes(sources, featureId);
|
|
469
|
+
const spec = {
|
|
470
|
+
version: 2,
|
|
471
|
+
title: `Knowledge-sync DAG (${featureId}): ${taskConfig.title}`,
|
|
472
|
+
outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
|
|
473
|
+
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
474
|
+
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
475
|
+
globalConstraints,
|
|
476
|
+
defaults: {
|
|
477
|
+
...KNOWLEDGE_SYNC_DEFAULTS,
|
|
478
|
+
contextProfile: taskConfig.contextProfile,
|
|
479
|
+
},
|
|
480
|
+
skillsByRole: KNOWLEDGE_SYNC_SKILLS_BY_ROLE,
|
|
481
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
482
|
+
tasks: [
|
|
483
|
+
buildKnowledgeSyncCollectNode(sources, featureId),
|
|
484
|
+
buildKnowledgeSyncDraftNode(sources, featureId),
|
|
485
|
+
buildKnowledgeSyncValidateNode(sources, featureId),
|
|
486
|
+
...multiReview,
|
|
487
|
+
buildKnowledgeSyncApplyNode(sources, featureId),
|
|
488
|
+
buildKnowledgeSyncPointerNode(sources, featureId),
|
|
489
|
+
],
|
|
490
|
+
};
|
|
491
|
+
stampGeneratedArtifactBindings(spec);
|
|
492
|
+
parseDagSpec(spec);
|
|
493
|
+
assertValidDagSpec(spec);
|
|
494
|
+
return spec;
|
|
495
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { DEFAULT_READ_ONLY_PI_RETRY_POLICY, PLANNER_OUTPUT_LIMIT_RETRY_POLICY, PROTOCOL_AWARE_PI_RETRY_POLICY, TARGET_TEMPLATE_WRITER_TRANSPORT_RETRY_POLICY, WRITER_TRANSPORT_RETRY_POLICY, isSafeReadOnlyPiRetryCandidate, isTargetTemplateImplementPi, isWriterTransportRetryCandidate } from "../../retry-policy.js";
|
|
2
|
+
import { FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID } from "../../frontend-implementation-contract.js";
|
|
3
|
+
import { mergeForbiddenPaths } from "../sources.js";
|
|
4
|
+
export function commonReadOnlyPaths(sources) {
|
|
5
|
+
return sources.taskConfig.allowedPaths.length > 0
|
|
6
|
+
? sources.taskConfig.allowedPaths
|
|
7
|
+
: ["**"];
|
|
8
|
+
}
|
|
9
|
+
export function commonForbiddenPaths(sources) {
|
|
10
|
+
return mergeForbiddenPaths(sources.taskConfig);
|
|
11
|
+
}
|
|
12
|
+
export function applyDefaultReadOnlyRetryPolicy(spec) {
|
|
13
|
+
for (const task of spec.tasks) {
|
|
14
|
+
if (task.retryPolicy !== undefined)
|
|
15
|
+
continue;
|
|
16
|
+
if (isSafeReadOnlyPiRetryCandidate(task)) {
|
|
17
|
+
task.retryPolicy = task.outputProtocol
|
|
18
|
+
? PROTOCOL_AWARE_PI_RETRY_POLICY
|
|
19
|
+
: task.role === "planner"
|
|
20
|
+
? PLANNER_OUTPUT_LIMIT_RETRY_POLICY
|
|
21
|
+
: DEFAULT_READ_ONLY_PI_RETRY_POLICY;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (isTargetTemplateImplementPi(task)) {
|
|
25
|
+
task.retryPolicy = TARGET_TEMPLATE_WRITER_TRANSPORT_RETRY_POLICY;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (isWriterTransportRetryCandidate(task)) {
|
|
29
|
+
task.retryPolicy = WRITER_TRANSPORT_RETRY_POLICY;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function stampGeneratedArtifactBindings(spec) {
|
|
34
|
+
const producedByNode = new Map();
|
|
35
|
+
for (const task of spec.tasks) {
|
|
36
|
+
if ((task.producesArtifacts?.length ?? 0) > 0) {
|
|
37
|
+
for (const artifact of task.producesArtifacts ?? []) {
|
|
38
|
+
producedByNode.set(task.id, artifact.artifactId);
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const gate = task.shell?.jsonArtifactGate;
|
|
43
|
+
const structured = task.structuredContractOutput;
|
|
44
|
+
if (!gate && !structured)
|
|
45
|
+
continue;
|
|
46
|
+
const artifactId = `${task.id}.structured-output`;
|
|
47
|
+
const path = gate
|
|
48
|
+
? `${gate.outputDir}/${gate.artifactName}`
|
|
49
|
+
: `contracts/candidates/${task.id}/canonical-contract.json`;
|
|
50
|
+
task.producesArtifacts = [
|
|
51
|
+
{
|
|
52
|
+
artifactId,
|
|
53
|
+
kind: "structured",
|
|
54
|
+
path,
|
|
55
|
+
mediaType: "application/json",
|
|
56
|
+
schemaId: gate?.schemaId ?? FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID,
|
|
57
|
+
revisionPolicy: "replace-file",
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
producedByNode.set(task.id, artifactId);
|
|
61
|
+
}
|
|
62
|
+
for (const task of spec.tasks) {
|
|
63
|
+
const existing = new Set((task.consumesArtifacts ?? []).map((binding) => binding.artifactId));
|
|
64
|
+
const generated = task.depends_on.flatMap((parentNodeId) => {
|
|
65
|
+
const artifactId = producedByNode.get(parentNodeId);
|
|
66
|
+
if (!artifactId || existing.has(artifactId))
|
|
67
|
+
return [];
|
|
68
|
+
existing.add(artifactId);
|
|
69
|
+
return [
|
|
70
|
+
{
|
|
71
|
+
artifactId,
|
|
72
|
+
required: task.dependsPolicy !== "all-or-condition-skip",
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
});
|
|
76
|
+
if (generated.length > 0) {
|
|
77
|
+
task.consumesArtifacts = [...(task.consumesArtifacts ?? []), ...generated];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
export function mapTaskComplexity(complexity) {
|
|
82
|
+
if (complexity === "small")
|
|
83
|
+
return "LOW";
|
|
84
|
+
if (complexity === "large")
|
|
85
|
+
return "HIGH";
|
|
86
|
+
return "MED";
|
|
87
|
+
}
|
|
88
|
+
export function isFrontendTestPathPattern(entry) {
|
|
89
|
+
const segments = entry.replaceAll("\\", "/").split("/");
|
|
90
|
+
return segments.some((segment) => /^(?:test|tests|spec|specs|e2e|__tests__|__specs__)$/i.test(segment));
|
|
91
|
+
}
|
|
92
|
+
export function replaceSourceSegmentWithTestSegment(entry) {
|
|
93
|
+
const normalized = entry.replaceAll("\\", "/");
|
|
94
|
+
const segments = normalized.split("/");
|
|
95
|
+
const sourceIndex = segments.findIndex((segment) => /^(?:src|source|app|lib)$/i.test(segment));
|
|
96
|
+
if (sourceIndex < 0)
|
|
97
|
+
return null;
|
|
98
|
+
const testSegments = [...segments];
|
|
99
|
+
testSegments[sourceIndex] = "test";
|
|
100
|
+
return testSegments.join("/");
|
|
101
|
+
}
|