mandrel 1.94.0 → 2.1.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/README.md +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +328 -28
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +3 -53
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -8
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- package/.agents/workflows/helpers/single-story-deliver.md +0 -557
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Tech Spec #819 §"Evidence record (Story 7)". Each successful gate run
|
|
5
5
|
* writes a record keyed by `{ gateName, commitSha, commandConfigHash }` to
|
|
6
|
-
* a
|
|
6
|
+
* a storyId-anchored path under the resolved `tempRoot`:
|
|
7
7
|
*
|
|
8
|
-
* -
|
|
9
|
-
* `<tempRoot>/epic-<epicId>/validation-evidence.json`
|
|
10
|
-
* - Story-scoped (scopeId === storyId):
|
|
11
|
-
* `<tempRoot>/epic-<epicId>/story-<storyId>/validation-evidence.json`
|
|
8
|
+
* `<tempRoot>/standalone/stories/story-<storyId>/validation-evidence.json`
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* The path is gitignored via `temp/`. v2.0.0 removed the Epic tier along
|
|
11
|
+
* with the Epic-keyed `<tempRoot>/epic-<epicId>/…` keyspace.
|
|
15
12
|
*
|
|
16
13
|
* A subsequent caller can `shouldSkip(...)` to learn whether the same gate
|
|
17
14
|
* has already passed against the current HEAD with an identical
|
|
@@ -34,7 +31,7 @@ import path from 'node:path';
|
|
|
34
31
|
import { fileURLToPath } from 'node:url';
|
|
35
32
|
import Ajv from 'ajv/dist/2020.js';
|
|
36
33
|
import addFormats from 'ajv-formats';
|
|
37
|
-
import {
|
|
34
|
+
import { storyTempDir } from './config/temp-paths.js';
|
|
38
35
|
|
|
39
36
|
export const SCHEMA_VERSION = 1;
|
|
40
37
|
const DEFAULT_TEMP_DIR = 'temp';
|
|
@@ -97,37 +94,28 @@ function requirePositiveInt(value, label) {
|
|
|
97
94
|
|
|
98
95
|
/**
|
|
99
96
|
* Compute the absolute path of the evidence file for `scopeId` under the
|
|
100
|
-
*
|
|
97
|
+
* storyId-anchored temp tree.
|
|
101
98
|
*
|
|
102
|
-
*
|
|
103
|
-
* - `scopeId === epicId` → `<tempRoot>/epic-<epicId>/validation-evidence.json`
|
|
104
|
-
* - `scopeId !== epicId` → treated as a Story id → `<tempRoot>/epic-<epicId>/story-<scopeId>/validation-evidence.json`
|
|
105
|
-
*
|
|
106
|
-
* **Standalone keyspace (Story #4250).** When `opts.standalone === true`,
|
|
99
|
+
* **Standalone keyspace (Story #4250).** `opts.standalone` must be `true`:
|
|
107
100
|
* the Story has no parent Epic, so the evidence file is anchored on the
|
|
108
101
|
* Story id alone at
|
|
109
102
|
* `<tempRoot>/standalone/stories/story-<scopeId>/validation-evidence.json`
|
|
110
|
-
* (the `storyTempDir(null, sid)` layout from Story #2874).
|
|
111
|
-
* `epicId` is ignored — callers MUST NOT feed a `0`/`null` epicId into the
|
|
112
|
-
* Epic-keyed branch (the historical bug this keyspace replaces). Outside
|
|
113
|
-
* standalone mode `epicId` remains required.
|
|
103
|
+
* (the `storyTempDir(null, sid)` layout from Story #2874).
|
|
114
104
|
*
|
|
115
|
-
* The legacy flat `temp/validation-evidence-<scopeId>.json` layout
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* override the default `'temp'`.
|
|
105
|
+
* The legacy flat `temp/validation-evidence-<scopeId>.json` layout and the
|
|
106
|
+
* Epic-keyed `<tempRoot>/epic-<epicId>/…` layout are both gone. The
|
|
107
|
+
* synthetic config bag passed to `storyTempDir` keeps the resolver from
|
|
108
|
+
* doing a disk-bound `.agentrc.json` lookup; bare callers can pass
|
|
109
|
+
* `tempDir` via `opts` to override the default `'temp'`.
|
|
121
110
|
*
|
|
122
111
|
* @param {number|string} scopeId
|
|
123
|
-
* @param {{ cwd?: string, tempDir?: string,
|
|
112
|
+
* @param {{ cwd?: string, tempDir?: string, standalone?: boolean }} opts
|
|
124
113
|
* @returns {string}
|
|
125
114
|
*/
|
|
126
115
|
export function evidencePath(scopeId, opts = {}) {
|
|
127
|
-
|
|
128
|
-
if (!standalone && opts.epicId == null) {
|
|
116
|
+
if (opts.standalone !== true) {
|
|
129
117
|
throw new Error(
|
|
130
|
-
'[validation-evidence] evidencePath requires opts.
|
|
118
|
+
'[validation-evidence] evidencePath requires opts.standalone (the storyId-anchored keyspace is the only keyspace).',
|
|
131
119
|
);
|
|
132
120
|
}
|
|
133
121
|
const { cwd, tempDir } = resolveOpts(opts);
|
|
@@ -143,18 +131,9 @@ export function evidencePath(scopeId, opts = {}) {
|
|
|
143
131
|
? tempDir
|
|
144
132
|
: path.join(cwd, tempDir);
|
|
145
133
|
const configBag = { project: { paths: { tempRoot: absTempRoot } } };
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// standalone-story sentinel `storyTempDir` accepts (Story #2874).
|
|
150
|
-
dir = storyTempDir(null, scope, configBag);
|
|
151
|
-
} else {
|
|
152
|
-
const epicId = requirePositiveInt(opts.epicId, 'epicId');
|
|
153
|
-
dir =
|
|
154
|
-
scope === epicId
|
|
155
|
-
? epicTempDir(epicId, configBag)
|
|
156
|
-
: storyTempDir(epicId, scope, configBag);
|
|
157
|
-
}
|
|
134
|
+
// Story #4250 — storyId-anchored standalone keyspace. `null` is the
|
|
135
|
+
// standalone-story sentinel `storyTempDir` accepts (Story #2874).
|
|
136
|
+
const dir = storyTempDir(null, scope, configBag);
|
|
158
137
|
return path.join(dir, EVIDENCE_FILENAME);
|
|
159
138
|
}
|
|
160
139
|
|
|
@@ -189,19 +168,17 @@ function emptyDoc(scopeId) {
|
|
|
189
168
|
* cross-scopeId cases — callers don't have to branch on those failure
|
|
190
169
|
* modes; they manifest as `shouldSkip()` returning `skip: false`.
|
|
191
170
|
*
|
|
192
|
-
* `opts.
|
|
193
|
-
*
|
|
194
|
-
* storyId-anchored standalone keyspace.
|
|
171
|
+
* `opts.standalone` is required (Story #4250) — it routes to the
|
|
172
|
+
* storyId-anchored keyspace.
|
|
195
173
|
*
|
|
196
174
|
* @param {number|string} scopeId
|
|
197
|
-
* @param {{ cwd?: string, tempDir?: string,
|
|
175
|
+
* @param {{ cwd?: string, tempDir?: string, standalone?: boolean, fs?: object }} opts
|
|
198
176
|
* @returns {{ storyId: number, schemaVersion: number, records: object[] }}
|
|
199
177
|
*/
|
|
200
178
|
export function loadEvidence(scopeId, opts = {}) {
|
|
201
179
|
const resolved = resolveOpts(opts);
|
|
202
180
|
const file = evidencePath(scopeId, {
|
|
203
181
|
...resolved,
|
|
204
|
-
epicId: opts.epicId,
|
|
205
182
|
standalone: opts.standalone,
|
|
206
183
|
});
|
|
207
184
|
if (!resolved.fs.existsSync(file)) return emptyDoc(scopeId);
|
|
@@ -223,9 +200,8 @@ export function loadEvidence(scopeId, opts = {}) {
|
|
|
223
200
|
* Validates the resulting document against the schema before writing — a
|
|
224
201
|
* malformed write throws so the bug surfaces immediately.
|
|
225
202
|
*
|
|
226
|
-
* `opts.
|
|
227
|
-
*
|
|
228
|
-
* storyId-anchored standalone keyspace.
|
|
203
|
+
* `opts.standalone` is required (Story #4250) — it routes to the
|
|
204
|
+
* storyId-anchored keyspace.
|
|
229
205
|
*
|
|
230
206
|
* @param {{
|
|
231
207
|
* storyId: number|string,
|
|
@@ -235,7 +211,7 @@ export function loadEvidence(scopeId, opts = {}) {
|
|
|
235
211
|
* exitCode?: number,
|
|
236
212
|
* durationMs?: number|null,
|
|
237
213
|
* }} input
|
|
238
|
-
* @param {{ cwd?: string, tempDir?: string,
|
|
214
|
+
* @param {{ cwd?: string, tempDir?: string, standalone?: boolean, fs?: object, now?: Function }} opts
|
|
239
215
|
* @returns {object} The persisted record.
|
|
240
216
|
*/
|
|
241
217
|
export function recordPass(
|
|
@@ -258,7 +234,6 @@ export function recordPass(
|
|
|
258
234
|
const resolved = resolveOpts(opts);
|
|
259
235
|
const evidenceOpts = {
|
|
260
236
|
...resolved,
|
|
261
|
-
epicId: opts.epicId,
|
|
262
237
|
standalone: opts.standalone,
|
|
263
238
|
};
|
|
264
239
|
const doc = loadEvidence(storyId, evidenceOpts);
|
|
@@ -297,13 +272,12 @@ export function recordPass(
|
|
|
297
272
|
* with a machine-readable `reason` so callers can log why the skip didn't
|
|
298
273
|
* fire.
|
|
299
274
|
*
|
|
300
|
-
* `opts.
|
|
301
|
-
*
|
|
302
|
-
* storyId-anchored standalone keyspace. `opts` is forwarded verbatim to
|
|
275
|
+
* `opts.standalone` is required (Story #4250) — it routes to the
|
|
276
|
+
* storyId-anchored keyspace. `opts` is forwarded verbatim to
|
|
303
277
|
* `loadEvidence`, so `standalone` flows through unchanged.
|
|
304
278
|
*
|
|
305
279
|
* @param {{ storyId: number|string, gateName: string, currentSha: string, configHash: string }} input
|
|
306
|
-
* @param {{ cwd?: string, tempDir?: string,
|
|
280
|
+
* @param {{ cwd?: string, tempDir?: string, standalone?: boolean, fs?: object }} opts
|
|
307
281
|
* @returns {{ skip: boolean, reason: string, record?: object }}
|
|
308
282
|
*/
|
|
309
283
|
export function shouldSkip(
|
|
@@ -336,23 +310,21 @@ export function shouldSkip(
|
|
|
336
310
|
}
|
|
337
311
|
|
|
338
312
|
/**
|
|
339
|
-
* Delete the evidence file for `scopeId`. Called by `story-init.js` at the
|
|
313
|
+
* Delete the evidence file for `scopeId`. Called by `single-story-init.js` at the
|
|
340
314
|
* start of each Story so a re-run always starts clean. Idempotent —
|
|
341
315
|
* absent file is not an error.
|
|
342
316
|
*
|
|
343
|
-
* `opts.
|
|
344
|
-
*
|
|
345
|
-
* storyId-anchored standalone keyspace.
|
|
317
|
+
* `opts.standalone` is required (Story #4250) — it routes to the
|
|
318
|
+
* storyId-anchored keyspace.
|
|
346
319
|
*
|
|
347
320
|
* @param {number|string} scopeId
|
|
348
|
-
* @param {{ cwd?: string, tempDir?: string,
|
|
321
|
+
* @param {{ cwd?: string, tempDir?: string, standalone?: boolean, fs?: object }} opts
|
|
349
322
|
* @returns {{ cleared: boolean, path: string }}
|
|
350
323
|
*/
|
|
351
324
|
export function forceClear(scopeId, opts = {}) {
|
|
352
325
|
const resolved = resolveOpts(opts);
|
|
353
326
|
const file = evidencePath(scopeId, {
|
|
354
327
|
...resolved,
|
|
355
|
-
epicId: opts.epicId,
|
|
356
328
|
standalone: opts.standalone,
|
|
357
329
|
});
|
|
358
330
|
if (!resolved.fs.existsSync(file)) return { cleared: false, path: file };
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* lib/wave-runner/ready-set.js — the path-agnostic ready-set scheduling
|
|
3
3
|
* core.
|
|
4
4
|
*
|
|
5
|
-
* This module is the scheduling kernel
|
|
6
|
-
*
|
|
5
|
+
* This module is the scheduling kernel the v2 `/deliver` multi-Story path
|
|
6
|
+
* dispatches through (`stories-wave-tick.js`). It replaces wave-*batch* selection
|
|
7
7
|
* (group N must fully drain before group N+1 opens) with *continuous*,
|
|
8
8
|
* dependency-driven selection: a Story becomes dispatchable the instant
|
|
9
9
|
* **its own** dependencies are satisfied, regardless of whether unrelated
|
|
@@ -15,14 +15,14 @@
|
|
|
15
15
|
* reads GitHub, the lifecycle ledger, nor a checkpoint, and it dispatches
|
|
16
16
|
* nothing. Callers supply the live Story records (already fetched), the
|
|
17
17
|
* resolved `inFlight` count, and the `globalCap`, and receive back the set
|
|
18
|
-
* of Stories that are safe to dispatch on this beat.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* of Stories that are safe to dispatch on this beat. The
|
|
19
|
+
* `stories-wave-tick.js` adapter wires this core; this module does not
|
|
20
|
+
* modify that CLI surface.
|
|
21
21
|
*
|
|
22
22
|
* Three exports:
|
|
23
23
|
* - `classifyStory(story)` — live-label classifier mapping a Story
|
|
24
24
|
* record's labels + issue state to one of `done | blocked | executing |
|
|
25
|
-
* ready`. Mirrors the done-predicate
|
|
25
|
+
* ready`. Mirrors the done-predicate this module uses
|
|
26
26
|
* (`agent::done` OR closed issue) so a Story closed manually through
|
|
27
27
|
* the GitHub UI is recognised as done.
|
|
28
28
|
* - `storiesOverlap(a, b)` — the file-overlap co-dispatch guard: true
|
|
@@ -156,15 +156,37 @@ export function storyFootprint(story) {
|
|
|
156
156
|
return out;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Does a declared path contain a glob metacharacter? Mirrors the detection
|
|
161
|
+
* in `story-body.js#extractChangePaths`, whose `isGlob` flag documents an
|
|
162
|
+
* "unknown-width footprint" policy that was never implemented downstream.
|
|
163
|
+
*
|
|
164
|
+
* @param {string} path
|
|
165
|
+
* @returns {boolean}
|
|
166
|
+
*/
|
|
167
|
+
function isGlobPath(path) {
|
|
168
|
+
return path.includes('*') || path.includes('?') || path.includes('{');
|
|
169
|
+
}
|
|
170
|
+
|
|
159
171
|
/**
|
|
160
172
|
* File-overlap co-dispatch guard. Returns `true` when two Stories' declared
|
|
161
|
-
* file footprints intersect
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
173
|
+
* file footprints intersect — meaning they would race the same file if
|
|
174
|
+
* dispatched onto parallel `story-<id>` branches in the same beat. Two
|
|
175
|
+
* Stories that overlap MUST NOT both appear in one dispatch set; one is
|
|
176
|
+
* withheld until the other clears.
|
|
165
177
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
178
|
+
* Two deliberate asymmetries:
|
|
179
|
+
*
|
|
180
|
+
* - **An empty footprint means "no known overlap"** → `false`. A Story that
|
|
181
|
+
* declares no files is never withheld. This is permissive by necessity:
|
|
182
|
+
* an undeclared footprint carries no information, and withholding on
|
|
183
|
+
* absence would serialize every run.
|
|
184
|
+
* - **A glob footprint overlaps EVERYTHING** → `true` (Story #4539/#4540).
|
|
185
|
+
* Comparison is exact-string, so a Story declaring
|
|
186
|
+
* `.agents/scripts/lib/**` would not match another declaring
|
|
187
|
+
* `.agents/scripts/lib/story-adjacency.js` — the guard would silently
|
|
188
|
+
* pass two Stories that genuinely race. Unknown width is not the same as
|
|
189
|
+
* no width: fail safe by serializing.
|
|
168
190
|
*
|
|
169
191
|
* @param {StoryRecord} a
|
|
170
192
|
* @param {StoryRecord} b
|
|
@@ -176,8 +198,12 @@ export function storiesOverlap(a, b) {
|
|
|
176
198
|
const fb = storyFootprint(b);
|
|
177
199
|
if (fb.size === 0) return false;
|
|
178
200
|
for (const path of fa) {
|
|
201
|
+
if (isGlobPath(path)) return true;
|
|
179
202
|
if (fb.has(path)) return true;
|
|
180
203
|
}
|
|
204
|
+
for (const path of fb) {
|
|
205
|
+
if (isGlobPath(path)) return true;
|
|
206
|
+
}
|
|
181
207
|
return false;
|
|
182
208
|
}
|
|
183
209
|
|
|
@@ -145,7 +145,7 @@ export function provision({
|
|
|
145
145
|
/**
|
|
146
146
|
* Throw if any required workspace file is missing from `worktree`. Used as a
|
|
147
147
|
* runtime guard (called right after every worktree create in
|
|
148
|
-
* `story-init.js`) and as a test oracle.
|
|
148
|
+
* `single-story-init.js`) and as a test oracle.
|
|
149
149
|
*
|
|
150
150
|
* When `sourceRoot` is supplied, the thrown error carries the missing target
|
|
151
151
|
* path and an exact `cp`/`copy` command the operator can paste to remediate.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* on a Windows lock-class failure, the entry is appended to
|
|
8
8
|
* `.worktrees/.pending-cleanup.json`. The plan-time `worktree-sweep.js`
|
|
9
9
|
* reader (shipped in Epic #349) picks up the manifest on the next
|
|
10
|
-
* `/
|
|
10
|
+
* `/plan` run and retries removal
|
|
11
11
|
* (`git worktree remove` then `fs.rm`) — by then the live file handles from Node / AV / the Windows
|
|
12
12
|
* Search indexer are almost always gone. If `MAX_SWEEP_ATTEMPTS` elapses
|
|
13
13
|
* without clearing, an `OPERATOR ACTION REQUIRED: persistent-lock` line
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import fs from 'node:fs';
|
|
17
17
|
import { rm as fsPromisesRm } from 'node:fs/promises';
|
|
18
|
-
import { isInsideWorktree, samePath
|
|
18
|
+
import { isInsideWorktree, samePath } from '../inspector.js';
|
|
19
19
|
import { sleepSync } from '../node-modules-strategy.js';
|
|
20
20
|
import { checkMergeReachability } from './merge-reachability.js';
|
|
21
21
|
import { recordPendingCleanup } from './pending-cleanup.js';
|
|
@@ -52,14 +52,12 @@ const WINDOWS_CWD_RE =
|
|
|
52
52
|
* proves the Story branch was integrated even though the current HEAD
|
|
53
53
|
* has diverged, so the worktree is still safe to reap.
|
|
54
54
|
*
|
|
55
|
-
* `opts.
|
|
56
|
-
*
|
|
57
|
-
* sites that thread `{ epicBranch }` through `reap()` keep working until
|
|
58
|
-
* they migrate.
|
|
55
|
+
* `opts.epicBranch` is the integration / base ref the Story must already
|
|
56
|
+
* be merged into (e.g. `main` or a plan-run branch).
|
|
59
57
|
*
|
|
60
58
|
* @param {object} ctx
|
|
61
59
|
* @param {string} wtPath
|
|
62
|
-
* @param {{
|
|
60
|
+
* @param {{ epicBranch?: string|null }} [opts]
|
|
63
61
|
* @returns {Promise<{ safe: boolean, reason?: string }>}
|
|
64
62
|
*/
|
|
65
63
|
export async function isSafeToRemove(ctx, wtPath, opts = {}) {
|
|
@@ -67,28 +65,65 @@ export async function isSafeToRemove(ctx, wtPath, opts = {}) {
|
|
|
67
65
|
if (!local.safe) return local;
|
|
68
66
|
if (local.reason === 'path-missing') return local;
|
|
69
67
|
|
|
70
|
-
const epicRef = opts.
|
|
68
|
+
const epicRef = opts.epicBranch ?? opts.epicRef ?? null;
|
|
71
69
|
if (!epicRef) return { safe: true };
|
|
72
70
|
|
|
73
71
|
return checkMergeReachability(ctx, wtPath, local.branch, epicRef);
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
/**
|
|
77
|
-
* Returns true iff `branch` is already
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
75
|
+
* Returns true iff `branch`'s work is demonstrably already integrated into
|
|
76
|
+
* `baseRef`. Used only to license discarding a **dirty** tree, so it must
|
|
77
|
+
* err toward `false`.
|
|
78
|
+
*
|
|
79
|
+
* Two-phase:
|
|
80
|
+
* 1. `merge-base --is-ancestor` — cheap SHA reachability; true for
|
|
81
|
+
* fast-forward and merge-commit integration.
|
|
82
|
+
* 2. `git cherry <baseRef> <branch>` — compares **patch-ids** rather than
|
|
83
|
+
* SHAs, marking a commit `-` when an equivalent change already exists
|
|
84
|
+
* upstream. All-`-` (or empty) means every commit on the branch is
|
|
85
|
+
* present in the base under some SHA.
|
|
86
|
+
*
|
|
87
|
+
* **Known limit — this is not a general squash detector.** A squash collapses
|
|
88
|
+
* N commits into ONE new commit whose patch equals their *combined* diff, so
|
|
89
|
+
* no individual original commit has an upstream patch-id equivalent and
|
|
90
|
+
* `git cherry` marks them all `+`. Phase 2 therefore only recognises a
|
|
91
|
+
* squash-landed branch when the branch had a **single** commit (verified
|
|
92
|
+
* empirically, Story #4539). A multi-commit squash still reads as unmerged
|
|
93
|
+
* and the dirty tree is refused — the safe direction, and the same answer
|
|
94
|
+
* the previous ancestor-only check gave. Detecting the general case needs
|
|
95
|
+
* the PR's merged state, which this module has no client for.
|
|
96
|
+
*
|
|
97
|
+
* A missing ref or a git failure yields false, so callers default to the
|
|
98
|
+
* safe, non-forcing behavior. Module-private: `ensureSafeOrForceDiscard` is
|
|
99
|
+
* its only caller, and the symbol it replaced was exported with no consumer
|
|
100
|
+
* and then baselined as a dead export — repeating that would just hide a
|
|
101
|
+
* new corpse.
|
|
102
|
+
*
|
|
103
|
+
* @param {object} ctx
|
|
104
|
+
* @param {string} branch
|
|
105
|
+
* @param {string} baseRef
|
|
106
|
+
* @returns {boolean}
|
|
81
107
|
*/
|
|
82
|
-
|
|
83
|
-
if (!branch || !
|
|
84
|
-
const
|
|
108
|
+
function isBranchMergedIntoBase(ctx, branch, baseRef) {
|
|
109
|
+
if (!branch || !baseRef) return false;
|
|
110
|
+
const ancestor = ctx.git.gitSpawn(
|
|
85
111
|
ctx.repoRoot,
|
|
86
112
|
'merge-base',
|
|
87
113
|
'--is-ancestor',
|
|
88
114
|
branch,
|
|
89
|
-
|
|
115
|
+
baseRef,
|
|
90
116
|
);
|
|
91
|
-
|
|
117
|
+
if (ancestor.status === 0) return true;
|
|
118
|
+
|
|
119
|
+
const cherry = ctx.git.gitSpawn(ctx.repoRoot, 'cherry', baseRef, branch);
|
|
120
|
+
if (cherry.status !== 0) return false;
|
|
121
|
+
const lines = (cherry.stdout ?? '')
|
|
122
|
+
.split('\n')
|
|
123
|
+
.map((l) => l.trim())
|
|
124
|
+
.filter(Boolean);
|
|
125
|
+
// Every line starting with '+' is a commit with no upstream equivalent.
|
|
126
|
+
return lines.every((line) => line.startsWith('-'));
|
|
92
127
|
}
|
|
93
128
|
|
|
94
129
|
/**
|
|
@@ -472,28 +507,40 @@ function checkReapPreconditions(ctx, _storyId, opts, wtPath) {
|
|
|
472
507
|
ok: false,
|
|
473
508
|
result: { removed: false, reason: 'not-a-worktree', path: wtPath },
|
|
474
509
|
};
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
510
|
+
// Story #4539 removed an `epic-branch-required` gate here: a
|
|
511
|
+
// `story-<id>` worktree used to be unreapable unless the caller supplied
|
|
512
|
+
// an Epic integration branch. v2 has no Epic branch, and the only v2
|
|
513
|
+
// caller (the close path) never passed one — so EVERY close silently
|
|
514
|
+
// failed to reap while reporting success, and cleanup fell to the next
|
|
515
|
+
// boot-sweep.
|
|
516
|
+
//
|
|
517
|
+
// Nothing is lost by dropping it. The close path reaps AFTER pushing
|
|
518
|
+
// `story-<id>` to origin and opening the PR, so the work is durable
|
|
519
|
+
// off-machine; and the real safety net is unchanged — `isSafeToRemove`
|
|
520
|
+
// still refuses a dirty tree (`uncommitted-changes`), which is what
|
|
521
|
+
// actually protects unsaved work.
|
|
481
522
|
return { ok: true };
|
|
482
523
|
}
|
|
483
524
|
|
|
484
525
|
async function ensureSafeOrForceDiscard(ctx, storyId, wtPath, opts) {
|
|
526
|
+
const baseRef = opts.baseRef ?? opts.epicBranch ?? null;
|
|
485
527
|
const safety = await isSafeToRemove(ctx, wtPath, {
|
|
486
|
-
|
|
528
|
+
epicBranch: baseRef,
|
|
487
529
|
});
|
|
488
530
|
if (safety.safe) return { ok: true, discardedPaths: null };
|
|
489
531
|
|
|
490
532
|
const discardAfterMerge = opts.discardAfterMerge !== false;
|
|
491
533
|
const branchName = `story-${validateStoryId(storyId)}`;
|
|
534
|
+
// Discarding a dirty tree is only permissible when the branch's work is
|
|
535
|
+
// demonstrably already integrated. See `isBranchMergedIntoBase` for what
|
|
536
|
+
// that can and cannot prove — notably a multi-commit squash reads as
|
|
537
|
+
// unmerged, so the discard is refused and the tree survives. Refusing is
|
|
538
|
+
// the safe direction: the cost is a stale worktree, not lost work.
|
|
492
539
|
const canForceReap =
|
|
493
540
|
discardAfterMerge &&
|
|
494
541
|
safety.reason === 'uncommitted-changes' &&
|
|
495
|
-
|
|
496
|
-
|
|
542
|
+
baseRef &&
|
|
543
|
+
isBranchMergedIntoBase(ctx, branchName, baseRef);
|
|
497
544
|
if (!canForceReap) {
|
|
498
545
|
ctx.logger.warn(
|
|
499
546
|
`reap-skipped storyId=${storyId} reason=${safety.reason} path=${wtPath}`,
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - `creation.js` — `ensure`
|
|
8
8
|
* - `registry-sync.js` — `pathFor`, `list`, `prune`, `getWorktreeList`,
|
|
9
9
|
* `invalidateWorktreeCache`, `findByPath`
|
|
10
|
-
* - `reap.js` — `isSafeToRemove`,
|
|
10
|
+
* - `reap.js` — `isSafeToRemove`,
|
|
11
11
|
* `removeWorktreeWithRecovery`, `reap`
|
|
12
12
|
* - `gc.js` — `gc`
|
|
13
13
|
* - `drift-detection.js` — `sweepStaleLocks`
|
|
@@ -26,7 +26,6 @@ export { sweepStaleLocks } from './lifecycle/drift-detection.js';
|
|
|
26
26
|
export { gc } from './lifecycle/gc.js';
|
|
27
27
|
export {
|
|
28
28
|
isSafeToRemove,
|
|
29
|
-
isStoryAlreadyMergedIntoEpic,
|
|
30
29
|
reap,
|
|
31
30
|
removeWorktreeWithRecovery,
|
|
32
31
|
} from './lifecycle/reap.js';
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Issue-body conformance lint (Story #4227).
|
|
5
5
|
*
|
|
6
6
|
* Runs the canonical `story-body.parse()` against a human-opened
|
|
7
|
-
* `type::story`
|
|
7
|
+
* `type::story` issue body and reports whether the body
|
|
8
8
|
* round-trips. This is the drift guard between the generated GitHub Issue
|
|
9
9
|
* Forms (`lib/bootstrap/issue-forms-template.js`) and the parser: if a
|
|
10
10
|
* human files a ticket whose body `parse()` rejects (or which lacks the
|
|
@@ -114,12 +114,12 @@ export function evaluateIssueBody(body) {
|
|
|
114
114
|
|
|
115
115
|
const problems = [];
|
|
116
116
|
|
|
117
|
-
//
|
|
117
|
+
// An unstructured body parses but carries no structured sections — that
|
|
118
118
|
// is exactly the human-filed shape this lint exists to catch.
|
|
119
|
-
if (result.info.
|
|
119
|
+
if (result.info.isUnstructuredBody) {
|
|
120
120
|
problems.push(
|
|
121
121
|
'The body has no recognised `## Goal` / `## Acceptance` / `## Verify` sections. ' +
|
|
122
|
-
'File via the Story
|
|
122
|
+
'File via the Story issue form so it round-trips through the parser.',
|
|
123
123
|
);
|
|
124
124
|
}
|
|
125
125
|
|
|
@@ -162,7 +162,7 @@ export function renderConformanceComment(verdict) {
|
|
|
162
162
|
'',
|
|
163
163
|
...verdict.problems.map((p) => `- ${p}`),
|
|
164
164
|
'',
|
|
165
|
-
'The quickest fix is to refile using the **Story**
|
|
165
|
+
'The quickest fix is to refile using the **Story** issue form ' +
|
|
166
166
|
'(New issue → pick the template), which lays out the required sections.',
|
|
167
167
|
];
|
|
168
168
|
if (verdict.warnings.length > 0) {
|
|
@@ -196,32 +196,40 @@ function gh(args) {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
*
|
|
200
|
-
* `ISSUE_NUMBER` env), fetches its body + labels, and — when the body is
|
|
201
|
-
* non-conformant — upserts a single marker comment. Always exits 0 (the
|
|
202
|
-
* lint *informs*, it does not block), unless an unexpected I/O error occurs.
|
|
199
|
+
* Stage 5: only `type::story` tickets are in scope (type::epic retired).
|
|
203
200
|
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
* --repo <o/r> owner/repo (defaults to env GITHUB_REPOSITORY).
|
|
207
|
-
* --dry-run Evaluate + print the verdict, never touch GitHub.
|
|
201
|
+
* @param {string[]} labelNames
|
|
202
|
+
* @returns {boolean}
|
|
208
203
|
*/
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
const i = args.indexOf(flag);
|
|
213
|
-
return i >= 0 ? args[i + 1] : undefined;
|
|
214
|
-
};
|
|
215
|
-
const dryRun = args.includes('--dry-run');
|
|
216
|
-
const issue = get('--issue') ?? process.env.ISSUE_NUMBER;
|
|
217
|
-
const repo = get('--repo') ?? process.env.GITHUB_REPOSITORY;
|
|
218
|
-
|
|
219
|
-
if (!issue) {
|
|
220
|
-
throw new Error('lint-issue-body: --issue <n> or ISSUE_NUMBER is required');
|
|
221
|
-
}
|
|
204
|
+
export function isStoryTicket(labelNames) {
|
|
205
|
+
return Array.isArray(labelNames) && labelNames.includes('type::story');
|
|
206
|
+
}
|
|
222
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Evaluate an issue payload and optionally post a conformance comment.
|
|
210
|
+
* Pure with respect to GitHub when `ghFn` is injected — the CLI `main`
|
|
211
|
+
* is a thin argv wrapper around this helper.
|
|
212
|
+
*
|
|
213
|
+
* @param {{
|
|
214
|
+
* issue: string|number,
|
|
215
|
+
* repo?: string,
|
|
216
|
+
* dryRun?: boolean,
|
|
217
|
+
* ghFn?: (args: string[]) => string,
|
|
218
|
+
* write?: (s: string) => void,
|
|
219
|
+
* }} opts
|
|
220
|
+
* @returns {{ skipped?: string, conformant?: boolean, problems?: string[] }}
|
|
221
|
+
*/
|
|
222
|
+
export function runLintIssueBody({
|
|
223
|
+
issue,
|
|
224
|
+
repo,
|
|
225
|
+
dryRun = false,
|
|
226
|
+
ghFn = gh,
|
|
227
|
+
write = (s) => {
|
|
228
|
+
process.stdout.write(s);
|
|
229
|
+
},
|
|
230
|
+
}) {
|
|
223
231
|
const repoArgs = repo ? ['--repo', repo] : [];
|
|
224
|
-
const raw =
|
|
232
|
+
const raw = ghFn([
|
|
225
233
|
'issue',
|
|
226
234
|
'view',
|
|
227
235
|
String(issue),
|
|
@@ -231,20 +239,14 @@ async function main() {
|
|
|
231
239
|
]);
|
|
232
240
|
const { body, labels } = JSON.parse(raw);
|
|
233
241
|
const labelNames = (labels ?? []).map((l) => l.name);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
// Machine-parsable JSON envelope → process.stdout.write (not console.log),
|
|
239
|
-
// per the .agents/scripts logging contract (tests/enforcement/no-console).
|
|
240
|
-
process.stdout.write(
|
|
241
|
-
`${JSON.stringify({ issue: Number(issue), skipped: 'not-a-ticket' })}\n`,
|
|
242
|
-
);
|
|
243
|
-
return;
|
|
242
|
+
if (!isStoryTicket(labelNames)) {
|
|
243
|
+
const envelope = { issue: Number(issue), skipped: 'not-a-ticket' };
|
|
244
|
+
write(`${JSON.stringify(envelope)}\n`);
|
|
245
|
+
return envelope;
|
|
244
246
|
}
|
|
245
247
|
|
|
246
248
|
const verdict = evaluateIssueBody(body ?? '');
|
|
247
|
-
|
|
249
|
+
write(
|
|
248
250
|
`${JSON.stringify({
|
|
249
251
|
issue: Number(issue),
|
|
250
252
|
conformant: verdict.conformant,
|
|
@@ -252,10 +254,39 @@ async function main() {
|
|
|
252
254
|
})}\n`,
|
|
253
255
|
);
|
|
254
256
|
|
|
255
|
-
if (verdict.conformant || dryRun)
|
|
257
|
+
if (verdict.conformant || dryRun) {
|
|
258
|
+
return {
|
|
259
|
+
conformant: verdict.conformant,
|
|
260
|
+
problems: verdict.problems,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
256
263
|
|
|
257
264
|
const comment = renderConformanceComment(verdict);
|
|
258
|
-
|
|
265
|
+
ghFn(['issue', 'comment', String(issue), ...repoArgs, '--body', comment]);
|
|
266
|
+
return { conformant: false, problems: verdict.problems };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* CLI entry. Reads the target issue (number from `--issue` or the
|
|
271
|
+
* `ISSUE_NUMBER` env), fetches its body + labels, and — when the body is
|
|
272
|
+
* non-conformant — upserts a single marker comment. Always exits 0 (the
|
|
273
|
+
* lint *informs*, it does not block), unless an unexpected I/O error occurs.
|
|
274
|
+
*/
|
|
275
|
+
async function main() {
|
|
276
|
+
const args = process.argv.slice(2);
|
|
277
|
+
const get = (flag) => {
|
|
278
|
+
const i = args.indexOf(flag);
|
|
279
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
280
|
+
};
|
|
281
|
+
const issue = get('--issue') ?? process.env.ISSUE_NUMBER;
|
|
282
|
+
if (!issue) {
|
|
283
|
+
throw new Error('lint-issue-body: --issue <n> or ISSUE_NUMBER is required');
|
|
284
|
+
}
|
|
285
|
+
runLintIssueBody({
|
|
286
|
+
issue,
|
|
287
|
+
repo: get('--repo') ?? process.env.GITHUB_REPOSITORY,
|
|
288
|
+
dryRun: args.includes('--dry-run'),
|
|
289
|
+
});
|
|
259
290
|
}
|
|
260
291
|
|
|
261
292
|
runAsCli(import.meta.url, main, { source: 'lint-issue-body' });
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* friendlier "you're offline" signal before any version probe.
|
|
26
26
|
*
|
|
27
27
|
* Severity is consistent with framework preflight conventions
|
|
28
|
-
* (cf. `story-close.js` runStoryClosePreflight, `
|
|
28
|
+
* (cf. `single-story-close.js` runStoryClosePreflight, `mandrel-update-preflight.js`):
|
|
29
29
|
* the consumer-shape check is a hard stop (blocker), dirty-index and offline
|
|
30
30
|
* are warn-only and never block the run.
|
|
31
31
|
*
|