mandrel 1.58.0 → 1.60.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 +100 -98
- package/.agents/docs/SDLC.md +140 -141
- package/.agents/docs/configuration.md +16 -16
- package/.agents/docs/workflows.md +7 -8
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/audit-rules.json +20 -0
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +1 -1
- package/.agents/scripts/acceptance-eval.js +21 -4
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/assert-branch.js +1 -3
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/bootstrap.js +1 -1
- package/.agents/scripts/check-arch-cycles.js +360 -0
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/coverage-capture.js +24 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +11 -9
- package/.agents/scripts/epic-deliver-prepare.js +13 -5
- package/.agents/scripts/epic-execute-record-wave.js +5 -5
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +2 -2
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/git-rebase-and-resolve.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +40 -24
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
- package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
- package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
- package/.agents/scripts/lib/baselines/writer.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/close-validation/commands.js +188 -0
- package/.agents/scripts/lib/close-validation/gates.js +235 -0
- package/.agents/scripts/lib/close-validation/process.js +101 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
- package/.agents/scripts/lib/close-validation/runner.js +325 -0
- package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/quality.js +6 -6
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-resolver.js +2 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/coverage-capture.js +147 -4
- package/.agents/scripts/lib/cpu-pool.js +14 -0
- package/.agents/scripts/lib/crap-utils.js +6 -11
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/git-utils.js +24 -22
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +3 -8
- package/.agents/scripts/lib/maintainability-engine.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +4 -187
- package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
- package/.agents/scripts/lib/orchestration/code-review.js +95 -82
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
- package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
- package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
- package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
- package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/project-root.js +17 -0
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-adjacency.js +76 -0
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +9 -9
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/transpile.js +93 -0
- package/.agents/scripts/lib/wave-runner/tick.js +4 -153
- package/.agents/scripts/lib/workers/crap-worker.js +1 -1
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +114 -10
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +10 -1
- package/.agents/scripts/run-tests.js +24 -4
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +13 -10
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +162 -26
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +2 -2
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +2 -2
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +226 -0
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +12 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/onboard.md +21 -20
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +17 -20
- package/docs/CHANGELOG.md +1149 -0
- package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
- package/lib/cli/__tests__/update-reexec.test.js +513 -0
- package/lib/cli/init.js +338 -0
- package/lib/cli/update.js +413 -52
- package/package.json +3 -1
- package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
- package/.agents/scripts/lib/close-validation.js +0 -897
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
- package/.agents/scripts/lib/task-utils.js +0 -26
- package/.agents/scripts/story-deliver-prepare.js +0 -267
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* review-providers/parse-findings.js — shared JSON-findings parser.
|
|
3
|
+
*
|
|
4
|
+
* Story #3981 — extracts the verbatim-duplicated parsing logic from
|
|
5
|
+
* `parseCodexFindings` (codex.js) and `parseSecurityReviewFindings`
|
|
6
|
+
* (security-review.js) into one templated parser. Both adapters emit
|
|
7
|
+
* JSON; the parser is liberal in what it accepts:
|
|
8
|
+
* - A bare array of finding objects.
|
|
9
|
+
* - An object with a `findings` array.
|
|
10
|
+
* - Either shape wrapped in an outer envelope with a `result` or
|
|
11
|
+
* `data` key (covers minor wire-format drift across versions
|
|
12
|
+
* without re-shimming).
|
|
13
|
+
*
|
|
14
|
+
* Each entry's severity is funnelled through the caller-supplied
|
|
15
|
+
* `mapSeverity` so the canonical enum is the only thing that reaches
|
|
16
|
+
* the renderer. Entries without a `title` or `body` are skipped — the
|
|
17
|
+
* orchestrator cannot post an empty finding, and silently dropping the
|
|
18
|
+
* entry is safer than fabricating one.
|
|
19
|
+
*
|
|
20
|
+
* Per-provider deltas ride in as options:
|
|
21
|
+
* - `errorPrefix` — prefix for the JSON-parse failure message.
|
|
22
|
+
* - `mapSeverity` — provider severity vocabulary → canonical enum.
|
|
23
|
+
* - `defaultCategory` — when set, entries missing a `category` get
|
|
24
|
+
* this value (security-review defaults to `'security'`); when
|
|
25
|
+
* omitted, `category` is only set when present (codex behavior).
|
|
26
|
+
*
|
|
27
|
+
* @typedef {import('./types.js').Finding} Finding
|
|
28
|
+
* @typedef {import('./types.js').Severity} Severity
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Parse a provider's raw stdout into `Finding[]`.
|
|
33
|
+
*
|
|
34
|
+
* @param {string} rawStdout
|
|
35
|
+
* @param {{
|
|
36
|
+
* errorPrefix: string,
|
|
37
|
+
* mapSeverity: (raw: unknown) => Severity,
|
|
38
|
+
* defaultCategory?: string,
|
|
39
|
+
* }} options
|
|
40
|
+
* @returns {Finding[]}
|
|
41
|
+
* @throws {Error} when stdout is not parseable JSON.
|
|
42
|
+
*/
|
|
43
|
+
export function parseProviderFindings(rawStdout, options) {
|
|
44
|
+
const { errorPrefix, mapSeverity, defaultCategory } = options;
|
|
45
|
+
const text = (rawStdout ?? '').trim();
|
|
46
|
+
if (text.length === 0) return [];
|
|
47
|
+
|
|
48
|
+
let parsed;
|
|
49
|
+
try {
|
|
50
|
+
parsed = JSON.parse(text);
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw new Error(`${errorPrefix}: ${err?.message ?? err}`);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Unwrap a single layer of envelope when present.
|
|
56
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
57
|
+
if (Array.isArray(parsed.findings)) parsed = parsed.findings;
|
|
58
|
+
else if (parsed.result !== undefined) parsed = parsed.result;
|
|
59
|
+
else if (parsed.data !== undefined) parsed = parsed.data;
|
|
60
|
+
}
|
|
61
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
62
|
+
if (Array.isArray(parsed.findings)) parsed = parsed.findings;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!Array.isArray(parsed)) return [];
|
|
66
|
+
|
|
67
|
+
/** @type {Finding[]} */
|
|
68
|
+
const findings = [];
|
|
69
|
+
for (const entry of parsed) {
|
|
70
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
71
|
+
const title =
|
|
72
|
+
typeof entry.title === 'string' && entry.title.trim().length > 0
|
|
73
|
+
? entry.title.trim()
|
|
74
|
+
: null;
|
|
75
|
+
const body =
|
|
76
|
+
typeof entry.body === 'string' && entry.body.trim().length > 0
|
|
77
|
+
? entry.body
|
|
78
|
+
: typeof entry.message === 'string' && entry.message.trim().length > 0
|
|
79
|
+
? entry.message
|
|
80
|
+
: null;
|
|
81
|
+
if (!title || !body) continue;
|
|
82
|
+
|
|
83
|
+
/** @type {Finding} */
|
|
84
|
+
const finding = {
|
|
85
|
+
severity: mapSeverity(entry.severity),
|
|
86
|
+
title,
|
|
87
|
+
body,
|
|
88
|
+
};
|
|
89
|
+
const category =
|
|
90
|
+
typeof entry.category === 'string' && entry.category.length > 0
|
|
91
|
+
? entry.category
|
|
92
|
+
: defaultCategory;
|
|
93
|
+
if (category !== undefined) {
|
|
94
|
+
finding.category = category;
|
|
95
|
+
}
|
|
96
|
+
if (typeof entry.file === 'string' && entry.file.length > 0) {
|
|
97
|
+
finding.file = entry.file;
|
|
98
|
+
}
|
|
99
|
+
if (Number.isInteger(entry.line) && entry.line > 0) {
|
|
100
|
+
finding.line = entry.line;
|
|
101
|
+
}
|
|
102
|
+
findings.push(finding);
|
|
103
|
+
}
|
|
104
|
+
return findings;
|
|
105
|
+
}
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
*/
|
|
33
33
|
|
|
34
34
|
import { spawnSync } from 'node:child_process';
|
|
35
|
+
import { parseProviderFindings } from './parse-findings.js';
|
|
35
36
|
import { renderDepthDirective } from './review-depth.js';
|
|
36
37
|
|
|
37
38
|
/**
|
|
@@ -138,65 +139,12 @@ export function mapSecurityReviewSeverity(raw) {
|
|
|
138
139
|
* @throws {Error} when stdout is not parseable JSON.
|
|
139
140
|
*/
|
|
140
141
|
export function parseSecurityReviewFindings(rawStdout) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
} catch (err) {
|
|
148
|
-
throw new Error(
|
|
149
|
-
`[security-review] Failed to parse /security-review stdout as JSON: ${
|
|
150
|
-
err?.message ?? err
|
|
151
|
-
}`,
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
156
|
-
if (Array.isArray(parsed.findings)) parsed = parsed.findings;
|
|
157
|
-
else if (parsed.result !== undefined) parsed = parsed.result;
|
|
158
|
-
else if (parsed.data !== undefined) parsed = parsed.data;
|
|
159
|
-
}
|
|
160
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
161
|
-
if (Array.isArray(parsed.findings)) parsed = parsed.findings;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!Array.isArray(parsed)) return [];
|
|
165
|
-
|
|
166
|
-
/** @type {Finding[]} */
|
|
167
|
-
const findings = [];
|
|
168
|
-
for (const entry of parsed) {
|
|
169
|
-
if (!entry || typeof entry !== 'object') continue;
|
|
170
|
-
const title =
|
|
171
|
-
typeof entry.title === 'string' && entry.title.trim().length > 0
|
|
172
|
-
? entry.title.trim()
|
|
173
|
-
: null;
|
|
174
|
-
const body =
|
|
175
|
-
typeof entry.body === 'string' && entry.body.trim().length > 0
|
|
176
|
-
? entry.body
|
|
177
|
-
: typeof entry.message === 'string' && entry.message.trim().length > 0
|
|
178
|
-
? entry.message
|
|
179
|
-
: null;
|
|
180
|
-
if (!title || !body) continue;
|
|
181
|
-
/** @type {Finding} */
|
|
182
|
-
const finding = {
|
|
183
|
-
severity: mapSecurityReviewSeverity(entry.severity),
|
|
184
|
-
title,
|
|
185
|
-
body,
|
|
186
|
-
category:
|
|
187
|
-
typeof entry.category === 'string' && entry.category.length > 0
|
|
188
|
-
? entry.category
|
|
189
|
-
: 'security',
|
|
190
|
-
};
|
|
191
|
-
if (typeof entry.file === 'string' && entry.file.length > 0) {
|
|
192
|
-
finding.file = entry.file;
|
|
193
|
-
}
|
|
194
|
-
if (Number.isInteger(entry.line) && entry.line > 0) {
|
|
195
|
-
finding.line = entry.line;
|
|
196
|
-
}
|
|
197
|
-
findings.push(finding);
|
|
198
|
-
}
|
|
199
|
-
return findings;
|
|
142
|
+
return parseProviderFindings(rawStdout, {
|
|
143
|
+
errorPrefix:
|
|
144
|
+
'[security-review] Failed to parse /security-review stdout as JSON',
|
|
145
|
+
mapSeverity: mapSecurityReviewSeverity,
|
|
146
|
+
defaultCategory: 'security',
|
|
147
|
+
});
|
|
200
148
|
}
|
|
201
149
|
|
|
202
150
|
/**
|
|
@@ -22,10 +22,8 @@
|
|
|
22
22
|
* that mock the upstream module URLs.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
runCloseValidation as defaultRunCloseValidation,
|
|
28
|
-
} from '../../../close-validation.js';
|
|
25
|
+
import { buildDefaultGates as defaultBuildDefaultGates } from '../../../close-validation/gates.js';
|
|
26
|
+
import { runCloseValidation as defaultRunCloseValidation } from '../../../close-validation/runner.js';
|
|
29
27
|
import { Logger } from '../../../Logger.js';
|
|
30
28
|
|
|
31
29
|
/**
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import { parseSprintArgs } from '../../../cli-args.js';
|
|
13
|
-
import { PROJECT_ROOT } from '../../../
|
|
13
|
+
import { PROJECT_ROOT } from '../../../project-root.js';
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* Resolve a flag value from an explicit override, a parsed CLI arg, or a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* phases/wrong-tree-guard.js — detect worktree/main-checkout edit divergence.
|
|
3
3
|
*
|
|
4
|
-
* Story #3364 — `/single-story-deliver` (and `/
|
|
4
|
+
* Story #3364 — `/single-story-deliver` (and `/deliver`) materializes a
|
|
5
5
|
* per-Story worktree and instructs the agent to `cd` into it before editing.
|
|
6
6
|
* On Windows that guidance is silently insufficient: `cd <workCwd>` steers the
|
|
7
7
|
* Bash tool's working directory, but the path-based Edit/Write tools operate on
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import nodeFs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
runCloseValidation,
|
|
6
|
-
} from '../../close-validation.js';
|
|
3
|
+
import { buildDefaultGates } from '../../close-validation/gates.js';
|
|
4
|
+
import { runCloseValidation } from '../../close-validation/runner.js';
|
|
7
5
|
import { resolveConfig } from '../../config-resolver.js';
|
|
8
6
|
import { getStoryBranch, gitSync } from '../../git-utils.js';
|
|
9
7
|
import { Logger } from '../../Logger.js';
|
|
@@ -39,16 +39,20 @@
|
|
|
39
39
|
*/
|
|
40
40
|
|
|
41
41
|
import {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
} from './ticket-lease.js';
|
|
42
|
+
acquireLeaseFailClosed,
|
|
43
|
+
resolveOperatorFromCandidates,
|
|
44
|
+
} from './lease-guard-shared.js';
|
|
45
|
+
import { releaseLease } from './ticket-lease.js';
|
|
46
46
|
|
|
47
47
|
/**
|
|
48
48
|
* Resolve the operator handle used as the lease owner from resolved config.
|
|
49
|
-
* Routes through the shared
|
|
49
|
+
* Routes through the shared lease-guard kernel
|
|
50
|
+
* (`lease-guard-shared.resolveOperatorFromCandidates`) so a leading `@` is
|
|
50
51
|
* stripped (the assignees API expects bare logins, not `@`-prefixed mentions)
|
|
51
|
-
* and the self-held-claim comparison matches.
|
|
52
|
+
* and the self-held-claim comparison matches. The standalone surface's
|
|
53
|
+
* missing-handle policy is `'throw'` (intentional divergence from the plan
|
|
54
|
+
* path's `'null'`): init has no best-effort leg that can degrade, so an
|
|
55
|
+
* unowned lease must refuse immediately.
|
|
52
56
|
*
|
|
53
57
|
* @param {object} config Resolved `.agentrc.json` config.
|
|
54
58
|
* @returns {string} Bare operator handle.
|
|
@@ -58,17 +62,16 @@ import {
|
|
|
58
62
|
* path cannot safely serialise concurrent runs.
|
|
59
63
|
*/
|
|
60
64
|
export function resolveOperator(config) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
throw
|
|
65
|
+
return resolveOperatorFromCandidates({
|
|
66
|
+
candidates: [config?.github?.operatorHandle],
|
|
67
|
+
missingHandleBehavior: 'throw',
|
|
68
|
+
missingHandleMessage:
|
|
64
69
|
'single-story lease: no operator identity is configured. ' +
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
return handle;
|
|
70
|
+
'github.operatorHandle is unset or still the shipped `@[USERNAME]` ' +
|
|
71
|
+
'placeholder, so the standalone Story lease has no owner. Set your own ' +
|
|
72
|
+
'handle in .agentrc.local.json (e.g. { "github": { "operatorHandle": ' +
|
|
73
|
+
'"@your-login" } }) and re-run.',
|
|
74
|
+
});
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
/**
|
|
@@ -99,31 +102,25 @@ export async function acquireStoryLease({
|
|
|
99
102
|
now,
|
|
100
103
|
}) {
|
|
101
104
|
const owner = operator ?? resolveOperator(config);
|
|
102
|
-
// Fail closed: with no live-heartbeat source on the standalone path,
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
typeof now === 'number' && Number.isFinite(now) ? now : Date.now();
|
|
108
|
-
const result = await acquireLease({
|
|
105
|
+
// Fail closed: with no live-heartbeat source on the standalone path, the
|
|
106
|
+
// shared kernel anchors `heartbeatAt` to the same `now` the primitive
|
|
107
|
+
// evaluates against, so `isClaimLive` returns true for any foreign owner
|
|
108
|
+
// and `acquireLease` refuses unless `steal` is set.
|
|
109
|
+
return acquireLeaseFailClosed({
|
|
109
110
|
provider,
|
|
110
111
|
ticketId: storyId,
|
|
111
112
|
operator: owner,
|
|
112
|
-
heartbeatAt: resolvedNow,
|
|
113
113
|
steal,
|
|
114
114
|
config,
|
|
115
|
-
now
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw new Error(
|
|
115
|
+
now,
|
|
116
|
+
anchorHeartbeatToNow: true,
|
|
117
|
+
renderRefusal: (result) =>
|
|
119
118
|
`single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
return result;
|
|
119
|
+
'Another /single-story-deliver run owns this Story. Coordinate with that ' +
|
|
120
|
+
'operator, or re-run with --steal to forcibly transfer the claim once you ' +
|
|
121
|
+
'have confirmed the other run is dead. (The standalone path has no Epic ' +
|
|
122
|
+
'heartbeat ledger, so a foreign assignee always blocks unless stolen.)',
|
|
123
|
+
});
|
|
127
124
|
}
|
|
128
125
|
|
|
129
126
|
/**
|
|
@@ -12,9 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
import fs from 'node:fs';
|
|
14
14
|
import path from 'node:path';
|
|
15
|
-
|
|
16
|
-
import { PROJECT_ROOT } from '../config-resolver.js';
|
|
17
15
|
import { Logger } from '../Logger.js';
|
|
16
|
+
import { PROJECT_ROOT } from '../project-root.js';
|
|
18
17
|
|
|
19
18
|
const POLICY_HEADING_RE = /^## Policy Capsule\s*$/;
|
|
20
19
|
const ANY_H2_RE = /^## /;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* spec-freshness.js — Tech Spec post-author cross-validation against the
|
|
3
3
|
* current codebase.
|
|
4
4
|
*
|
|
5
|
-
* `/
|
|
5
|
+
* `/plan` Phase 7 authors PRD + Tech Spec from documentation alone.
|
|
6
6
|
* When `project.docsContextFiles` (architecture.md, etc.) drift from
|
|
7
7
|
* the real source tree, the Architect persona happily cites modules and
|
|
8
8
|
* paths that no longer exist. The mismatch only surfaces at delivery time,
|
|
@@ -1,30 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/orchestration/spec-renderer.js — tickets.json → epic-spec.yaml
|
|
3
|
-
* projection (
|
|
3
|
+
* projection (2-tier shape, Story #4041).
|
|
4
4
|
*
|
|
5
5
|
* The decomposer (`epic-plan-decompose-author`) produces a flat array
|
|
6
|
-
* of ticket objects
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `{ epic,
|
|
11
|
-
* `
|
|
6
|
+
* of Story ticket objects keyed by stable `slug`s and linked by
|
|
7
|
+
* `depends_on`. The spec reconciler consumes the declarative
|
|
8
|
+
* structural representation defined by
|
|
9
|
+
* `.agents/schemas/epic-spec.schema.json` — a flat
|
|
10
|
+
* `{ epic, stories: [...] }` shape with Story-level `wave`,
|
|
11
|
+
* `dependsOn`, and inline `acceptance[]` / `verify[]` arrays
|
|
12
12
|
* projected from the decomposer's edges.
|
|
13
13
|
*
|
|
14
|
-
* Under the
|
|
15
|
-
* children — acceptance and verify steps live
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* "Contract Cutovers — No Shim Layer").
|
|
20
|
-
* decomposer input is rejected at index time.
|
|
14
|
+
* Under the 2-tier hierarchy (Story #4041), Stories have no Feature
|
|
15
|
+
* parent and no Task children — acceptance and verify steps live
|
|
16
|
+
* inline on the Story, and Stories attach directly to the Epic. Any
|
|
17
|
+
* `feature` or `task` ticket in the decomposer input is rejected at
|
|
18
|
+
* index time (hard cutover; see `.agents/rules/git-conventions.md` §
|
|
19
|
+
* "Contract Cutovers — No Shim Layer").
|
|
21
20
|
*
|
|
22
21
|
* `renderSpec(tickets, opts)` is the pure projection between those
|
|
23
22
|
* two shapes. It:
|
|
24
23
|
*
|
|
25
|
-
* 1. Indexes the flat array by slug
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* 1. Indexes the flat array by slug. Any unrecognised type
|
|
25
|
+
* (including the historical `feature` / `task`) raises
|
|
26
|
+
* immediately.
|
|
28
27
|
* 2. Filters Story `depends_on` edges down to inter-Story
|
|
29
28
|
* references in the same Epic.
|
|
30
29
|
* 3. Layers Stories into waves via `Graph.assignLayers` (depth in
|
|
@@ -33,9 +32,9 @@
|
|
|
33
32
|
* convention (`build-wave-dag.js` produces the same layering at
|
|
34
33
|
* dispatch time from the live GH state, so the spec's waves are
|
|
35
34
|
* observationally identical to what dispatch will compute).
|
|
36
|
-
* 4. Walks the
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* 4. Walks the Stories in decomposer-declared order, preserving
|
|
36
|
+
* the order the LLM emitted so the reconciler's diff stays
|
|
37
|
+
* human-readable.
|
|
39
38
|
* 5. Strips `agent::*` labels from every entity. The decomposer
|
|
40
39
|
* doesn't normally write them, but they can leak via reverse-
|
|
41
40
|
* bootstrap from live GH state — and the schema forbids them
|
|
@@ -162,7 +161,7 @@ function sanitizeLabels(labels) {
|
|
|
162
161
|
* Convert a decomposer body value into a spec `body` string. The
|
|
163
162
|
* decomposer schema admits two shapes for a Story body:
|
|
164
163
|
*
|
|
165
|
-
* - A short string (preferred under the
|
|
164
|
+
* - A short string (preferred under the 2-tier hierarchy).
|
|
166
165
|
* - A structured object (`{ goal, changes[], acceptance[],
|
|
167
166
|
* verify[] }`) carried over from the legacy 4-tier Task body
|
|
168
167
|
* shape, when the decomposer chooses to inline the Story's
|
|
@@ -215,16 +214,12 @@ function assignNonEmpty(target, key, value) {
|
|
|
215
214
|
}
|
|
216
215
|
|
|
217
216
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* (parents-before-children is guaranteed by the validator's
|
|
222
|
-
* topological sort, but we don't need that invariant here — we walk
|
|
223
|
-
* features in array order, then look up their children by parent_slug
|
|
224
|
-
* in the order the decomposer emitted them).
|
|
217
|
+
* Index the flat ticket array by slug. Returns the ordered Story slug
|
|
218
|
+
* list alongside the lookup map so the renderer can walk Stories in
|
|
219
|
+
* the decomposer's emit order.
|
|
225
220
|
*
|
|
226
|
-
* Under the
|
|
227
|
-
*
|
|
221
|
+
* Under the 2-tier hierarchy (Story #4041), only the `story` type is
|
|
222
|
+
* recognised. Any other type — including the historical `feature` /
|
|
228
223
|
* `task` — falls through to the unknown-type guard and raises
|
|
229
224
|
* immediately; there is no silent drop.
|
|
230
225
|
*
|
|
@@ -232,7 +227,6 @@ function assignNonEmpty(target, key, value) {
|
|
|
232
227
|
*/
|
|
233
228
|
function indexTickets(tickets) {
|
|
234
229
|
const bySlug = new Map();
|
|
235
|
-
const featureSlugs = [];
|
|
236
230
|
const storySlugs = [];
|
|
237
231
|
|
|
238
232
|
for (const t of tickets) {
|
|
@@ -247,15 +241,14 @@ function indexTickets(tickets) {
|
|
|
247
241
|
throw new Error(`[spec-renderer] duplicate slug "${slug}"`);
|
|
248
242
|
}
|
|
249
243
|
bySlug.set(slug, t);
|
|
250
|
-
if (t.type === '
|
|
251
|
-
else if (t.type === 'story') storySlugs.push(slug);
|
|
244
|
+
if (t.type === 'story') storySlugs.push(slug);
|
|
252
245
|
else {
|
|
253
246
|
throw new Error(
|
|
254
247
|
`[spec-renderer] ticket "${slug}" has unknown type "${t.type}"`,
|
|
255
248
|
);
|
|
256
249
|
}
|
|
257
250
|
}
|
|
258
|
-
return { bySlug,
|
|
251
|
+
return { bySlug, storySlugs };
|
|
259
252
|
}
|
|
260
253
|
|
|
261
254
|
/**
|
|
@@ -283,9 +276,8 @@ function layerStories(storySlugs, bySlug) {
|
|
|
283
276
|
* Project the decomposer ticket array into the structural spec object.
|
|
284
277
|
*
|
|
285
278
|
* @param {Array<object>} tickets — flat ticket array as emitted by the
|
|
286
|
-
* decomposer Skill (`type`
|
|
287
|
-
* `
|
|
288
|
-
* `acceptance`, `verify`).
|
|
279
|
+
* decomposer Skill (`type` = story, `slug`, `depends_on`, `title`,
|
|
280
|
+
* `body`, `labels`, `acceptance`, `verify`).
|
|
289
281
|
* @param {object} opts
|
|
290
282
|
* @param {{id: number, title: string, body?: string, labels?: string[]}} opts.epic
|
|
291
283
|
* — Epic descriptor (the decomposer doesn't emit the Epic row; it's
|
|
@@ -297,7 +289,7 @@ function layerStories(storySlugs, bySlug) {
|
|
|
297
289
|
* @param {boolean} [opts.validate=true] — when `false`, skip final
|
|
298
290
|
* schema validation (used by tests that intentionally craft invalid
|
|
299
291
|
* inputs).
|
|
300
|
-
* @returns {object} spec — `{ epic,
|
|
292
|
+
* @returns {object} spec — `{ epic, stories, gates? }` matching
|
|
301
293
|
* `.agents/schemas/epic-spec.schema.json`.
|
|
302
294
|
*/
|
|
303
295
|
function validateRenderSpecInputs(tickets, opts) {
|
|
@@ -318,18 +310,6 @@ function validateRenderSpecInputs(tickets, opts) {
|
|
|
318
310
|
}
|
|
319
311
|
}
|
|
320
312
|
|
|
321
|
-
function bucketStoriesByFeature({ storySlugs, bySlug }) {
|
|
322
|
-
const storiesByFeature = new Map();
|
|
323
|
-
for (const slug of storySlugs) {
|
|
324
|
-
const story = bySlug.get(slug);
|
|
325
|
-
const parent = story.parent_slug;
|
|
326
|
-
if (typeof parent !== 'string' || !bySlug.has(parent)) continue;
|
|
327
|
-
if (!storiesByFeature.has(parent)) storiesByFeature.set(parent, []);
|
|
328
|
-
storiesByFeature.get(parent).push(slug);
|
|
329
|
-
}
|
|
330
|
-
return storiesByFeature;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
313
|
function pickStringArray(primary, fallback) {
|
|
334
314
|
const src = Array.isArray(primary)
|
|
335
315
|
? primary
|
|
@@ -342,7 +322,7 @@ function pickStringArray(primary, fallback) {
|
|
|
342
322
|
}
|
|
343
323
|
|
|
344
324
|
function extractStoryAcceptanceVerify(story) {
|
|
345
|
-
//
|
|
325
|
+
// Stories carry inline
|
|
346
326
|
// acceptance[] / verify[] arrays directly on the ticket. The
|
|
347
327
|
// decomposer may emit these either at the top level of the ticket
|
|
348
328
|
// (preferred) or nested under a structured `body` object (legacy
|
|
@@ -380,20 +360,6 @@ function buildStoryOut({ story, layers, storySet }) {
|
|
|
380
360
|
return out;
|
|
381
361
|
}
|
|
382
362
|
|
|
383
|
-
function buildFeatureOut({ feature, storySlugs, bySlug, layers, storySet }) {
|
|
384
|
-
const stories = storySlugs.map((storySlug) =>
|
|
385
|
-
buildStoryOut({
|
|
386
|
-
story: bySlug.get(storySlug),
|
|
387
|
-
layers,
|
|
388
|
-
storySet,
|
|
389
|
-
}),
|
|
390
|
-
);
|
|
391
|
-
const out = { slug: feature.slug, title: feature.title, stories };
|
|
392
|
-
assignNonEmpty(out, 'body', renderBody(feature.body));
|
|
393
|
-
assignNonEmpty(out, 'labels', sanitizeLabels(feature.labels));
|
|
394
|
-
return out;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
363
|
function buildEpicOut(epic) {
|
|
398
364
|
const out = { id: epic.id, title: epic.title };
|
|
399
365
|
assignNonEmpty(out, 'body', renderBody(epic.body));
|
|
@@ -432,22 +398,19 @@ export function renderSpec(tickets, opts = {}) {
|
|
|
432
398
|
validateRenderSpecInputs(tickets, opts);
|
|
433
399
|
const { epic, gates, schemaPath, validate = true } = opts;
|
|
434
400
|
|
|
435
|
-
const { bySlug,
|
|
401
|
+
const { bySlug, storySlugs } = indexTickets(tickets);
|
|
436
402
|
const { layers } = layerStories(storySlugs, bySlug);
|
|
437
|
-
const storiesByFeature = bucketStoriesByFeature({ storySlugs, bySlug });
|
|
438
403
|
const storySet = new Set(storySlugs);
|
|
439
404
|
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
storySlugs: storiesByFeature.get(featureSlug) ?? [],
|
|
444
|
-
bySlug,
|
|
405
|
+
const stories = storySlugs.map((storySlug) =>
|
|
406
|
+
buildStoryOut({
|
|
407
|
+
story: bySlug.get(storySlug),
|
|
445
408
|
layers,
|
|
446
409
|
storySet,
|
|
447
410
|
}),
|
|
448
411
|
);
|
|
449
412
|
|
|
450
|
-
const spec = { epic: buildEpicOut(epic),
|
|
413
|
+
const spec = { epic: buildEpicOut(epic), stories };
|
|
451
414
|
const gatesOut = buildGatesOut(gates);
|
|
452
415
|
if (gatesOut) spec.gates = gatesOut;
|
|
453
416
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* spec-section-validator.js — Phase 7.5 Tech Spec post-authoring section gate.
|
|
3
3
|
*
|
|
4
|
-
* `/
|
|
4
|
+
* `/plan` Phase 7 authors a Tech Spec from documentation and the PRD.
|
|
5
5
|
* Phase 8.3 (Holistic Consolidation) then reconciles the draft ticket array
|
|
6
6
|
* against the Tech Spec's `## Delivery Slicing` section, which the
|
|
7
7
|
* decompose-author skill uses as the capability-boundary anchor. When the
|