@tea-agent/loop-agent 0.31.0 → 0.32.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 +1 -1
- package/CHANGELOG.md +60 -0
- package/README.md +1 -1
- package/dist/executors/dag-pi-executor.js +69 -2
- package/dist/executors/pi-sdk-executor.js +61 -0
- package/dist/executors/shell-executor.js +136 -79
- package/dist/shared/operator/capabilities.js +22 -1
- package/dist/shared/operator/command-lifecycle.js +94 -0
- package/dist/shared/operator/index.js +1 -0
- package/dist/worker/cli.js +10 -24
- package/dist/worker/console/doctor.js +11 -4
- package/dist/worker/console/observe-health-match.js +18 -15
- package/dist/worker/console/operator-actions.js +2 -1
- package/dist/worker/feature/acceptance-policy.js +227 -0
- package/dist/worker/feature/decision-loader.js +1 -1
- package/dist/worker/feature/next-action.js +56 -6
- package/dist/worker/feature/profile-schema.js +3 -0
- package/dist/worker/feature/reducer.js +1 -0
- package/dist/worker/feature/review.js +72 -8
- package/dist/worker/feature/scaffold.js +14 -0
- package/dist/worker/loop-agent/controller-protocol.js +143 -0
- package/dist/worker/materialize/harness-task-lifecycle-probe.js +126 -0
- package/dist/worker/materialize/harness-task-lineage.js +220 -0
- package/dist/worker/materialize/harness-task-materializer.js +350 -80
- package/dist/worker/observability/progress-composite.js +1 -0
- package/dist/worker/observability/read-model.js +66 -19
- package/dist/worker/pool/attempt-identity.js +41 -0
- package/dist/worker/pool/attempt-lease.js +184 -0
- package/dist/worker/pool/attempt-transition.js +210 -0
- package/dist/worker/pool/begin-attempt-with-lease.js +26 -0
- package/dist/worker/pool/begin-attempt.js +35 -0
- package/dist/worker/pool/failure-routing.js +49 -0
- package/dist/worker/pool/recovery-decision.js +163 -0
- package/dist/worker/pool/run-owner-store.js +126 -0
- package/dist/worker/pool/run-store.js +32 -46
- package/dist/worker/pool/runtime-reconcile-inventory.js +127 -0
- package/dist/worker/pool/state-projection.js +57 -0
- package/dist/worker/run-task/run-task.js +31 -4
- package/dist/worker/runner/run-ready.js +64 -14
- package/dist/worker/runner/single-task-attempt.js +42 -13
- package/dist/worker/task-graph/acceptance-schema.js +3 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +162 -7
- package/dist/workflows/dag/backend-test-result-contract.js +105 -67
- package/dist/workflows/dag/backend-test-scenario-param.js +92 -30
- package/dist/workflows/dag/backend-test-writer-completeness.js +55 -0
- package/dist/workflows/dag/init-hybrid.js +44 -47
- package/dist/workflows/dag/rerun-task.js +86 -0
- package/docs/README.md +3 -1
- package/docs/architecture/evolution.md +1 -1
- package/docs/operations/README.md +1 -0
- package/docs/templates/README.md +1 -0
- package/docs/templates/agent-worker-production-readiness-checklist.md +45 -0
- package/docs/templates/backend-test-dag.json +40 -60
- package/docs/templates/evaluation/agents-map-slim-v1.md +1 -1
- package/docs/templates/evaluation/agents-map-verbose-v0.md +1 -1
- package/docs/templates/init-managed-agents.md +1 -1
- package/docs/templates/product-line/scaffold-samples/backend-only/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/backend-only/feature.yaml +11 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/fe-with-api/feature.yaml +11 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/acceptance.yaml +3 -0
- package/docs/templates/product-line/scaffold-samples/frontend-only/feature.yaml +11 -0
- package/package.json +1 -1
- package/skills/agent-worker/SKILL.md +1 -1
- package/skills/agent-worker/references/agent-worker-operator.md +1 -1
- package/skills/loop-agent/references/command-reference.md +2 -2
- package/skills/loop-agent/references/harness-policy.md +1 -1
|
@@ -3595,7 +3595,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3595
3595
|
"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.",
|
|
3596
3596
|
"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.",
|
|
3597
3597
|
"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.",
|
|
3598
|
-
"Mandatory module index: include a `## Module Index` table in README that lists every planned module as a canonical relative link of the exact form `[label](./<stem>.md)` plus a `testcase/md/<stem>.md` path cell, so a downstream deterministic manifest can parse the module list. Name each module file with a stable lowercase business stem such as `health` or `resource_notes`. Do not use Case-ID-like module filenames such as `BE-HEALTH.md` or `BE-NOTES.md`. The relative link target MUST equal the on-disk filename stem the sharded writer will create. 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.",
|
|
3598
|
+
"Mandatory module index: include a `## Module Index` table in README that lists every planned module as a canonical relative link of the exact form `[label](./<stem>.md)` plus a `testcase/md/<stem>.md` path cell, so a downstream deterministic manifest can parse the module list. Group by stable business resource/domain, not by CRUD operation: one resource's list/detail/create/update/delete cases belong in one module such as `resource_notes`; split only when a single module would exceed the per-child 16K output budget, and keep the total module count at the smallest safe value. Name each module file with a stable lowercase business stem such as `health` or `resource_notes`. Do not use Case-ID-like module filenames such as `BE-HEALTH.md` or `BE-NOTES.md`. The relative link target MUST equal the on-disk filename stem the sharded writer will create. 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.",
|
|
3599
3599
|
"Before finalizing README, 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.",
|
|
3600
3600
|
intake.boundedSourceContext,
|
|
3601
3601
|
"## Authoritative reference index",
|
|
@@ -3641,7 +3641,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3641
3641
|
maxItems: 64,
|
|
3642
3642
|
maxExpandedNodes: 64,
|
|
3643
3643
|
childIdPrefix: "generate-backend-md-case",
|
|
3644
|
-
tokenBudget: { maxTokensPerCase: 16384 },
|
|
3644
|
+
tokenBudget: { maxTokensPerCase: 16384, maxTotalTokens: 600000 },
|
|
3645
3645
|
childTask: {
|
|
3646
3646
|
executor: "pi",
|
|
3647
3647
|
role: "implementer",
|
|
@@ -3680,18 +3680,17 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3680
3680
|
id: "review-and-revise-backend-md-cases-pi",
|
|
3681
3681
|
depends_on: [generateMdCasesMap.id],
|
|
3682
3682
|
role: "reviewer",
|
|
3683
|
-
executor: "
|
|
3684
|
-
|
|
3685
|
-
complexity: "MED",
|
|
3683
|
+
executor: "static",
|
|
3684
|
+
complexity: "LOW",
|
|
3686
3685
|
writePolicy: "exclusive",
|
|
3687
3686
|
writeSet: ["testcase/md/**"],
|
|
3688
3687
|
allowedPaths: ["testcase/md/**"],
|
|
3689
3688
|
forbiddenPaths: forbidden,
|
|
3690
|
-
outputContract: "
|
|
3689
|
+
outputContract: "Deterministic advisory handoff that reserves testcase/md/** in the approved DAG write set for upstream map children; no model invocation or writes.",
|
|
3691
3690
|
subtask_prompt: [
|
|
3692
3691
|
"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.",
|
|
3693
3692
|
"Output budget protocol: default to local edit per file; never dump full Matrix/case bodies into assistant chat. Review order is README (Scope/Matrix) then one module file per turn. When adding omitted in-scope cases, write one file per tool call and keep every required section. Do not bulk-delete in-scope cases to save tokens.",
|
|
3694
|
-
"For every variant Test Point, ensure the Markdown scenario intent is machine-checkable
|
|
3693
|
+
"For every variant Test Point, ensure the Markdown scenario intent is machine-checkable with an exact transport target: `场景意图: <TP-ID>; operation=<METHOD /path>; target=<body.field|query.field|path.field|header.field|request>; intent=<empty|missing|null|min-1|min|max|max+1|pattern-invalid|enum-invalid|wrong-type|nominal-operation|custom-literal:V>; bound=<n optional>; example=<optional>; expectedCode=<optional>`. Never use vague targets such as field=resource/health. Keep pytest params aligned to the exact target.",
|
|
3695
3694
|
"Treat the requirement document as the coverage baseline; scope is limited to operations/rules it (or its referenced API contract) describes, and API contract evidence supplements scenario dimensions. 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. Inspect shared validator/helper/DTO/query builder evidence and expand Affected Operations when the same affected path can affect them; unresolved impact stays visible as GAP/CONFLICT. Directly add in-scope omissions; reject scope expansion to operations absent from the requirement document; undefined impact remains GAP/CONFLICT rather than invented behavior.",
|
|
3696
3695
|
"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.",
|
|
3697
3696
|
"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.",
|
|
@@ -3701,6 +3700,9 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3701
3700
|
JSON.stringify(intake.referenceIndex, null, 2),
|
|
3702
3701
|
"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.",
|
|
3703
3702
|
].join("\n\n"),
|
|
3703
|
+
static: {
|
|
3704
|
+
resultMarkdown: "Markdown module writers completed. Deterministic node 6 validation owns advisory structure/coverage findings; no model review or rewrite was invoked.",
|
|
3705
|
+
},
|
|
3704
3706
|
};
|
|
3705
3707
|
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.");
|
|
3706
3708
|
// N5 line (sharded): pytest shared-asset plan → manifest shell → map_agent barrier.
|
|
@@ -3710,29 +3712,25 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3710
3712
|
id: "generate-backend-pytest-plan-pi",
|
|
3711
3713
|
depends_on: [validateCases.id],
|
|
3712
3714
|
role: "implementer",
|
|
3713
|
-
executor: "
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
writeSet: ["testcase/**/helpers/**", "testcase/**/factories/**"],
|
|
3718
|
-
allowedPaths: Array.from(new Set([...ro, "testcase/**/helpers/**", "testcase/**/factories/**"])),
|
|
3715
|
+
executor: "static",
|
|
3716
|
+
complexity: "LOW",
|
|
3717
|
+
writePolicy: "none",
|
|
3718
|
+
allowedPaths: [],
|
|
3719
3719
|
forbiddenPaths: forbidden,
|
|
3720
|
-
|
|
3721
|
-
writerOutcomePolicy: {
|
|
3722
|
-
type: "implementation-outcome-v1",
|
|
3723
|
-
requireChangedFiles: true,
|
|
3724
|
-
},
|
|
3725
|
-
outputContract: "Write the shared pytest support assets (HTTP logging/redaction helper, request factories, shared fixtures) under testcase/**/helpers/** and testcase/**/factories/** only. Do not write per-module test_*.py here; those are written by downstream sharded nodes. No JSON and no pytest execution.",
|
|
3720
|
+
outputContract: "Deterministic pytest generation handoff. Per-module map children write self-contained test_<module>.py files with bounded local fixtures, HTTP logging/redaction and payload builders; no model invocation, JSON, shared-asset writes or pytest execution.",
|
|
3726
3721
|
subtask_prompt: [
|
|
3727
3722
|
"Convert testcase/md/** into pytest using upstream environment and advisory validation evidence plus only bounded pytest config/conftest. This node writes ONLY the shared pytest support assets (HTTP logging/redaction helper, request factories, shared fixtures); each module's test_<module>.py is written by a downstream sharded node that imports these helpers. A FAIL advisory report does not authorize inventing missing behavior; use the final Markdown facts that are present.",
|
|
3728
3723
|
"Output budget protocol (hard, max output <=16K per turn): Write helpers/factories one file per write/edit tool call. Never paste full Python modules into assistant chat. Do not reduce params/assertions/skips semantics to fit. If OUTPUT_LIMIT_RECOVERY is injected, continue only listed missing/broken shared files.",
|
|
3729
3724
|
"Align every variant pytest.param payload with the Markdown scenario intent (empty/missing/null/length/pattern/enum/wrong-type/nominal). Prefer literal payloads over Faker for intent-critical fields so pre-execution scenario-param checks can verify them.",
|
|
3730
|
-
"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.",
|
|
3725
|
+
"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. If shared pytest fixtures are generated, keep their dependency graph in one provider module and require each downstream test module to register that provider with an exact pytest_plugins tuple; importing only the outer fixture is insufficient and will be rejected by fixture-resolution preflight.",
|
|
3731
3726
|
"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.",
|
|
3732
3727
|
"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.",
|
|
3733
3728
|
"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.",
|
|
3734
3729
|
"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`.",
|
|
3735
3730
|
].join("\n\n"),
|
|
3731
|
+
static: {
|
|
3732
|
+
resultMarkdown: "Shared pytest plan is deterministic: each downstream module writer owns one self-contained test_<module>.py and must not depend on generated shared helper/factory files.",
|
|
3733
|
+
},
|
|
3736
3734
|
};
|
|
3737
3735
|
const materializePytestManifest = {
|
|
3738
3736
|
id: "materialize-backend-pytest-module-manifest-shell",
|
|
@@ -3760,7 +3758,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3760
3758
|
writePolicy: "none",
|
|
3761
3759
|
allowedPaths: [],
|
|
3762
3760
|
forbiddenPaths: forbidden,
|
|
3763
|
-
outputContract: "Serial aggregate of sharded pytest module writers. Each child writes exactly one testcase/test_<stem>.py with its own 16K Pi budget
|
|
3761
|
+
outputContract: "Serial aggregate of sharded pytest module writers. Each child writes exactly one self-contained testcase/test_<stem>.py with its own 16K Pi budget and no generated shared-asset dependency.",
|
|
3764
3762
|
subtask_prompt: "Expand the README module manifest into one sharded pytest writer child per module and run them serially. Child failures fail-close the map barrier.",
|
|
3765
3763
|
static: { resultMarkdown: "Backend-test pytest module map expansion barrier." },
|
|
3766
3764
|
dynamicExpansion: {
|
|
@@ -3771,13 +3769,13 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3771
3769
|
maxItems: 64,
|
|
3772
3770
|
maxExpandedNodes: 64,
|
|
3773
3771
|
childIdPrefix: "generate-backend-pytest-case",
|
|
3774
|
-
tokenBudget: { maxTokensPerCase: 16384 },
|
|
3772
|
+
tokenBudget: { maxTokensPerCase: 16384, maxTotalTokens: 600000 },
|
|
3775
3773
|
childTask: {
|
|
3776
3774
|
executor: "pi",
|
|
3777
3775
|
role: "implementer",
|
|
3778
3776
|
skills: BACKEND_TEST_SKILLS_BY_ROLE.implementer,
|
|
3779
3777
|
toolProfile: "write",
|
|
3780
|
-
complexity: "
|
|
3778
|
+
complexity: "MED",
|
|
3781
3779
|
writePolicy: "exclusive",
|
|
3782
3780
|
allowedPaths: ["testcase/test_{{item.stem}}.py"],
|
|
3783
3781
|
forbiddenPaths: Array.from(new Set([
|
|
@@ -3796,18 +3794,18 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3796
3794
|
retryPolicy: BACKEND_TEST_WRITER_COMPLETENESS_RETRY_POLICY,
|
|
3797
3795
|
outputContract: "Write exactly one pytest module file testcase/test_<stem>.py whose actual test function region contains the exact Case ID, preferably in the function name or docstring. 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.",
|
|
3798
3796
|
subtaskPromptTemplate: [
|
|
3799
|
-
"Convert the single Markdown module testcase/md/{{item.stem}}.md into
|
|
3797
|
+
"Convert the single Markdown module testcase/md/{{item.stem}}.md into one self-contained pytest module. After reading the module Markdown and the bounded pytest config/conftest, immediately use write tools to create the single file testcase/test_{{item.stem}}.py. Define any bounded HTTP client fixture, request logging/redaction/truncation helper and payload builders needed by this module inside that same file; do not import generated testcase/**/helpers/** or testcase/**/factories/** assets. Do not end after analysis or planning. Do not modify Markdown, conftest, helpers/factories, or any other module's pytest script.",
|
|
3800
3798
|
"Output budget protocol (hard, max output <=16K per turn): Write exactly one test_{{item.stem}}.py. Never paste full Python modules into assistant chat. Do not merge or split modules. Do not reduce params/assertions/skips to fit. If OUTPUT_LIMIT_RECOVERY is injected, continue only listed missing/broken scripts.",
|
|
3801
3799
|
"Align every variant pytest.param payload with the Markdown scenario intent (empty/missing/null/length/pattern/enum/wrong-type/nominal). Prefer literal payloads over Faker for intent-critical fields so pre-execution scenario-param checks can verify them.",
|
|
3802
3800
|
'Ensure every final Markdown Case ID in this module 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 前置条件/测试数据/自动化映射.',
|
|
3803
3801
|
"Name the generated pytest file so it corresponds one-to-one with its source Markdown module file: this module stem `{{item.stem}}` maps to exactly one `testcase/test_{{item.stem}}.py`. The <module> stem is the Markdown filename without the `.md` extension, lowercased and with non-alphanumeric characters replaced by underscores. For example, `resource_notes` → `testcase/test_resource_notes.py`, `health` → `testcase/test_health.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. 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.",
|
|
3804
|
-
"
|
|
3802
|
+
"Keep this module self-contained: define module-local fixtures and helpers directly in testcase/test_{{item.stem}}.py, so pytest discovers every fixture dependency without external plugin registration. 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. Recursively redact sensitive values and apply bounded truncation before logging.",
|
|
3805
3803
|
"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`.",
|
|
3806
3804
|
].join("\n\n"),
|
|
3807
3805
|
},
|
|
3808
3806
|
},
|
|
3809
3807
|
};
|
|
3810
|
-
const collectionAssess = shellNode("assess-backend-pytest-collection-shell", [generatePytestCasesMap.id], "markdown-collection-assess", "Resolve final Markdown-mapped scripts before any business test body execution.
|
|
3808
|
+
const collectionAssess = shellNode("assess-backend-pytest-collection-shell", [generatePytestCasesMap.id], "markdown-collection-assess", "Resolve final Markdown-mapped scripts before any business test body execution. Run scenario-param assess/at-most-one deterministic repair first, then pytest --collect-only and a no-business-body pytest --setup-plan fixture-resolution preflight. A safe missing mapped script, generated-local syntax/import defect, or generated fixture dependency/plugin-registration defect is REPAIRABLE; dependency, third-party plugin, production-module, environment, safety and unknown failures remain blocked. Materialize hash-bound collection-v3 facts with repairPaths and fixtureResolutionStatus.", "Run-owned reports/backend-test-pytest-collection-initial.md and contracts/backend-test-pytest-collection-initial.json (collection-v3) with bounded collection/fixture diagnostics, repairPaths, asset hashes, collected item IDs and deterministic repair eligibility.", [], 120000);
|
|
3811
3809
|
const repairPytest = {
|
|
3812
3810
|
id: "repair-backend-pytest-collection-pi",
|
|
3813
3811
|
depends_on: [collectionAssess.id],
|
|
@@ -3835,7 +3833,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3835
3833
|
outputContract: "First non-empty line is IMPLEMENTATION_OUTCOME: changed|already-satisfied|blocked, followed by a concise repair summary. Modify only generated pytest scripts/helpers/factories and preserve every Markdown Case, Test Point, primary symbol and assertion meaning.",
|
|
3836
3834
|
subtask_prompt: [
|
|
3837
3835
|
"Repair the generated backend pytest asset as one bounded program using the direct upstream collection assessment. This is the only repair attempt and happens before any business test body execution.",
|
|
3838
|
-
"Fix only
|
|
3836
|
+
"Fix only readiness-proven generated testcase-local defects on initial facts repairPaths: create exact safe missing mapped test_*.py paths, repair syntax/import/symbol/decorator/parameterization, or close generated fixture dependencies and plugin registration. For fixture defects inspect both provider and importer; when a shared fixture depends on sibling fixtures, register the whole provider module through an exact pytest_plugins declaration rather than importing only the outer fixture. Do not create unrelated pytest scripts.",
|
|
3839
3837
|
"Preserve final testcase/md/** semantics, every Case ID, Rule/Test Point binding, primary symbol, parameter ID, expected status/body/schema assertion, HTTP logging, redaction and truncation behavior.",
|
|
3840
3838
|
"Use local edit only on assessment-listed paths; keep summaries short; never rewrite unrelated modules.",
|
|
3841
3839
|
"Do not read task source/** or reinterpret requirements. Do not modify Markdown, conftest, pytest config, production code or dependencies.",
|
|
@@ -3843,37 +3841,28 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3843
3841
|
"Do not execute pytest; the deterministic effective collection gate owns the final collection attempt.",
|
|
3844
3842
|
].join("\n\n"),
|
|
3845
3843
|
};
|
|
3846
|
-
const collectionEffective = shellNode("effective-backend-pytest-collection-gate-shell", [collectionAssess.id, repairPytest.id], "markdown-collection-effective", "If initial collection passed, verify unchanged asset hashes and reuse it
|
|
3844
|
+
const collectionEffective = shellNode("effective-backend-pytest-collection-gate-shell", [collectionAssess.id, repairPytest.id], "markdown-collection-effective", "If initial collection+fixture readiness passed, verify unchanged asset hashes and reuse it. If the single repair ran, rerun scenario-param preflight, final collection and no-business-body fixture resolution once. BLOCKED facts, repair failure, residual fixture failure or hash drift prevent business pytest execution. Materialize canonical backend-test-execution-readiness.json.", "Run-owned effective collection-v3 facts plus contracts/backend-test-execution-readiness.json proving exact final assets are collectable, fixture-resolvable and hash-bound; initial PASS is reused, repair path records attempt=1.", [], 120000);
|
|
3847
3845
|
collectionEffective.dependsPolicy = "all-or-condition-skip";
|
|
3848
|
-
const traceability = shellNode("backend-test-traceability-gate-shell", [collectionEffective.id], "markdown-traceability", "Deterministically scan only Markdown-mapped pytest scripts
|
|
3846
|
+
const traceability = shellNode("backend-test-traceability-gate-shell", [collectionEffective.id], "markdown-traceability", "Deterministically scan only final readiness-authorized Markdown-mapped pytest scripts. Produce bidirectional Markdown module/Case/Test Point ↔ pytest file/primary symbol correspondence and logging findings. scenario-param assessment/repair already ran before collection; consume and display its final PASS/PARTIAL/FAIL/UNAVAILABLE facts without modifying pytest assets after readiness was frozen. Correspondence findings remain advisory; Never block pytest solely on correspondence FAIL.", "Run-owned reports/backend-test-traceability.md, reports/backend-test-markdown-pytest-correspondence.md, contracts/backend-test-markdown-pytest-correspondence-facts.json, reports/backend-test-scenario-param-consistency.md and contracts/backend-test-scenario-param-consistency-facts.json (initial+final) with optional repair audit; PASS/FAIL/UNAVAILABLE correspondence facts bound after effective collection.");
|
|
3849
3847
|
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.");
|
|
3850
3848
|
const pytestCommand = [
|
|
3851
3849
|
'mkdir -p "${HARNESS_DAG_RUN_DIR}/reports"',
|
|
3852
3850
|
'echo "pytest targets are resolved at runtime from final Markdown 自动化映射"',
|
|
3853
3851
|
].join("; ");
|
|
3854
|
-
const execute = shellNode("execute-backend-pytest-and-html-report-shell", [manifest.id], "markdown-execute-html", "
|
|
3852
|
+
const execute = shellNode("execute-backend-pytest-and-html-report-shell", [manifest.id], "markdown-execute-html", "Read canonical contracts/backend-test-execution-readiness.json, verify final asset hashes, then resolve the final Markdown Automation Notes/自动化映射 to a unique, safe set of testcase/**/test_*.py targets and execute only those scripts exactly once. Prefer the deterministic module one-to-one path when a mapped script is missing but the module stem file exists. Generate a native pytest-html self-contained report, then render the primary self-contained Chinese HTML report from the same pytest-html plus final Markdown case metadata without rerun. Keep 测试结论 and quality status; make node 6 Markdown validation + case coverage and node 13 traceability + Markdown-to-pytest correspondence expandable to their full escaped details; show each failure overview item with its original pytest message plus deterministic evidence-based reason analysis; list failure/error case cards before the remaining cases while preserving stable order. Each polished per-case result card includes concise scenario, automation test name, result, duration, and redacted bounded HTTP request parameters/response results for both passed and failed cases. Do not render a technical/execution evidence section in HTML; retain auditable paths and hashes in facts.", "One scoped pytest execution over Markdown-mapped scripts producing a valid pytest-html report with per-case captured output, self-contained reports/backend-test.html, reports/backend-test.md, reports/backend-test-facts.md, a deterministic self-contained reports/backend-test-l5-dashboard.html (machine-computed L-5 metrics, no JSON), and an optional contracts/code-coverage-v1.json when jacocoCoverage is configured (JaCoCo TCP dump → jacoco.xml → parsed; failure-safe); exit 0/1 with valid evidence continues.", [pytestCommand], 300000);
|
|
3855
3853
|
if (execute.shell) {
|
|
3856
3854
|
execute.shell.envAllowlist = collectBackendTestShellEnvAllowlist(sources);
|
|
3857
3855
|
}
|
|
3858
|
-
const canWriteReport = taskAllowsBackendTestReportWrite(sources);
|
|
3859
3856
|
const report = {
|
|
3860
3857
|
id: "backend-test-report-and-l5-pi",
|
|
3861
3858
|
depends_on: [execute.id],
|
|
3862
3859
|
role: "closeout",
|
|
3863
|
-
executor: "
|
|
3864
|
-
complexity: "
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
toolProfile: "write",
|
|
3868
|
-
writePolicy: "exclusive",
|
|
3869
|
-
writeSet: ["docs/test-reports/**"],
|
|
3870
|
-
allowedPaths: ["docs/test-reports/**"],
|
|
3871
|
-
}
|
|
3872
|
-
: { writePolicy: "read-only", allowedPaths: ro }),
|
|
3860
|
+
executor: "static",
|
|
3861
|
+
complexity: "LOW",
|
|
3862
|
+
writePolicy: "none",
|
|
3863
|
+
allowedPaths: [],
|
|
3873
3864
|
forbiddenPaths: forbidden,
|
|
3874
|
-
outputContract:
|
|
3875
|
-
? "Final Markdown report and L-5 conclusion under docs/test-reports/**; the deterministic L-5 dashboard at reports/backend-test-l5-dashboard.html is the authoritative visualization and must be linked, not re-rendered; no JSON."
|
|
3876
|
-
: "Final Markdown report and L-5 conclusion in assistant output; the deterministic L-5 dashboard at reports/backend-test-l5-dashboard.html is the authoritative visualization and must be linked; no JSON or writes.",
|
|
3865
|
+
outputContract: "Deterministic evidence handoff linking the node 15 Markdown facts, HTML report, failure analysis and L-5 dashboard; no model invocation, JSON or writes.",
|
|
3877
3866
|
subtask_prompt: [
|
|
3878
3867
|
"Generate the final Markdown report only from authoritative run-owned artifacts. Read node 1 reports/backend-test-environment.md; node 6 backend-md-case-validation.md and backend-test-case-coverage-analysis.md; nodes 10/12 backend-test-pytest-collection initial/effective reports and facts; node 13 backend-test-traceability.md, backend-test-markdown-pytest-correspondence.md and backend-test-scenario-param-consistency.md; node 14 contracts/backend-test-case-manifest.json; and node 15 backend-test-result.json, backend-test-facts.md, backend-test-failure-analysis.md, pytest-html/HTML and L-5 dashboard. Do not use node 2/3/4/7/8/9 assistant prose as facts. Do not emit JSON.",
|
|
3879
3868
|
"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. Prefer linking reports/backend-test-failure-analysis.md for structured failure analysis rather than inventing classifications.",
|
|
@@ -3882,10 +3871,18 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3882
3871
|
"Always state the exact Coverage Scope classification, policy, affected operations, regression floor, completeness claim, PASS/FAIL/UNAVAILABLE status and findings from node 6 case validation + coverage, plus node 13 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.",
|
|
3883
3872
|
"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.",
|
|
3884
3873
|
"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.",
|
|
3885
|
-
|
|
3886
|
-
? "Write only under docs/test-reports/**."
|
|
3887
|
-
: "Keep the full report in assistant output.",
|
|
3874
|
+
"Node 15 already rendered the authoritative Markdown and HTML outputs; this node only hands off their stable paths without recomputation.",
|
|
3888
3875
|
].join("\n\n"),
|
|
3876
|
+
static: {
|
|
3877
|
+
resultMarkdown: [
|
|
3878
|
+
"# Backend-test deterministic closeout",
|
|
3879
|
+
"",
|
|
3880
|
+
"Authoritative execution facts: `reports/backend-test.md` and `contracts/backend-test-result.json`.",
|
|
3881
|
+
"Failure analysis: `reports/backend-test-failure-analysis.md`.",
|
|
3882
|
+
"HTML report: `reports/backend-test.html`.",
|
|
3883
|
+
"L-5 dashboard: `reports/backend-test-l5-dashboard.html`.",
|
|
3884
|
+
].join("\n"),
|
|
3885
|
+
},
|
|
3889
3886
|
};
|
|
3890
3887
|
const spec = {
|
|
3891
3888
|
version: 3,
|
|
@@ -3,6 +3,7 @@ import path from "node:path";
|
|
|
3
3
|
import { generateTaskDagUseCase } from "../../application/dag/generate-task-dag.js";
|
|
4
4
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
5
|
import { DAG_RUNS_DIR, isTerminalDagRunStatus, locateDagRun, readDagRunSpec, readDagRunState, } from "./lifecycle.js";
|
|
6
|
+
import { parseDagSpec } from "./types.js";
|
|
6
7
|
export const RERUN_TASK_LINEAGE_REL_PATH = ".runtime/rerun-task-lineage.json";
|
|
7
8
|
export function parseDagRerunTaskArgs(args) {
|
|
8
9
|
if (args.length === 0) {
|
|
@@ -142,6 +143,46 @@ async function fileExists(filePath) {
|
|
|
142
143
|
return false;
|
|
143
144
|
}
|
|
144
145
|
}
|
|
146
|
+
export const GENERATED_OUTPUT_CONFLICT_REASON = "generated-output-conflict";
|
|
147
|
+
/**
|
|
148
|
+
* Determine whether a regenerated DAG would overwrite existing/dirty generated
|
|
149
|
+
* outputs that overlap a requireChangedFiles writer's declared write set.
|
|
150
|
+
*
|
|
151
|
+
* Only concrete writeSet entries are checked: glob-only entries (containing
|
|
152
|
+
* `*`) are skipped, consistent with the bounded-writer constraint that a
|
|
153
|
+
* requireChangedFiles writer declare concrete paths. This is a deterministic,
|
|
154
|
+
* path-bounded preflight — it never deletes files and never inspects `.harness/**`
|
|
155
|
+
* internals. Returns the list of conflicting concrete paths (relative to cwd).
|
|
156
|
+
*/
|
|
157
|
+
export async function assessGeneratedOutputConflict(spec, cwd) {
|
|
158
|
+
const conflictPaths = [];
|
|
159
|
+
const seen = new Set();
|
|
160
|
+
for (const task of spec.tasks) {
|
|
161
|
+
const isRequireChangedFilesWriter = task.executor === "pi" &&
|
|
162
|
+
task.toolProfile === "write" &&
|
|
163
|
+
task.writerOutcomePolicy?.type === "implementation-outcome-v1" &&
|
|
164
|
+
task.writerOutcomePolicy?.requireChangedFiles === true;
|
|
165
|
+
if (!isRequireChangedFilesWriter)
|
|
166
|
+
continue;
|
|
167
|
+
const writeSet = task.writeSet ?? [];
|
|
168
|
+
for (const entry of writeSet) {
|
|
169
|
+
const normalized = entry
|
|
170
|
+
.trim()
|
|
171
|
+
.replace(/\\/g, "/")
|
|
172
|
+
.replace(/^\.\//, "");
|
|
173
|
+
if (!normalized || normalized.includes("*"))
|
|
174
|
+
continue;
|
|
175
|
+
if (seen.has(normalized))
|
|
176
|
+
continue;
|
|
177
|
+
seen.add(normalized);
|
|
178
|
+
const absolute = path.join(cwd, normalized);
|
|
179
|
+
if (await fileExists(absolute)) {
|
|
180
|
+
conflictPaths.push(normalized);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return conflictPaths;
|
|
185
|
+
}
|
|
145
186
|
async function readExistingOperatorRequest(cwd, requestId) {
|
|
146
187
|
try {
|
|
147
188
|
const raw = JSON.parse(await readFile(operatorRequestPath(cwd, requestId), "utf-8"));
|
|
@@ -190,6 +231,7 @@ function blockedResult(input) {
|
|
|
190
231
|
blockedReason: input.blockedReason,
|
|
191
232
|
...(input.note ? { note: input.note } : {}),
|
|
192
233
|
...(input.taskId ? { taskId: input.taskId } : {}),
|
|
234
|
+
...(input.conflictPaths ? { conflictPaths: input.conflictPaths } : {}),
|
|
193
235
|
};
|
|
194
236
|
}
|
|
195
237
|
function extractRunSummary(generateResult) {
|
|
@@ -236,6 +278,50 @@ export async function rerunStandaloneTask(input) {
|
|
|
236
278
|
});
|
|
237
279
|
}
|
|
238
280
|
const execute = input.execute ?? true;
|
|
281
|
+
// AC-001 generated-output-conflict preflight: when a real execution is
|
|
282
|
+
// requested, first generate the DAG draft (execute:false) so we can inspect
|
|
283
|
+
// the regenerated spec's requireChangedFiles writer write sets against
|
|
284
|
+
// existing/dirty generated outputs. This is deterministic and path-bounded:
|
|
285
|
+
// it never deletes files and never reads `.harness/**` internals. Only the
|
|
286
|
+
// two-phase path pays the double-generate cost; --no-execute and idempotent
|
|
287
|
+
// replays are unaffected.
|
|
288
|
+
if (execute) {
|
|
289
|
+
const preflight = await generateTaskDagUseCase({
|
|
290
|
+
repoRoot: input.cwd,
|
|
291
|
+
taskId: taskResolution.taskId,
|
|
292
|
+
cwd: input.cwd,
|
|
293
|
+
strictModels: true,
|
|
294
|
+
execute: false,
|
|
295
|
+
initOnly: false,
|
|
296
|
+
dryRun: false,
|
|
297
|
+
profile: input.profile ?? "auto",
|
|
298
|
+
profileExplicit: input.profileExplicit ?? false,
|
|
299
|
+
});
|
|
300
|
+
if (preflight.mode === "generate+basic-validate") {
|
|
301
|
+
let conflictPaths = [];
|
|
302
|
+
try {
|
|
303
|
+
const raw = await readFile(preflight.outputPath, "utf8");
|
|
304
|
+
const preflightSpec = parseDagSpec(JSON.parse(raw));
|
|
305
|
+
conflictPaths = await assessGeneratedOutputConflict(preflightSpec, input.cwd);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// If the draft cannot be parsed, fall through to the real execution
|
|
309
|
+
// path: execution's own validation will surface the structural defect.
|
|
310
|
+
conflictPaths = [];
|
|
311
|
+
}
|
|
312
|
+
if (conflictPaths.length > 0) {
|
|
313
|
+
return blockedResult({
|
|
314
|
+
parentRunId: input.parentRunId,
|
|
315
|
+
reason: input.reason,
|
|
316
|
+
requestId: input.requestId,
|
|
317
|
+
taskId: taskResolution.taskId,
|
|
318
|
+
blockedReason: GENERATED_OUTPUT_CONFLICT_REASON,
|
|
319
|
+
conflictPaths,
|
|
320
|
+
note: `generated-output-conflict: existing outputs overlap requireChangedFiles writer write sets at ${conflictPaths.join(", ")}; use a fresh worktree or precisely restore the listed paths before rerunning — rerun-task will not delete files automatically and will not accept arbitrary already-satisfied generation outcomes`,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
239
325
|
const generateResult = await generateTaskDagUseCase({
|
|
240
326
|
repoRoot: input.cwd,
|
|
241
327
|
taskId: taskResolution.taskId,
|
package/docs/README.md
CHANGED
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
| 运行 DAG、查看报告或处理失败 | `runtime/agent-dag-runner.md`、`runtime/agent-dag-recovery-playbook.md` |
|
|
30
30
|
| 按 TDD、验证和调试纪律推进修复 | `governance/harness-methodology-tdd.md`、`governance/harness-methodology-verification.md`、`governance/harness-methodology-debugging.md` |
|
|
31
31
|
| 执行真实 backend-test provider 验证 | `runtime/backend-test-live-campaign.md` |
|
|
32
|
-
|
|
|
32
|
+
| 判断单任务 loop-agent Production Readiness | `operations/production-readiness.md` |
|
|
33
|
+
| 判断 agent-worker Feature/Attempt Production Readiness v1 | `operations/agent-worker-production-readiness.md`、`templates/agent-worker-production-readiness-checklist.md` |
|
|
34
|
+
| 分阶段推进 agent-worker Production Readiness v1 | `exec-plans/active/2026-08-09-agent-worker-production-readiness-v1-program.md`、`exec-plans/active/README.md` |
|
|
33
35
|
| 合并分支并留下 source-SHA 报告 | `operations/branch-merge-guideline.md` |
|
|
34
36
|
| 使用仓库内 GitHub 协作方式 | `operations/github-collaboration.md` |
|
|
35
37
|
| 排查本机开发环境问题 | `operations/local-development-environment.md` |
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
| Backend-test | **Markdown-first 8 节点**;Markdown 映射限定 pytest 执行,生成 Markdown 与分层 HTML/脱敏请求响应详情 | completed `2026-07-24-backend-test-scoped-execution-report-ui.md`;`CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
21
21
|
| Frontend-test | 默认 short-chain RAG;内容/evidence 质量进入 advisory findings,路径与安全边界仍 fail-closed | `CHANGELOG.md [0.21.0]` / `[0.22.0]` |
|
|
22
22
|
| Frontend-implementation | Contract / trace / repair / Mock assess;`ai_workspace` + 知识库 + `openspec` 规范发现;lint 基线债务隔离 | `CHANGELOG.md [0.22.0]` |
|
|
23
|
-
| Inspect(原 Observe) | 统一 Console 的 `/inspect/`
|
|
23
|
+
| Inspect(原 Observe) | 统一 Console 的 `/inspect/` 只读运营面与富时间线;独立 `observe serve` 已硬下线 | `website/docs/guides/observe-ui.md`、`CHANGELOG.md [Unreleased]` |
|
|
24
24
|
| Local Operator Console | `agent-worker console serve\|doctor`(loopback);Operate + Inspect、Happy Path / Interview / split view / recovery CTA;**0.23.x 起含 General Operator Chat**(Pi SDK services session、operator-only tool surface);**非**远端多用户 Console | unify design;ADR 0005;completed `2026-07-25-console-phase-4-general-operator-chat.md` |
|
|
25
25
|
| Task Contract / operator surface | journaled `task contract *`;machine envelope;**DagSpec v4 `taskContractBinding`**(exclusive writer 强制) | `CHANGELOG.md [0.17.0]`–`[0.17.2]`;ADR 0005 |
|
|
26
26
|
| DagSpec / repair | v3 `runtimeContract` + 显式 `repairNodeId`;新 writer 生成默认 v4 binding | `dag-execution.md`、`CHANGELOG.md [0.11.0]` / `[0.17.0]` |
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- [`branch-merge-guideline.md`](branch-merge-guideline.md):按风险选择合并模式并留下 source-SHA 证据。
|
|
9
9
|
- [`github-collaboration.md`](github-collaboration.md):仓库内 GitHub 协作约定及自动/人工发布通道。
|
|
10
10
|
- [`production-readiness.md`](production-readiness.md):交付前的生产就绪判断。
|
|
11
|
+
- [`agent-worker-production-readiness.md`](agent-worker-production-readiness.md):agent-worker Feature/Task Pool 外层编排的 Production Readiness v1 操作合同。
|
|
11
12
|
- [`backend-test-jacoco-coverage.md`](backend-test-jacoco-coverage.md):Java 被测服务挂载 JaCoCo tcpserver agent 的 Maven、`java -jar` 与 Docker 操作手册。
|
|
12
13
|
|
|
13
14
|
验证命令仍以 [`../governance/verification-matrix.md`](../governance/verification-matrix.md) 为准。
|
package/docs/templates/README.md
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
- `branch-merge-report.md` — source-SHA 分支合并报告。
|
|
15
15
|
- `init-evolution-review.md` — 初始化能力演化审查。
|
|
16
16
|
- `production-readiness-checklist.md` — Production Readiness 检查。
|
|
17
|
+
- `agent-worker-production-readiness-checklist.md` — agent-worker Feature/Task Pool Production Readiness v1 campaign 检查清单。
|
|
17
18
|
- `worker-dogfood-setup.md`、`worker-dogfood-evidence.md` — 已发布控制器下的 Worker dogfood 设置与证据。
|
|
18
19
|
- `interactive-ui-round2-experiment.md` — 交互式 UI 对照实验。
|
|
19
20
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Agent Worker Production Readiness v1 Checklist
|
|
2
|
+
|
|
3
|
+
用于 Phase 8 campaign / 发布门禁。勾选前必须有新鲜命令输出或 report ref。
|
|
4
|
+
|
|
5
|
+
## 包络与身份
|
|
6
|
+
|
|
7
|
+
- [ ] 支持包络已冻结(单机/单用户/单仓)
|
|
8
|
+
- [ ] controller packageVersion + fingerprint 已记录
|
|
9
|
+
- [ ] 无 npm link / workspace bin 冒充正式 controller
|
|
10
|
+
- [ ] protocol/capabilities preflight 通过
|
|
11
|
+
|
|
12
|
+
## Ownership / Attempt
|
|
13
|
+
|
|
14
|
+
- [ ] Running 投影不依赖 terminal ledger
|
|
15
|
+
- [ ] Task-scoped events 含 featureId
|
|
16
|
+
- [ ] dual Feature 同名 Task 隔离
|
|
17
|
+
- [ ] ownership warning 按根因聚合(≤1 finding/根因)
|
|
18
|
+
|
|
19
|
+
## Lifecycle / Recovery
|
|
20
|
+
|
|
21
|
+
- [ ] safe run-error → Failed → `task retry` 无需 mark-failed
|
|
22
|
+
- [ ] Blocked 保留 contract/human 语义
|
|
23
|
+
- [ ] resume / retry / reconcile / revise / decide 决策一致
|
|
24
|
+
|
|
25
|
+
## Lease
|
|
26
|
+
|
|
27
|
+
- [ ] `{featureId, taskId}` 单 active writer
|
|
28
|
+
- [ ] begin/finalize crash injection 有 doctor finding
|
|
29
|
+
- [ ] 不盲目偷取 live DAG lease
|
|
30
|
+
|
|
31
|
+
## Acceptance / Delivery
|
|
32
|
+
|
|
33
|
+
- [ ] Task Done ≠ AC covered
|
|
34
|
+
- [ ] `feature verify-final` 唯一 writer
|
|
35
|
+
- [ ] verify-final → delivery → closeout 可收口
|
|
36
|
+
- [ ] dirty HEAD / tamper fail-closed
|
|
37
|
+
|
|
38
|
+
## Campaign / SLO
|
|
39
|
+
|
|
40
|
+
- [ ] C0–C11 已执行或明确 blocked-external
|
|
41
|
+
- [ ] burn-in 窗口达标(7 日或 30 Attempt)
|
|
42
|
+
- [ ] SLO 分子/分母已记录
|
|
43
|
+
- [ ] promote + rollback drill 完成
|
|
44
|
+
- [ ] scorecard: READY 或合法 CONDITIONALLY_READY
|
|
45
|
+
- [ ] open P0 = 0
|