@tea-agent/loop-agent 0.25.5 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +1020 -986
- package/bin/loop-agent.js +21 -21
- package/dist/commands/cursor-prompt.js +6 -6
- package/dist/commands/init.js +68 -129
- package/dist/commands/loop-benchmark.js +11 -11
- package/dist/commands/pi-reuse-benchmark.js +16 -16
- package/dist/executors/dag-pi-executor.js +26 -20
- package/dist/executors/model-routing.js +34 -18
- package/dist/executors/shell-executor.js +1 -1
- package/dist/governance/manifest-types.js +33 -5
- package/dist/sidecars/cursor-prompt/executor.js +1 -1
- package/dist/task/task-demand-routing.js +3 -1
- package/dist/worker/console/chat/model-resolver.js +15 -3
- package/dist/worker/observe/static/constants.js +3 -2
- package/dist/worker/observe/static/copy.js +67 -67
- package/dist/worker/observe/static/dag-layout.d.ts +31 -31
- package/dist/worker/observe/static/dag-layout.js +83 -83
- package/dist/worker/observe/static/dag-model.js +1 -0
- package/dist/worker/observe/static/dom.js +220 -220
- package/dist/worker/observe/static/relations.js +133 -133
- package/dist/worker/observe/static/router.js +93 -93
- package/dist/worker/observe/static/run-processing.js +148 -148
- package/dist/worker/observe/static/styles.css +182 -42
- package/dist/worker/observe/static/views/batch.js +227 -227
- package/dist/worker/observe/static/views/dag-graph.js +172 -172
- package/dist/worker/observe/static/views/failures.js +143 -143
- package/dist/worker/observe/static/views/feature.js +492 -492
- package/dist/worker/observe/static/views/run.js +453 -453
- package/dist/worker/observe/static/views/shell.js +7 -7
- package/dist/worker/observe/static/views/timeline.js +163 -163
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +362 -45
- package/dist/workflows/dag/backend-test-case-manifest.js +20 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +17 -0
- package/dist/workflows/dag/canvas-observer.js +275 -275
- package/dist/workflows/dag/init-hybrid.js +18 -15
- package/dist/workflows/dag/lifecycle.js +40 -30
- package/dist/workflows/dag/node-execution.js +13 -0
- package/dist/workflows/dag/types.js +59 -19
- package/docs/init-surface.manifest.json +3 -1
- package/docs/skills/README.md +7 -7
- package/docs/templates/README.md +1 -0
- package/docs/templates/adr.md +60 -60
- package/docs/templates/agent-dag-authority-surface-audit.prompt.md +94 -94
- package/docs/templates/agent-dag-decision-envelope.schema.json +213 -213
- package/docs/templates/agent-dag-decision-gate.prompt.md +246 -246
- package/docs/templates/agent-dag-process-supervisor.prompt.md +98 -98
- package/docs/templates/agent-dag-report.schema.json +473 -473
- package/docs/templates/agent-dag-review-verdict.prompt.md +68 -68
- package/docs/templates/backend-test-dag.json +9 -9
- package/docs/templates/backend-test-result.schema.json +99 -99
- package/docs/templates/feature-spec.md +53 -53
- package/docs/templates/frontend-design-contract.md +42 -42
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -17
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -16
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -21
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -29
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -27
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -28
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -27
- package/docs/templates/frontend-eval/metrics.md +138 -138
- package/docs/templates/frontend-eval/smoke-targets.md +53 -53
- package/docs/templates/frontend-task-constraints.md +35 -35
- package/docs/templates/frontend-task-requirement.md +70 -70
- package/docs/templates/harness.schema.json +29 -7
- package/docs/templates/init-evolution-review.md +35 -35
- package/docs/templates/init-managed-agents.md +137 -0
- package/docs/templates/interactive-ui-round2-experiment.md +66 -66
- package/docs/templates/knowledge-graph-bootstrap-dag.json +118 -118
- package/docs/templates/knowledge-sync-dag.json +178 -178
- package/docs/templates/knowledge-sync-draft.schema.json +71 -71
- package/docs/templates/product-line/closeout.yaml +9 -9
- package/docs/templates/product-line/design.md +13 -13
- package/docs/templates/product-line/links.md +10 -10
- package/docs/templates/product-line/requirement.md +17 -17
- package/docs/templates/product-line/test-plan.md +7 -7
- package/docs/templates/production-readiness-checklist.md +57 -57
- package/docs/templates/project-start-checklist.md +9 -9
- package/docs/templates/qa-report.md +48 -48
- package/docs/templates/sprint-contract.md +29 -29
- package/docs/templates/worker-dogfood-evidence.md +80 -80
- package/docs/templates/worker-dogfood-setup.md +68 -68
- package/harness.json +1 -2
- package/package.json +1 -1
- package/scripts/kb-bootstrap-init-skeleton.sh +0 -0
- package/scripts/kb-graph-incremental-prepare.mjs +386 -386
- package/scripts/kb-graph-materialize.mjs +105 -105
- package/scripts/kb-graph-promote.mjs +164 -164
- package/scripts/kb-query.mjs +554 -554
- package/skills/ai-engineering-context/SKILL.md +48 -48
- package/skills/analyze-product-dependencies/SKILL.md +67 -67
- package/skills/analyze-product-dependencies/agents/openai.yaml +4 -4
- package/skills/analyze-product-dependencies/references/api-documentation-schema.md +30 -30
- package/skills/analyze-product-dependencies/references/dependency-analysis-schema.md +28 -28
- package/skills/analyze-product-dependencies/references/example.md +76 -76
- package/skills/analyze-product-dependencies/references/forward-test-cases.md +35 -35
- package/skills/analyze-product-dependencies/references/input-contract.md +11 -11
- package/skills/analyze-product-dependencies/references/scouting-rules.md +61 -61
- package/skills/analyze-product-dependencies/scripts/test-validators.mjs +267 -267
- package/skills/analyze-product-dependencies/scripts/validate-api-documentation.mjs +101 -101
- package/skills/analyze-product-dependencies/scripts/validate-dependency-analysis.mjs +142 -142
- package/skills/analyze-product-dependencies/scripts/validate-product-requirement-input.mjs +76 -76
- package/skills/analyze-product-dependencies/scripts/validation-helpers.mjs +146 -146
- package/skills/analyze-product-requirements/SKILL.md +90 -90
- package/skills/analyze-product-requirements/agents/openai.yaml +4 -4
- package/skills/analyze-product-requirements/references/acceptance-criteria.md +91 -91
- package/skills/analyze-product-requirements/references/clarification-and-knowledge.md +56 -56
- package/skills/analyze-product-requirements/references/example.md +86 -86
- package/skills/analyze-product-requirements/references/forward-test-cases.md +66 -66
- package/skills/analyze-product-requirements/references/product-analysis-schema.md +32 -32
- package/skills/analyze-product-requirements/references/product-requirement-schema.md +33 -33
- package/skills/analyze-product-requirements/references/requirement-clarification-schema.md +35 -35
- package/skills/analyze-product-requirements/scripts/test-validators.mjs +193 -193
- package/skills/analyze-product-requirements/scripts/validate-product-analysis.mjs +69 -69
- package/skills/analyze-product-requirements/scripts/validate-product-requirement.mjs +97 -97
- package/skills/analyze-product-requirements/scripts/validate-requirement-clarification.mjs +98 -98
- package/skills/analyze-product-requirements/scripts/validation-helpers.mjs +156 -156
- package/skills/browser-tools/browser-content.js +103 -103
- package/skills/browser-tools/browser-cookies.js +35 -35
- package/skills/browser-tools/browser-eval.js +53 -53
- package/skills/browser-tools/browser-hn-scraper.js +108 -108
- package/skills/browser-tools/browser-nav.js +44 -44
- package/skills/browser-tools/browser-pick.js +162 -162
- package/skills/browser-tools/browser-screenshot.js +34 -34
- package/skills/browser-tools/browser-start.js +86 -86
- package/skills/browser-tools/package-lock.json +2556 -2556
- package/skills/browser-tools/package.json +19 -19
- package/skills/code-review-core/SKILL.md +20 -20
- package/skills/codebase-scout/SKILL.md +19 -19
- package/skills/grill-me/SKILL.md +10 -10
- package/skills/loop-agent/references/README.md +67 -67
- package/skills/loop-agent/references/command-reference.md +5 -4
- package/skills/loop-agent/references/docs-converge.md +126 -126
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/skills/loop-agent/references/learned/README.md +21 -21
- package/skills/loop-agent/references/long-running-loop.md +57 -57
- package/skills/loop-agent/references/model-routing.md +2 -0
- package/skills/loop-agent/references/one-shot-runs.md +85 -85
- package/skills/loop-agent/references/pi-prompt.md +23 -23
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +84 -84
- package/skills/playwright-cli/SKILL.md +420 -420
- package/skills/playwright-cli/references/element-attributes.md +23 -23
- package/skills/playwright-cli/references/playwright-tests.md +39 -39
- package/skills/playwright-cli/references/request-mocking.md +87 -87
- package/skills/playwright-cli/references/running-code.md +241 -241
- package/skills/playwright-cli/references/session-management.md +225 -225
- package/skills/playwright-cli/references/storage-state.md +275 -275
- package/skills/playwright-cli/references/test-generation.md +433 -433
- package/skills/playwright-cli/references/tracing.md +139 -139
- package/skills/playwright-cli/references/video-recording.md +143 -143
- package/skills/requesting-code-review/SKILL.md +101 -101
- package/skills/requesting-code-review/code-reviewer.md +168 -168
- package/skills/systematic-debugging/CREATION-LOG.md +119 -119
- package/skills/systematic-debugging/condition-based-waiting-example.ts +158 -158
- package/skills/systematic-debugging/condition-based-waiting.md +115 -115
- package/skills/systematic-debugging/defense-in-depth.md +122 -122
- package/skills/systematic-debugging/find-polluter.sh +63 -63
- package/skills/systematic-debugging/root-cause-tracing.md +169 -169
- package/skills/systematic-debugging/test-academic.md +14 -14
- package/skills/systematic-debugging/test-pressure-1.md +58 -58
- package/skills/systematic-debugging/test-pressure-2.md +68 -68
- package/skills/systematic-debugging/test-pressure-3.md +69 -69
- package/skills/using-git-worktrees/SKILL.md +215 -215
- package/skills/verification-before-completion/SKILL.md +154 -154
- package/skills/webapp-testing/SKILL.md +19 -19
|
@@ -3508,14 +3508,16 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3508
3508
|
"Read the upstream environment report. Generate a Markdown-first backend test strategy and cases under testcase/md/**.",
|
|
3509
3509
|
"Write human-readable content in Simplified Chinese by default. Keep English only for machine-readable IDs and technical literals such as Case/AC/REQ/BR IDs, HTTP methods, paths, field names, enum values, commands, filenames, code symbols and exact source citations.",
|
|
3510
3510
|
"Create testcase/md/README.md as the concise entry page: test objective, target/environment, isolation/cleanup, module summary and a linked case index table with Case ID, Chinese case name, scenario type, endpoint and expected status/result. Avoid repeating every case body in README.",
|
|
3511
|
-
"Before
|
|
3511
|
+
"Before the Coverage Matrix, write a mandatory machine-readable `## Coverage Scope` section in README using exactly `| Field | Value |`, immediately followed by the separator row `|---|---|`, and these six unique rows: `Change Classification`, `Coverage Policy`, `Affected Operations`, `Affected Rule Keys`, `Regression Floor`, `Scope Evidence`. Classify from authoritative task/reference evidence, not merely whether a route already exists. Use only these pairs: `new-operation` → `full-contract`; `contract-change` → `affected-contract-full`; `behavior-change` → `affected-behavior-full`; `bugfix` → `reproduction-plus-neighbors`; `implementation-optimization` → `change-focused-plus-regression-floor`. List affected operations exactly as `METHOD /path`, stable rule keys separated by semicolons, and precise source pointers as Scope Evidence.",
|
|
3512
|
+
"Coverage depth follows the declared change scope. For `new-operation`, fully cover every documented status, request/response field rule, requiredness, enum, boundary, format, auth and business state of each affected new operation, but do not re-test unrelated existing operations. For contract/behavior changes, fully cover the changed contract or behavior and its directly affected operations. For bugfix, cover exact reproduction, adjacent boundary/equivalence cases and a normal path. For `implementation-optimization`, cover explicit ACs, deterministic affected operations and a minimum regression floor; do not exhaustively regenerate unrelated POST/PUT/GET/DELETE rules. Every non-new classification must include `main-success-path` and `unchanged-response-shape`; contract changes also include `changed-contract-boundaries`, behavior changes `affected-state-transition`, and bugfixes `defect-reproduction` plus `adjacent-boundary`. Inspect shared validator/helper/DTO/query builder evidence and expand Affected Operations when the same changed path can affect them; unresolved impact stays visible as GAP/CONFLICT.",
|
|
3513
|
+
"Before writing cases, build the mandatory machine-readable Coverage Matrix inside `testcase/md/README.md` itself. Its section heading line must be exactly `## Coverage Matrix` with no numeric prefix/suffix; never place the canonical Matrix only in a module file. Use this exact header: `| Rule Key | Priority | Source | Endpoint/Field | Dimension | Rule | Required Test Points | Case IDs | Status |`. Every data row must contain exactly 9 pipe-delimited cells and must never omit `Dimension`; use concise dimensions such as requirement, operation, response-status, requiredness, enum, boundary, format, business-state or error. Use only P0/P1/P2 and COVERED/PARTIAL/GAP/CONFLICT. Use stable `TP-<UPPERCASE-HYPHENATED-ID>` test points separated by semicolons.",
|
|
3512
3514
|
"Each Rule Key must appear in exactly one Matrix row. Preserve each AC/REQ/BR Rule Key as one row; if one product rule spans multiple dimensions, use a concise composite Dimension in that single row instead of duplicating the key. Derive OpenAPI Rule Keys exactly as the deterministic analyzer does: operation token is `<HTTP-METHOD>-<PATH>` with braces removed and every non-alphanumeric run replaced by a hyphen, uppercase (for example POST `/api/resource-notes` → `POST-API-RESOURCE-NOTES`); response statuses use `API-<OPERATION>-RESPONSE-STATUS`; body/parameter fields use `API-<OPERATION>-<FIELD>-REQUIRED|ENUM|MIN-LENGTH|MAX-LENGTH|MINIMUM|MAXIMUM|PATTERN|FORMAT`. Do not invent aliases such as API-CREATE-FIELDS when a deterministic key applies.",
|
|
3513
|
-
"Coverage priority is strict: P0 product requirements/task hard constraints
|
|
3515
|
+
"Coverage priority is strict inside the declared scope: P0 product requirements/task hard constraints always remain in scope; P1 exhaustively supplements documented operations, fields, business rules, statuses and errors only for Affected Operations; P2 adds bounded protocol robustness only when it is relevant to the change and does not invent product behavior. Coverage percentages describe the declared affected scope, never whole-API completeness unless every operation is explicitly listed. Conflicts or undefined expectations must stay visible as GAP/CONFLICT with precise source pointers, never guessed.",
|
|
3514
3516
|
"For uniqueness/lifecycle rules cover absent, active-existing, deleted-existing, create-delete-recreate, restore-then-recreate and documented scope/case-normalization states. For every enum cover every valid value plus bounded invalid equivalence classes (unknown, case variant, whitespace, empty, null/missing and wrong types as applicable). For every length/number rule cover min-1, min, nominal, max and max+1. For format rules cover each allowed class separately plus a valid mixed value, and representative forbidden classes including uppercase, internal/leading/trailing whitespace, tab/newline, unsupported punctuation, slash, emoji or control characters when the source contract supports that expectation.",
|
|
3515
|
-
"Write each module as readable case cards. Every case starts with `## BE-<MODULE>-<NNN>|<中文用例名称>`. `<NNN>` is exactly three zero-padded digits (`001`, `002`, ...), never two digits (`01`)
|
|
3517
|
+
"Write each module as readable case cards. Every case starts with `## BE-<MODULE>-<NNN>|<中文用例名称>`. `<NNN>` is exactly three zero-padded digits (`001`, `002`, ...), never two digits (`01`), a bare number, or an alphabetic suffix such as `011A`. Every case must include `### 覆盖规则`, `### 测试点`, `### 场景类型`, `### 前置条件`, `### 操作步骤`, `### 预期结果`, and `### 自动化映射`; `覆盖规则` and `测试点` must reference exact Matrix Rule Keys/Test Points. Add `测试目的`, `验收标准`, `需求依据`, and `测试数据` for readable evidence. The `验收标准` section must list the exact applicable `AC-...` IDs, and every explicit task AC must appear in at least one Case. Every automatable case explicitly names its target pytest script and exactly one primary symbol so traceability scans only that script/symbol.",
|
|
3516
3518
|
"Name each module file with a stable lowercase business stem such as `testcase/md/health.md` or `testcase/md/resource_notes.md`. Do not use Case-ID-like module filenames such as `BE-HEALTH.md` or `BE-NOTES.md`. For every automatable case, `自动化映射` must name exactly `testcase/test_<module>.py`, where <module> is that Markdown filename without `.md`, lowercased, with non-alphanumeric characters replaced by underscores. Example: `testcase/md/health.md` → `testcase/test_health.py`; `testcase/md/resource_notes.md` → `testcase/test_resource_notes.py`. Never invent a different pytest path in Markdown than the module stem implies.",
|
|
3517
|
-
"
|
|
3518
|
-
"In `自动化映射`,
|
|
3519
|
+
"Every Case must keep at least one numbered executable line under `### 操作步骤`; a compact variant/result table may follow but must not replace the numbered action anchor. Keep numbered/bulleted independently assertable results under `### 预期结果`. The exact `### 操作步骤` and `### 预期结果` headings must remain present for every Case, including compact/table-based Cases; never compress later Cases by dropping required headings. Every result must name the observable HTTP status, response field/value, state transition or membership condition, never vague wording such as ‘符合预期’.",
|
|
3520
|
+
"In every `自动化映射`, use exactly these machine-readable list labels: `脚本`, `primary symbol`, `变体测试点`, `场景断言测试点`, `横切证据测试点`. Each Test Point from `### 测试点` must appear in exactly one binding list, and every Test Point named in any binding list must also be declared in that Case's `### 测试点`; write `无` for an empty list. A variant Test Point is atomic: one exact endpoint/input/precondition/outcome row equals one exact pytest item and one exact TP ID. If a parameter table has five rows, declare five distinct variant TP IDs in Markdown; never declare one family TP and append row suffixes only in pytest. Classify as `variant` only when endpoint, request input, precondition business state, or expected outcome genuinely changes and therefore needs an independent pytest parameter item. Classify CRUD checkpoints, status/body/header/schema assertions and multiple checks over the same response/journey as `assertion`; classify shared HTTP logging/redaction/truncation evidence as `cross-cutting`. Never create a Test Point merely to parameterize a checkpoint. Every non-cross-cutting TP ID is owned by exactly one Case; when the same response/schema/error assertion is needed in different Cases, use distinct Case-specific TP IDs instead of reusing one assertion TP across Cases. Keep the script path identical to the module one-to-one path and declare exactly one primary symbol named with the canonical Case prefix, for example `BE-RN-003` → `test_BE_RN_003_<description>`; non-Case-prefixed primary symbols are forbidden because parameterized item association must remain deterministic. For redaction scenarios, list sensitive header/field key names only. Never write any header-name-and-value pair, credential placeholder, fake token, anti-example, or other secret-shaped literal in Markdown; state only that a test-only value is supplied at runtime and omitted. Put implementation-only restrictions in a concise `<details>` block rather than dominating the main case flow. Before finalizing Markdown, calculate the predicted collected-item count as `sum(max(1, number of variant Test Points in each Case))`. If the task declares an item budget, the prediction must not exceed it. Reduce excess only by removing duplicate execution and converting same-request checkpoints to assertions; never drop required rules, boundaries, enums, operation-specific inputs, or business states. Record the prediction in README. Use only environment-supported fixtures/targets/isolation, record evidence gaps in Chinese, and do not emit JSON, pytest, or execute commands.",
|
|
3519
3521
|
intake.boundedSourceContext,
|
|
3520
3522
|
"## Authoritative reference index",
|
|
3521
3523
|
JSON.stringify(intake.referenceIndex, null, 2),
|
|
@@ -3537,9 +3539,9 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3537
3539
|
outputContract: "Review source fidelity and directly revise only testcase/md/**; return concise Markdown, never JSON.",
|
|
3538
3540
|
subtask_prompt: [
|
|
3539
3541
|
"Independently review generated Markdown cases against the task requirements and environment evidence. Treat the files as human-facing test documentation: require clear preconditions, executable steps and assertable expected results; improve names, purpose, metadata and automation mapping where useful while preserving exact machine IDs and technical literals.",
|
|
3540
|
-
"Independently reconstruct P0 product scenarios and P1 documented API rules from authoritative sources before trusting the generated Coverage Matrix.
|
|
3541
|
-
"Check AC completeness/meaning, endpoint, fields/shape, status/error codes, rules, states, documented boundaries/auth, positive/negative coverage, executable steps and assertable results. Require the exact unnumbered `## Coverage Matrix` heading
|
|
3542
|
-
"Correct testcase/md/** directly: add documented omissions, remove unsupported cases, rename module files to stable lowercase stems when needed, normalize every Case ID to hyphen-separated module segments plus exactly three zero-padded digits (`BE-RESOURCE_NOTES-01` → `BE-RESOURCE-NOTES-001`) consistently across headings/index/mappings, fix automation mappings so each case points at `testcase/test_<module>.py` derived from that module filename, merge duplicates, improve navigation/tables/Chinese wording, or record gaps in Chinese. Keep Case IDs, AC/REQ/BR IDs, HTTP methods, paths, fields, enum values, filenames, code symbols and source citations as exact machine-readable identifiers; only normalize Case ID separator/sequence formatting as specified above. The validator accepts Chinese and legacy English section aliases; retain or converge to the Chinese human-readable headings without losing structure.",
|
|
3542
|
+
"Independently reconstruct the change classification, affected operations/rules, P0 product scenarios and applicable P1 documented API rules from authoritative sources before trusting the generated Coverage Scope or Coverage Matrix. Perform an explicit coverage-scope review: reject `new-operation` when the task only optimizes an existing implementation without contract change; reject narrow optimization scope when shared validator/helper/DTO/query builder evidence directly affects more operations; reject full-contract expansion across unrelated operations. For every in-scope operation, check applicable lifecycle/uniqueness states (including deleted-existing when in scope), valid enum values, bounded invalid classes, min-1/min/nominal/max/max+1, allowed/forbidden format classes, required/null/missing/wrong-type semantics, status/error codes, auth and state transitions. Directly add in-scope omissions; undefined impact remains GAP/CONFLICT rather than invented behavior.",
|
|
3543
|
+
"Check AC completeness/meaning, endpoint, fields/shape, status/error codes, rules, states, documented boundaries/auth, positive/negative coverage, executable steps and assertable results. Require the exact `## Coverage Scope` Field/Value table with the `|---|---|` separator row, a valid classification-policy pair, non-empty Affected Operations/Rule Keys/Scope Evidence, and the classification-specific Regression Floor. Require the exact unnumbered `## Coverage Matrix` heading in `testcase/md/README.md`, exact headers, exactly 9 cells in every data row (including a non-empty Dimension), deterministic OpenAPI Rule Keys for every in-scope affected operation, exactly one Matrix row per Rule Key (merge multi-dimension product rows), and bidirectional Matrix Rule/Test Point ↔ Case bindings. Never describe affected-scope coverage as whole-API completeness. Every explicit AC ID must appear in at least one Case `验收标准`; every explicit in-scope AC/REQ/BR Rule Key cited by a Case must have exactly one Coverage Matrix row, and no Case may cite a source Rule Key omitted from the Matrix. Every Matrix Case ID must share at least one of that row's Required Test Points and the Case must cite that Rule Key. Perform an explicit execution-redundancy review: merge checkpoint-only parameter rows, repeated default/read-back assertions, DELETE status/body/follow-up-read checks, response schema/Content-Type checks, PUT full-update/timestamp checks, repeated list setup and identical null/empty inputs when endpoint, input partition, precondition state and expected outcome are the same. Preserve separate POST/PUT, boundary, enum, wrong-type, role/tenant and distinct business-state variants. Directly repair malformed headings/rows/keys and binding modes rather than merely commenting on them. Reject avoidable English prose, duplicated bilingual wording, repeated boilerplate, oversized unstructured sections, a `### 操作步骤` section that contains only a table without any numbered executable line, vague results such as ‘符合预期’, Case-ID-like module filenames (for example `BE-HEALTH.md`), dropped exact `### 操作步骤`/`### 预期结果` headings, and missing or drifted script/function mapping where it can be derived.",
|
|
3544
|
+
"Correct testcase/md/** directly: add documented omissions, remove unsupported cases, rename module files to stable lowercase stems when needed, normalize every Case ID to hyphen-separated module segments plus exactly three zero-padded digits (`BE-RESOURCE_NOTES-01` → `BE-RESOURCE-NOTES-001`; `BE-RN-011A` must be renumbered or merged) consistently across headings/index/mappings, fix automation mappings so each case points at `testcase/test_<module>.py` derived from that module filename and declares exactly one primary symbol, assign every Test Point exactly one of `变体测试点`/`场景断言测试点`/`横切证据测试点`, ensure every binding-list Test Point is also present in that Case's `### 测试点`, expand every variant parameter row into its own atomic TP ID, make every non-cross-cutting TP Case-specific and owned by exactly one Case, require every primary symbol to start with the canonical Case prefix, ensure every explicit AC ID appears in an applicable Case `验收标准`, merge execution duplicates, improve navigation/tables/Chinese wording, or record gaps in Chinese. Remove every credential/header value, placeholder, fake token and anti-example from Markdown. Sensitive key names may remain only as a plain list; values must be described as runtime-only and omitted, with no colon/value pair or literal example anywhere, including details blocks and explanatory text. Keep Case IDs, AC/REQ/BR IDs, HTTP methods, paths, fields, enum values, filenames, code symbols and source citations as exact machine-readable identifiers; only normalize Case ID separator/sequence formatting as specified above. Recalculate predicted collected items as `sum(max(1, variant count per Case))`; when the task declares a budget, directly merge redundant journeys/reclassify same-request checkpoints until the prediction is within budget, while preserving all required coverage. The validator accepts Chinese and legacy English section aliases; retain or converge to the Chinese human-readable headings without losing structure.",
|
|
3543
3545
|
"Read only precise referenced source paths plus requirement sections needed for uncovered ACs. Do not scan the repository, modify source/**, generate pytest, execute tests, or emit JSON.",
|
|
3544
3546
|
intake.boundedSourceContext,
|
|
3545
3547
|
"## Authoritative reference index",
|
|
@@ -3547,7 +3549,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3547
3549
|
"For each index entry, use `readPath` for Pi read-tool calls and keep `path` as the exact Markdown Source References citation. Bound files under .harness/tasks/<taskId>/source/** are read-only inputs: reading them is allowed even though writing .harness/** is forbidden. Never resolve `path` relative to the repository root, search for substitutes, or fall back to docs/** when a bound read fails.",
|
|
3548
3550
|
].join("\n\n"),
|
|
3549
3551
|
};
|
|
3550
|
-
const validateCases = shellNode("validate-backend-md-cases-shell", [reviewCases.id], "markdown-cases", "Record advisory findings for Markdown structure and deterministically analyze the final README Coverage Matrix against final Case rule/test-point bindings. Detect missing product/API rules, enum values, invalid equivalence classes, boundaries, format classes, business lifecycle states, GAP/CONFLICT
|
|
3552
|
+
const validateCases = shellNode("validate-backend-md-cases-shell", [reviewCases.id], "markdown-cases", "Record advisory findings for Markdown structure and deterministically analyze the final README Coverage Scope and Coverage Matrix against final Case rule/test-point bindings. Validate the classification-policy pair, affected operations/rules, scope evidence and regression floor; require documented OpenAPI completeness only for declared affected operations, while all explicit AC/REQ/BR remain in scope. Detect missing in-scope product/API rules, enum values, invalid equivalence classes, boundaries, format classes, business lifecycle states, GAP/CONFLICT, bidirectional Matrix/Case drift, non-canonical Case IDs, unclassified Test Points, duplicate binding modes and non-cross-cutting Test Points bound by multiple Cases. Do not validate source-reference existence. Write human and machine evidence from the same facts. Keep quality findings advisory, but fail closed after writing the report when secret-shaped values are detected. Coverage FAIL stays advisory.", "Run-owned reports/backend-md-case-validation.md, reports/backend-test-case-coverage-analysis.md and contracts/backend-test-case-coverage-facts.json v3 with Coverage Scope plus PASS/FAIL/UNAVAILABLE advisory facts; downstream execution continues.");
|
|
3551
3553
|
const generatePytest = {
|
|
3552
3554
|
id: "generate-backend-pytest-pi",
|
|
3553
3555
|
depends_on: [validateCases.id],
|
|
@@ -3566,16 +3568,17 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3566
3568
|
outputContract: "Convert every final automatable Markdown case into pytest assets whose actual test function region contains the exact Case ID, preferably in the function name or docstring. Each testcase/md/<module>.md (excluding README.md) maps one-to-one to testcase/test_<module>.py; never merge or split modules. No JSON and no pytest execution.",
|
|
3567
3569
|
subtask_prompt: [
|
|
3568
3570
|
"Convert testcase/md/** to pytest using upstream environment and advisory validation evidence plus only bounded pytest config/conftest. A FAIL advisory report does not authorize inventing missing behavior; use the final Markdown facts that are present.",
|
|
3569
|
-
"Ensure every final Markdown Case ID appears in exactly one primary pytest test function or pytest test class method region,
|
|
3571
|
+
"Ensure every final Markdown Case ID appears in exactly one primary pytest test function or pytest test class method region, using the exact `primary symbol` declared by Markdown. The symbol must start with `test_BE_<MODULE>_<NNN>_` so every parameterized collected item remains associated with its Case. Module-level functions and class-based pytest methods are both supported. Only `变体测试点` may use stable `pytest.param(..., id=\"TP-...\")` IDs, and every atomic variant ID must appear exactly once with a genuine input/state/outcome change. Use `pytest.param(..., id=...)` for every row; do not use decorator-level `ids=[...]`, generated suffixes, or IDs that extend/shorten the exact Markdown TP. Do not parameterize `场景断言测试点` or `横切证据测试点`; execute all assertion checkpoints within the same business journey/item and use shared helpers for cross-cutting evidence. The primary symbol docstring must contain exact metadata lines `Case-ID: BE-...`, `Assertion-Test-Points: TP-...;TP-...` and `Cross-Cutting-Test-Points: TP-...;TP-...` (use `none` when empty). No Test Point may be invented, renamed, omitted or bound in two modes. The generated pytest collection shape must equal the Markdown prediction `sum(max(1, variant count per Case))`; keep it at or below the task's explicit budget by removing duplicate execution, never by collapsing multiple parameter rows under a coarse family TP. Assertions come only from 预期结果 and setup comes only from 前置条件/测试数据/自动化映射.",
|
|
3570
3572
|
"Name each generated pytest file so it corresponds one-to-one with its source Markdown module file: for each `testcase/md/<module>.md` (excluding README.md), emit exactly one `testcase/test_<module>.py`. The <module> stem is the Markdown filename without the `.md` extension, lowercased and with non-alphanumeric characters replaced by underscores. For example, `testcase/md/resource_notes.md` maps to `testcase/test_resource_notes.py`, `testcase/md/health.md` maps to `testcase/test_health.py`, `testcase/md/BE-HEALTH.md` maps to `testcase/test_be_health.py`, and `testcase/md/order-api.md` maps to `testcase/test_order_api.py`. If Markdown automation mapping names a different path than this module stem path, still write the module stem path and do not invent prefixes such as `test_be_*` unless the module filename itself normalizes to that stem. Never merge multiple Markdown modules into one pytest file, never split one module across several files, and never invent pytest filenames unrelated to the Markdown modules.",
|
|
3571
3573
|
"Generate a reusable HTTP logging helper (or equivalent client wrapper) and call it for every interface request. The request log must include method, URL/path, and request parameters (query plus JSON/body/payload summary). The response log must include status code and response result (JSON/text/body summary), and both records must be visible in pytest stdout/stderr without changing assertions.",
|
|
3574
|
+
"HTTP response header names are case-insensitive. If the helper stores a lower-case normalized header map, every Content-Type or other header assertion must query the lower-case key (for example `content-type`) or use an explicitly case-insensitive accessor; never call a case-sensitive plain dict with `Content-Type` when the stored key is lower-case. Preserve the actual media-type assertion rather than dropping it.",
|
|
3572
3575
|
"Compare timestamps and other semantically equivalent protocol values by parsed meaning, not byte-for-byte serialization. In particular, normalize valid ISO-8601 instants before equality/order assertions so differences such as omitted trailing fractional seconds do not create TestBug failures; preserve exact-string assertions only when the Markdown explicitly requires representation equality.",
|
|
3573
3576
|
"Before logging, recursively redact sensitive keys and header values including authorization, proxy-authorization, cookie, set-cookie, token, password, secret, api key and credentials. Never print full Authorization/Cookie values. Apply bounded truncation to serialized request and response bodies (with an explicit truncation marker) so large payloads cannot flood pytest or report artifacts.",
|
|
3574
3577
|
"Do not read source/**, add cases, reassign ACs, modify conftest/config/production code, use skip/xfail, swallow assertions, execute pytest, or emit JSON. For best-effort cleanup, catch only the narrow transport exception actually raised by the selected HTTP client (for example `requests.RequestException` or `urllib.error.URLError`); never use bare `except`, `Exception`, or `BaseException` with `pass`.",
|
|
3575
3578
|
].join("\n\n"),
|
|
3576
3579
|
};
|
|
3577
|
-
const traceability = shellNode("backend-test-traceability-gate-shell", [generatePytest.id], "markdown-traceability", "Deterministically scan only Markdown-mapped pytest scripts. Keep the existing traceability/logging checks and
|
|
3578
|
-
const manifest = shellNode("backend-test-case-manifest-shell", [traceability.id], "markdown-manifest", "Materialize the canonical Backend Test Case Manifest only from contracts/backend-test-case-coverage-facts.json and contracts/backend-test-markdown-pytest-correspondence-facts.json. Validate schema, task binding, input hashes and freshness; never re-read source semantics, re-analyze Coverage Matrix, rescan pytest symbols or recompute a second set of metrics. Missing/stale/conflicting facts produce partial/unavailable diagnostics rather than fabricated zeros.", "Run-owned contracts/backend-test-case-manifest.json with materializationStatus, sourceFactsIssues, validated coverageSummary, ruleCoverageSummary and correspondenceSummary; this is the single machine input for L-5 and closeout.");
|
|
3580
|
+
const traceability = shellNode("backend-test-traceability-gate-shell", [generatePytest.id], "markdown-traceability", "Deterministically scan only Markdown-mapped pytest scripts. Keep the existing traceability/logging checks and produce a bidirectional Markdown module/Case/Test Point ↔ pytest file/primary symbol correspondence analysis. Map variant Test Points from stable parameter IDs, assertion Test Points from the primary symbol docstring, and cross-cutting Test Points from the primary symbol evidence binding. Report 1:1, 1:0, 1:N, 0:1, script/primary-symbol mismatch, missing Case ID, missing variant parameter IDs, missing assertion/cross-cutting bindings, duplicate modes and extra bindings. Human and machine evidence must come from the same facts. Findings are advisory and never block pytest.", "Run-owned reports/backend-test-traceability.md, reports/backend-test-markdown-pytest-correspondence.md and contracts/backend-test-markdown-pytest-correspondence-facts.json with PASS/FAIL/UNAVAILABLE correspondence facts.");
|
|
3581
|
+
const manifest = shellNode("backend-test-case-manifest-shell", [traceability.id], "markdown-manifest", "Materialize the canonical Backend Test Case Manifest only from contracts/backend-test-case-coverage-facts.json and contracts/backend-test-markdown-pytest-correspondence-facts.json. Validate schema, task binding, input hashes and freshness; never re-read source semantics, re-analyze Coverage Matrix, rescan pytest symbols or recompute a second set of metrics. Missing/stale/conflicting facts produce partial/unavailable diagnostics rather than fabricated zeros.", "Run-owned contracts/backend-test-case-manifest.json with materializationStatus, sourceFactsIssues, validated coverageScope, coverageSummary, ruleCoverageSummary and correspondenceSummary; this is the single machine input for L-5 and closeout.");
|
|
3579
3582
|
const pytestCommand = [
|
|
3580
3583
|
'mkdir -p "${HARNESS_DAG_RUN_DIR}/reports"',
|
|
3581
3584
|
'echo "pytest targets are resolved at runtime from final Markdown 自动化映射"',
|
|
@@ -3607,8 +3610,8 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3607
3610
|
"Generate the final Markdown report only from authoritative run-owned artifacts. Read node 1 reports/backend-test-environment.md; node 4 backend-md-case-validation.md and backend-test-case-coverage-analysis.md; node 6 backend-test-traceability.md and backend-test-markdown-pytest-correspondence.md; node 7 contracts/backend-test-case-manifest.json; and node 8 backend-test-result.json, backend-test-facts.md, pytest-html/HTML and L-5 dashboard. Do not use node 2/3/5 assistant prose as facts. Do not emit JSON.",
|
|
3608
3611
|
"Use this exact human-facing section order: 测试结论 → 执行概览 → 质量校验 → 失败分析 → 风险与建议 → 证据与 L-5. Put the decision and key numbers first, use compact tables/bullets, and keep headings concise. Do not paste entire upstream reports, duplicate per-case tables already present in facts, or repeat the same evidence in multiple sections; link to paths/hashes and quote only the findings needed for the conclusion.",
|
|
3609
3612
|
"The L-5 metrics and visualization are produced deterministically by node 8 at reports/backend-test-l5-dashboard.html. Link to that dashboard as the authoritative L-5 view. Pytest execution facts come from node 8; coverage/correspondence numbers and materializationStatus come from node 7; detailed coverage findings come from node 4; detailed mapping findings come from node 6. Never recompute these values. If machine manifest and human reports disagree, report evidence inconsistency rather than silently choosing.",
|
|
3610
|
-
"Always state the exact PASS/FAIL/UNAVAILABLE status and findings from node 4 case validation + coverage
|
|
3611
|
-
"Include environment, case quality/review, automation mapping, exact pytest facts, failure classification/analysis, risks, regression recommendations, evidence paths/hashes, coverage availability, and L-5 READY/NOT READY.",
|
|
3613
|
+
"Always state the exact Coverage Scope classification, policy, affected operations, regression floor, completeness claim, PASS/FAIL/UNAVAILABLE status and findings from node 4 case validation + coverage, plus node 6 traceability + correspondence. Affected-scope or affected-operations-full coverage must never be described as whole-API completeness unless every operation is explicitly listed. Their FAIL status does not block pytest, but it must remain visible and must never be rewritten as PASS.",
|
|
3614
|
+
"Include environment, case quality/review, automation mapping, exact pytest facts, failure classification/analysis, risks, regression recommendations, evidence paths/hashes, coverage availability, and L-5 READY/NOT READY. Distinguish Markdown Case count, primary pytest symbol count, collected pytest item count, variant/assertion/cross-cutting Test Point counts and execution amplification; never describe pytest item count as the number of business scenarios.",
|
|
3612
3615
|
"Never override Shell/pytest-html facts. L-5 requires pass=100%, AC=100%, automation>=90%, line>=80%, branch>=70%, skipped=0 and no blocking Critical risk.",
|
|
3613
3616
|
canWriteReport
|
|
3614
3617
|
? "Write only under docs/test-reports/**."
|
|
@@ -3629,7 +3632,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3629
3632
|
"Model nodes produce Markdown and pytest assets, never backend-test business JSON envelopes.",
|
|
3630
3633
|
"Environment, advisory Markdown validation/coverage, advisory traceability/correspondence, canonical manifest, pytest-html, HTML and execution facts are deterministic evidence. Nodes 4 and 6 record findings without blocking nodes 5, 7 or 8; node 7 partial/unavailable does not block node 8.",
|
|
3631
3634
|
"Only Markdown case generation/review may read source facts; pytest generation must not read source/**.",
|
|
3632
|
-
"Functional case IDs use BE-<MODULE>-<NNN
|
|
3635
|
+
"Functional case IDs use canonical BE-<MODULE>-<NNN> with exactly three digits and no alphabetic suffix. Every Test Point has exactly one variant/assertion/cross-cutting binding; only variant bindings create pytest parameter items. Production code/config, skip/xfail, repair and rerun are forbidden.",
|
|
3633
3636
|
],
|
|
3634
3637
|
defaults: {
|
|
3635
3638
|
...BACKEND_TEST_DEFAULTS,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { access, mkdir, readFile, readdir, rename
|
|
1
|
+
import { access, mkdir, readFile, readdir, rename } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { hostname as localHostname } from "node:os";
|
|
4
4
|
import { writeJsonAtomic, } from "../../infrastructure/harness/atomic-write.js";
|
|
@@ -170,20 +170,22 @@ export function assessDagRunLiveness(input) {
|
|
|
170
170
|
return { status: "unknown" };
|
|
171
171
|
if (runner.hostname !== (input.hostname ?? localHostname()))
|
|
172
172
|
return { status: "unknown-host" };
|
|
173
|
-
const isAlive = input.isProcessAlive ??
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
173
|
+
const isAlive = input.isProcessAlive ??
|
|
174
|
+
((pid) => {
|
|
175
|
+
try {
|
|
176
|
+
process.kill(pid, 0);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
});
|
|
182
183
|
if (!isAlive(runner.pid))
|
|
183
184
|
return { status: "orphaned", runnerAlive: false };
|
|
184
185
|
const heartbeatMs = Date.parse(runner.heartbeatAt);
|
|
185
186
|
const nowMs = (input.now ?? new Date()).getTime();
|
|
186
|
-
if (!Number.isNaN(heartbeatMs) &&
|
|
187
|
+
if (!Number.isNaN(heartbeatMs) &&
|
|
188
|
+
nowMs - heartbeatMs > (input.staleThresholdMs ?? 90_000)) {
|
|
187
189
|
return { status: "stale", runnerAlive: true };
|
|
188
190
|
}
|
|
189
191
|
const activeNode = Object.values(input.state.nodes).find((node) => node.status === "RUNNING");
|
|
@@ -193,20 +195,20 @@ export function assessDagRunLiveness(input) {
|
|
|
193
195
|
if (activeNode.livenessStatus === "needs-attention") {
|
|
194
196
|
return { status: "needs-attention", runnerAlive: true };
|
|
195
197
|
}
|
|
196
|
-
if (activeNode.livenessStatus === "suspected-stall"
|
|
197
|
-
|
|
198
|
+
if (activeNode.livenessStatus === "suspected-stall" ||
|
|
199
|
+
activeNode.livenessStatus === "probing") {
|
|
198
200
|
return { status: "suspected-stall", runnerAlive: true };
|
|
199
201
|
}
|
|
200
202
|
if (activeNode.livenessStatus === "quiet") {
|
|
201
203
|
return { status: "node-quiet", runnerAlive: true };
|
|
202
204
|
}
|
|
203
205
|
// Fall back to meaningful progress clocks (never use runner lease as progress).
|
|
204
|
-
const meaningfulAt = activeNode.lastMeaningfulProgressAt
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
206
|
+
const meaningfulAt = activeNode.lastMeaningfulProgressAt ??
|
|
207
|
+
activeNode.lastProviderActivityAt ??
|
|
208
|
+
activeNode.lastToolActivityAt ??
|
|
209
|
+
activeNode.lastOutputActivityAt ??
|
|
210
|
+
activeNode.lastActivityAt ??
|
|
211
|
+
activeNode.startedAt;
|
|
210
212
|
const nodeActivityMs = Date.parse(meaningfulAt ?? "");
|
|
211
213
|
if (!Number.isNaN(nodeActivityMs)) {
|
|
212
214
|
const idleMs = nowMs - nodeActivityMs;
|
|
@@ -229,12 +231,12 @@ export function deriveDagRunEffectiveStatus(input) {
|
|
|
229
231
|
if (input.lifecycle === "paused")
|
|
230
232
|
return "paused";
|
|
231
233
|
if (input.lifecycle === "completed") {
|
|
232
|
-
return input.state.status
|
|
234
|
+
return mapTerminalDagRunEffectiveStatus(input.state.status);
|
|
233
235
|
}
|
|
234
236
|
if (input.state.status === "pending")
|
|
235
237
|
return "pending";
|
|
236
238
|
if (isTerminalDagRunStatus(input.state.status)) {
|
|
237
|
-
return input.state.status
|
|
239
|
+
return mapTerminalDagRunEffectiveStatus(input.state.status);
|
|
238
240
|
}
|
|
239
241
|
if (input.liveness === "orphaned" || input.liveness === "stale")
|
|
240
242
|
return "interrupted";
|
|
@@ -252,19 +254,20 @@ export function deriveDagRunEffectiveStatus(input) {
|
|
|
252
254
|
}
|
|
253
255
|
export function assessDagRunRecoveryEligibility(input) {
|
|
254
256
|
const reasons = [];
|
|
255
|
-
const canResume = input.lifecycle === "active"
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
257
|
+
const canResume = input.lifecycle === "active" &&
|
|
258
|
+
input.state.status === "running" &&
|
|
259
|
+
Boolean(input.state.humanDecisionNodeId) &&
|
|
260
|
+
Boolean(input.hasHumanApproval);
|
|
259
261
|
if (!canResume)
|
|
260
262
|
reasons.push("standard-resume-preconditions-not-met");
|
|
261
263
|
let canReconcile = true;
|
|
262
|
-
if (input.lifecycle === "completed" ||
|
|
264
|
+
if (input.lifecycle === "completed" ||
|
|
265
|
+
isTerminalDagRunStatus(input.state.status)) {
|
|
263
266
|
canReconcile = false;
|
|
264
267
|
reasons.push("run-already-terminal");
|
|
265
268
|
}
|
|
266
|
-
if (input.lifecycle === "active"
|
|
267
|
-
|
|
269
|
+
if (input.lifecycle === "active" &&
|
|
270
|
+
[
|
|
268
271
|
"active",
|
|
269
272
|
"node-quiet",
|
|
270
273
|
"suspected-stall",
|
|
@@ -298,6 +301,13 @@ export const TERMINAL_RUN_STATUSES = new Set([
|
|
|
298
301
|
export function isTerminalDagRunStatus(status) {
|
|
299
302
|
return TERMINAL_RUN_STATUSES.has(status);
|
|
300
303
|
}
|
|
304
|
+
function mapTerminalDagRunEffectiveStatus(status) {
|
|
305
|
+
if (status === "finished")
|
|
306
|
+
return "finished";
|
|
307
|
+
if (status === "partial_failed")
|
|
308
|
+
return "partial_failed";
|
|
309
|
+
return "failed";
|
|
310
|
+
}
|
|
301
311
|
function listPendingNodeIds(state) {
|
|
302
312
|
return Object.values(state.nodes)
|
|
303
313
|
.filter((node) => node.status === "PENDING")
|
|
@@ -477,8 +487,8 @@ export async function listAllDagRunEntries(repoRoot) {
|
|
|
477
487
|
located.push(...(await listRunsInLifecycleDir(repoRoot, lifecycle)));
|
|
478
488
|
}
|
|
479
489
|
return located.sort((left, right) => {
|
|
480
|
-
const lifecycleCompare = DAG_LIFECYCLE_SCAN_ORDER.indexOf(left.lifecycle)
|
|
481
|
-
|
|
490
|
+
const lifecycleCompare = DAG_LIFECYCLE_SCAN_ORDER.indexOf(left.lifecycle) -
|
|
491
|
+
DAG_LIFECYCLE_SCAN_ORDER.indexOf(right.lifecycle);
|
|
482
492
|
if (lifecycleCompare !== 0)
|
|
483
493
|
return lifecycleCompare;
|
|
484
494
|
return left.runId.localeCompare(right.runId);
|
|
@@ -16,6 +16,8 @@ import { assertSkillSnapshotCoversSpec, buildNodePromptFromSnapshot, isDagSkillS
|
|
|
16
16
|
import { resolveDagSkillInstructions, skillInstructionMetadata, } from "./skill-instructions.js";
|
|
17
17
|
import { parseRepairArtifactFromText, resolveRepairTaskForGate, validateRepairArtifactScope, } from "./repair-artifact.js";
|
|
18
18
|
import { resolveModelForTask, } from "./types.js";
|
|
19
|
+
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
20
|
+
import { resolveExecutorThinkingMatrix } from "../../executors/model-routing.js";
|
|
19
21
|
export function buildNodePrompt(spec, task, upstream, options) {
|
|
20
22
|
const policy = resolveContextPolicy(spec);
|
|
21
23
|
return buildDagNodePromptEnvelope({
|
|
@@ -359,6 +361,16 @@ export async function executeDagNode(input) {
|
|
|
359
361
|
node.resolvedSkills = resolvedSkills;
|
|
360
362
|
await writeNodeSkillArtifacts(runDir, nodeId, resolvedSkills);
|
|
361
363
|
const model = resolveModelForTask(task, spec.executorModels);
|
|
364
|
+
let thinking;
|
|
365
|
+
if (task.executor === "pi") {
|
|
366
|
+
try {
|
|
367
|
+
const manifest = await loadHarnessManifest(cwd);
|
|
368
|
+
thinking = resolveExecutorThinkingMatrix(manifest.executors?.pi)[task.complexity];
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
// Invalid or missing harness preserves the previous no-override behavior.
|
|
372
|
+
}
|
|
373
|
+
}
|
|
362
374
|
const retryPolicy = task.retryPolicy && isSafeReadOnlyPiRetryCandidate(task)
|
|
363
375
|
? task.retryPolicy
|
|
364
376
|
: undefined;
|
|
@@ -418,6 +430,7 @@ export async function executeDagNode(input) {
|
|
|
418
430
|
task,
|
|
419
431
|
cwd,
|
|
420
432
|
model,
|
|
433
|
+
...(thinking ? { thinking } : {}),
|
|
421
434
|
prompt: buildAttemptPrompt(task, prompt, attemptNumber, previousFailureCategory, previousProtocolReason),
|
|
422
435
|
attempt: attemptNumber,
|
|
423
436
|
reportActivity,
|
|
@@ -74,7 +74,9 @@ export const dagVerdictGateSchema = z.object({
|
|
|
74
74
|
* Prefer listing the post-revision / final reviewer first when both may exist.
|
|
75
75
|
*/
|
|
76
76
|
fallbackFromNodeIds: z
|
|
77
|
-
.array(z
|
|
77
|
+
.array(z
|
|
78
|
+
.string()
|
|
79
|
+
.regex(/^[a-z][a-z0-9-]*$/, "fallbackFromNodeIds must be kebab-case"))
|
|
78
80
|
.optional(),
|
|
79
81
|
accept: z.array(z.string().min(1)).min(1),
|
|
80
82
|
lineMode: z.enum(["first-non-empty", "first-verdict-line"]).optional(),
|
|
@@ -130,15 +132,20 @@ export const dagFrontendPrewriteGateSchema = z.object({
|
|
|
130
132
|
planFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
|
|
131
133
|
reviewFromNodeId: dagFrontendNodeIdSchema,
|
|
132
134
|
reviewFallbackFromNodeIds: z.array(dagFrontendNodeIdSchema).default([]),
|
|
133
|
-
requiredRequirementIds: z
|
|
134
|
-
|
|
135
|
+
requiredRequirementIds: z
|
|
136
|
+
.array(z.string().regex(/^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$/))
|
|
137
|
+
.default([]),
|
|
138
|
+
allowedMockStrategies: z
|
|
139
|
+
.array(z.enum(["native", "browser-intercept", "request-adapter", "not-needed"]))
|
|
140
|
+
.min(1),
|
|
135
141
|
artifactName: z.string().regex(/^[a-z0-9][a-z0-9._-]*\.json$/),
|
|
136
142
|
outputDir: z.string().regex(/^[a-z0-9][a-z0-9._-]*$/),
|
|
137
143
|
requireSourceFreshness: z.literal(true),
|
|
138
144
|
implementationWriteSet: z.array(z.string().min(1)).min(1).optional(),
|
|
139
145
|
openspecCandidatePaths: z
|
|
140
|
-
.array(z
|
|
141
|
-
|
|
146
|
+
.array(z
|
|
147
|
+
.string()
|
|
148
|
+
.refine((candidate) => !candidate.includes("\\") && isOpenspecSpecFilePath(candidate), "openspec candidate must be a repo-relative supported file under openspec/schemas/ or openspec/project-specs/"))
|
|
142
149
|
.default([]),
|
|
143
150
|
});
|
|
144
151
|
export const dagFrontendLintBaselineSchema = z
|
|
@@ -148,7 +155,8 @@ export const dagFrontendLintBaselineSchema = z
|
|
|
148
155
|
lintEvidence: dagShellVerifyEvidenceSchema,
|
|
149
156
|
})
|
|
150
157
|
.strict();
|
|
151
|
-
export const dagFrontendVerificationBundleSchema = z
|
|
158
|
+
export const dagFrontendVerificationBundleSchema = z
|
|
159
|
+
.object({
|
|
152
160
|
schemaVersion: z.literal(1),
|
|
153
161
|
mockCommands: z.array(z.string()).default([]),
|
|
154
162
|
lintCommands: z.array(z.string().min(1)).optional(),
|
|
@@ -161,7 +169,8 @@ export const dagFrontendVerificationBundleSchema = z.object({
|
|
|
161
169
|
lintBaselineNodeId: dagFrontendNodeIdSchema.optional(),
|
|
162
170
|
writerNodeIds: z.array(dagFrontendNodeIdSchema).optional(),
|
|
163
171
|
mode: z.enum(["initial", "repair"]),
|
|
164
|
-
})
|
|
172
|
+
})
|
|
173
|
+
.superRefine((bundle, context) => {
|
|
165
174
|
const groups = [
|
|
166
175
|
["mock", bundle.mockCommands, bundle.mockEvidence],
|
|
167
176
|
["lint", bundle.lintCommands ?? [], bundle.lintEvidence],
|
|
@@ -169,23 +178,48 @@ export const dagFrontendVerificationBundleSchema = z.object({
|
|
|
169
178
|
["behavior", bundle.behaviorCommands, bundle.behaviorEvidence],
|
|
170
179
|
];
|
|
171
180
|
for (const [name, commands, evidence] of groups) {
|
|
172
|
-
if ((name === "mock" || name === "lint") &&
|
|
181
|
+
if ((name === "mock" || name === "lint") &&
|
|
182
|
+
commands.length === 0 &&
|
|
183
|
+
!evidence)
|
|
173
184
|
continue;
|
|
174
185
|
if (!evidence) {
|
|
175
|
-
context.addIssue({
|
|
186
|
+
context.addIssue({
|
|
187
|
+
code: z.ZodIssueCode.custom,
|
|
188
|
+
path: [`${name}Evidence`],
|
|
189
|
+
message: `${name} evidence is required when commands are configured`,
|
|
190
|
+
});
|
|
176
191
|
continue;
|
|
177
192
|
}
|
|
178
193
|
if (evidence.commandCount !== commands.length) {
|
|
179
|
-
context.addIssue({
|
|
194
|
+
context.addIssue({
|
|
195
|
+
code: z.ZodIssueCode.custom,
|
|
196
|
+
path: [`${name}Evidence`, "commandCount"],
|
|
197
|
+
message: `${name} commandCount must match commands`,
|
|
198
|
+
});
|
|
180
199
|
}
|
|
181
200
|
if (evidence.commandLabels.length !== commands.length) {
|
|
182
|
-
context.addIssue({
|
|
201
|
+
context.addIssue({
|
|
202
|
+
code: z.ZodIssueCode.custom,
|
|
203
|
+
path: [`${name}Evidence`, "commandLabels"],
|
|
204
|
+
message: `${name} commandLabels must match commands`,
|
|
205
|
+
});
|
|
183
206
|
}
|
|
184
|
-
if (evidence.commandTexts.length !== commands.length ||
|
|
185
|
-
|
|
207
|
+
if (evidence.commandTexts.length !== commands.length ||
|
|
208
|
+
evidence.commandTexts.some((command, index) => command !== commands[index])) {
|
|
209
|
+
context.addIssue({
|
|
210
|
+
code: z.ZodIssueCode.custom,
|
|
211
|
+
path: [`${name}Evidence`, "commandTexts"],
|
|
212
|
+
message: `${name} commandTexts must exactly match commands`,
|
|
213
|
+
});
|
|
186
214
|
}
|
|
187
|
-
if (new Set(evidence.commandLabels).size !==
|
|
188
|
-
|
|
215
|
+
if (new Set(evidence.commandLabels).size !==
|
|
216
|
+
evidence.commandLabels.length ||
|
|
217
|
+
evidence.commandLabels.some((label) => !label.trim())) {
|
|
218
|
+
context.addIssue({
|
|
219
|
+
code: z.ZodIssueCode.custom,
|
|
220
|
+
path: [`${name}Evidence`, "commandLabels"],
|
|
221
|
+
message: `${name} commandLabels must be non-empty and unique`,
|
|
222
|
+
});
|
|
189
223
|
}
|
|
190
224
|
}
|
|
191
225
|
if ((bundle.lintCommands?.length ?? 0) > 0) {
|
|
@@ -318,9 +352,12 @@ export const dagShellConfigSchema = z.object({
|
|
|
318
352
|
commands: z.array(z.string()).default([]),
|
|
319
353
|
preset: dagShellPresetSchema.optional(),
|
|
320
354
|
verdictGate: dagVerdictGateSchema.optional(),
|
|
321
|
-
projectGovernanceGate: z
|
|
355
|
+
projectGovernanceGate: z
|
|
356
|
+
.object({
|
|
322
357
|
contextPath: z.literal(".runtime/project-governance-context.json"),
|
|
323
|
-
})
|
|
358
|
+
})
|
|
359
|
+
.strict()
|
|
360
|
+
.optional(),
|
|
324
361
|
requirementCoverageGate: dagRequirementCoverageGateSchema.optional(),
|
|
325
362
|
jsonArtifactGate: dagJsonArtifactGateSchema.optional(),
|
|
326
363
|
frontendPrewriteGate: dagFrontendPrewriteGateSchema.optional(),
|
|
@@ -333,7 +370,8 @@ export const dagShellConfigSchema = z.object({
|
|
|
333
370
|
frontendTestHtmlReport: dagFrontendTestHtmlReportSchema.optional(),
|
|
334
371
|
backendTestPipeline: dagBackendTestPipelineSchema.optional(),
|
|
335
372
|
/** JaCoCo coverage collection for backend-test (Java services). When set, node 7 dumps coverage over TCP from a JaCoCo tcpserver agent and feeds it to the L-5 dashboard. */
|
|
336
|
-
jacocoCoverage: z
|
|
373
|
+
jacocoCoverage: z
|
|
374
|
+
.object({
|
|
337
375
|
/** JaCoCo tcpserver endpoint, e.g. "host:6300". */
|
|
338
376
|
endpoint: z.string().min(1),
|
|
339
377
|
/** Absolute path to jacococli.jar on this machine, used to convert .exec → jacoco.xml. */
|
|
@@ -342,7 +380,9 @@ export const dagShellConfigSchema = z.object({
|
|
|
342
380
|
includes: z.string().min(1).optional().default("*"),
|
|
343
381
|
/** TCP connect timeout in ms. Defaults to 5000. */
|
|
344
382
|
connectTimeoutMs: z.number().int().positive().optional().default(5000),
|
|
345
|
-
})
|
|
383
|
+
})
|
|
384
|
+
.strict()
|
|
385
|
+
.optional(),
|
|
346
386
|
verifyEvidence: dagShellVerifyEvidenceSchema.optional(),
|
|
347
387
|
repairArtifactGate: dagRepairArtifactGateSchema.optional(),
|
|
348
388
|
/** fail (default): any nonzero command fails the node. record: finish node FINISHED with failure facts for downstream assess/repair. */
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"docs/skills/README.md",
|
|
24
24
|
"docs/skills/vetted-skill-registry.md",
|
|
25
25
|
"docs/templates/README.md",
|
|
26
|
+
"docs/templates/init-managed-agents.md",
|
|
26
27
|
"docs/templates/init-evolution-review.md",
|
|
27
28
|
"docs/templates/exec-plan.md",
|
|
28
29
|
"docs/templates/production-readiness-checklist.md",
|
|
@@ -257,7 +258,8 @@
|
|
|
257
258
|
],
|
|
258
259
|
"initExcluded": [
|
|
259
260
|
"examples/example-dag.json",
|
|
260
|
-
"docs/skills/vetted-skill-registry.md"
|
|
261
|
+
"docs/skills/vetted-skill-registry.md",
|
|
262
|
+
"docs/templates/init-managed-agents.md"
|
|
261
263
|
],
|
|
262
264
|
"reviewTriggers": [
|
|
263
265
|
"AGENTS.md",
|
package/docs/skills/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
# Skill Registry
|
|
2
|
-
|
|
3
|
-
This directory records repo-local skill wrappers and vetting notes used by Agent DAG role mapping.
|
|
4
|
-
|
|
5
|
-
- `vetted-skill-registry.md` — supported roles, source inspiration, risk notes, and default/optional usage.
|
|
6
|
-
- `../../skills/agent-worker/SKILL.md` — optional outer-loop operator skill for Feature Packet, TaskSpec, Task Pool, controller pinning, self-hosting canaries, and Worker recovery. Single DAG implementation or runtime repair stays with `loop-agent`; this skill is not a default DAG role skill.
|
|
7
|
-
- `../../scripts/check-skill-entry.sh` — validates both public skill entries, their required references, line budgets, and the `agent-worker` trigger vocabulary.
|
|
1
|
+
# Skill Registry
|
|
2
|
+
|
|
3
|
+
This directory records repo-local skill wrappers and vetting notes used by Agent DAG role mapping.
|
|
4
|
+
|
|
5
|
+
- `vetted-skill-registry.md` — supported roles, source inspiration, risk notes, and default/optional usage.
|
|
6
|
+
- `../../skills/agent-worker/SKILL.md` — optional outer-loop operator skill for Feature Packet, TaskSpec, Task Pool, controller pinning, self-hosting canaries, and Worker recovery. Single DAG implementation or runtime repair stays with `loop-agent`; this skill is not a default DAG role skill.
|
|
7
|
+
- `../../scripts/check-skill-entry.sh` — validates both public skill entries, their required references, line budgets, and the `agent-worker` trigger vocabulary.
|
package/docs/templates/README.md
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 通用任务、报告与治理
|
|
8
8
|
|
|
9
|
+
- `init-managed-agents.md` — **package-only**:目标项目 `AGENTS.md` managed block 的可编辑正文真源(`__LOOP_AGENT_PROJECT_NAME__` / `__LOOP_AGENT_GOVERNANCE_ROOT__` 占位符;由 `init` 渲染注入,**不**复制到目标治理 `templates/`)。
|
|
9
10
|
- `project-start-checklist.md` — 开工检查。
|
|
10
11
|
- `feature-spec.md`、`sprint-contract.md` — 功能规格与短周期合同。
|
|
11
12
|
- `exec-plan.md`、`progress-log.md`、`qa-report.md` — 执行计划、交接与验证报告。
|