mandrel 1.59.0 → 1.61.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 +86 -44
- package/.agents/docs/SDLC.md +135 -141
- package/.agents/docs/configuration.md +77 -20
- package/.agents/docs/quality-gates.md +796 -0
- package/.agents/docs/workflows.md +6 -9
- 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/runtime-deps.json +2 -2
- package/.agents/schemas/agentrc.schema.json +3 -3
- 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 +2 -2
- package/.agents/scripts/acceptance-eval.js +1 -1
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/agents-bootstrap-github.js +23 -119
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/check-doc-links.js +2 -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 +6 -6
- package/.agents/scripts/epic-deliver-prepare.js +1 -1
- package/.agents/scripts/epic-execute-record-wave.js +4 -4
- 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 +1 -1
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +7 -11
- 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/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 +47 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +7 -9
- package/.agents/scripts/lib/bootstrap/manifest.js +21 -1
- package/.agents/scripts/lib/bootstrap/merge-methods.js +31 -16
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +32 -11
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/sync-agentrc.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- 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/detect-package-manager.js +72 -0
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/errors/index.js +4 -4
- 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/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +5 -10
- package/.agents/scripts/lib/onboard/detect-stack.js +10 -10
- package/.agents/scripts/lib/onboard/init-tail.js +218 -0
- package/.agents/scripts/lib/onboard/scaffold-docs.js +18 -3
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +1 -1
- package/.agents/scripts/lib/orchestration/code-review.js +5 -5
- 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 +9 -25
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +8 -8
- 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-lease-guard.js +26 -13
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +1 -1
- 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 +2 -2
- 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 +6 -21
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +8 -8
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +4 -4
- 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 +2 -2
- 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 +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +1 -1
- 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 +1 -1
- 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/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/preflight-cache.js +1 -1
- 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/single-story-close/phases/wrong-tree-guard.js +1 -1
- 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/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +2 -2
- 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 +2 -2
- 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 +5 -12
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +8 -8
- package/.agents/scripts/lib/orchestration/ticketing/state.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +2 -2
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +1 -1
- 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/runtime-deps/preflight.js +6 -6
- 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-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 +8 -8
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/wave-runner/tick.js +1 -1
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +5 -2
- 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 +4 -4
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +1 -1
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +5 -5
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +13 -16
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +1 -1
- 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/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +16 -31
- 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 +2 -2
- 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/agents-sync-config.md +3 -2
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +43 -43
- 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 +13 -13
- 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 +11 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/plan.md +131 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +19 -39
- package/bin/mandrel.js +235 -16
- package/docs/CHANGELOG.md +1173 -0
- package/lib/cli/doctor.js +45 -3
- package/lib/cli/init.js +97 -36
- package/lib/cli/registry.js +41 -145
- package/lib/cli/sync.js +122 -23
- package/lib/cli/uninstall.js +42 -7
- package/lib/cli/update.js +524 -210
- package/lib/cli/version-helpers.js +59 -0
- package/package.json +7 -6
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/workflows/onboard.md +0 -208
- package/lib/cli/__tests__/migrate.test.js +0 -268
- package/lib/cli/__tests__/sync-local-zone.test.js +0 -247
- package/lib/cli/__tests__/sync.test.js +0 -372
- package/lib/cli/__tests__/update-major.test.js +0 -217
- package/lib/cli/__tests__/update.test.js +0 -696
- package/lib/cli/__tests__/version-check.test.js +0 -398
- package/lib/migrations/__tests__/index.test.js +0 -216
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* .agents/scripts/hierarchy-gate.js — Hierarchy Completeness Gate
|
|
5
5
|
*
|
|
6
|
-
* Walks the Epic's full sub-issue graph (
|
|
6
|
+
* Walks the Epic's full sub-issue graph (Stories) and verifies
|
|
7
7
|
* every descendant is closed. Where the wave gate asks "did the sprint
|
|
8
8
|
* complete what it committed to?" (manifest view), this gate asks "is
|
|
9
9
|
* anything still open under this Epic?" (live GitHub graph view).
|
|
@@ -16,14 +16,13 @@
|
|
|
16
16
|
* top-level Stories outside the Epic's sub-issue graph.
|
|
17
17
|
*
|
|
18
18
|
* Per ticket type the rule is:
|
|
19
|
-
* - Features — must be closed.
|
|
20
19
|
* - Stories — must be closed.
|
|
21
20
|
* - Auxiliary (context::prd, context::tech-spec) — ignored.
|
|
22
21
|
* These are closed by the operator after the Epic PR merges, so
|
|
23
22
|
* requiring them closed here would block every Epic.
|
|
24
23
|
*
|
|
25
|
-
* **
|
|
26
|
-
*
|
|
24
|
+
* **2-tier hierarchy (Story #4041).** Mandrel ships only Epic / Story
|
|
25
|
+
* tickets. `getSubTickets(<storyId>)` returns `[]`; the walk
|
|
27
26
|
* terminates at the Story. Acceptance criteria live inline on the
|
|
28
27
|
* Story body.
|
|
29
28
|
*
|
|
@@ -53,7 +52,6 @@ const SUB_TICKET_FETCH_CONCURRENCY = 4;
|
|
|
53
52
|
function classify(ticket) {
|
|
54
53
|
const labels = ticket.labels ?? [];
|
|
55
54
|
if (labels.includes(TYPE_LABELS.STORY)) return 'story';
|
|
56
|
-
if (labels.includes(TYPE_LABELS.FEATURE)) return 'feature';
|
|
57
55
|
if (
|
|
58
56
|
labels.includes(CONTEXT_LABELS.PRD) ||
|
|
59
57
|
labels.includes(CONTEXT_LABELS.TECH_SPEC)
|
|
@@ -80,7 +78,7 @@ async function collectDescendants(provider, epicId) {
|
|
|
80
78
|
const out = [];
|
|
81
79
|
// Level-order BFS: each round fetches the whole frontier's children with a
|
|
82
80
|
// bounded-parallel map instead of one awaited round-trip per node. Stories
|
|
83
|
-
// are
|
|
81
|
+
// are leaves (no sub-issues by contract), so they are never expanded
|
|
84
82
|
// — that skip alone removes the largest class of wasted GraphQL calls.
|
|
85
83
|
let frontier = [epicId];
|
|
86
84
|
while (frontier.length > 0) {
|
|
@@ -126,7 +124,7 @@ export async function runHierarchyGate({ epicId, injectedProvider } = {}) {
|
|
|
126
124
|
process.exit(2);
|
|
127
125
|
}
|
|
128
126
|
|
|
129
|
-
const failures = {
|
|
127
|
+
const failures = { story: [], other: [] };
|
|
130
128
|
let auxiliaryDeferred = 0;
|
|
131
129
|
for (const ticket of descendants) {
|
|
132
130
|
const kind = classify(ticket);
|
|
@@ -144,15 +142,13 @@ export async function runHierarchyGate({ epicId, injectedProvider } = {}) {
|
|
|
144
142
|
}
|
|
145
143
|
}
|
|
146
144
|
|
|
147
|
-
const totalOpen =
|
|
148
|
-
failures.feature.length + failures.story.length + failures.other.length;
|
|
145
|
+
const totalOpen = failures.story.length + failures.other.length;
|
|
149
146
|
|
|
150
147
|
if (totalOpen > 0) {
|
|
151
148
|
Logger.error(
|
|
152
149
|
`[hierarchy-gate] ❌ Hierarchy-completeness gate FAILED for Epic #${epicId}: ${totalOpen} descendant(s) incomplete.`,
|
|
153
150
|
);
|
|
154
151
|
const sections = [
|
|
155
|
-
['feature', 'Features'],
|
|
156
152
|
['story', 'Stories'],
|
|
157
153
|
['other', 'Untyped descendants'],
|
|
158
154
|
];
|
|
@@ -163,7 +159,7 @@ export async function runHierarchyGate({ epicId, injectedProvider } = {}) {
|
|
|
163
159
|
Logger.error(` - #${item.id} (${item.reason}) — ${item.title}`);
|
|
164
160
|
}
|
|
165
161
|
}
|
|
166
|
-
Logger.error('\nClose the open descendants and re-run `/
|
|
162
|
+
Logger.error('\nClose the open descendants and re-run `/deliver`.');
|
|
167
163
|
process.exit(1);
|
|
168
164
|
}
|
|
169
165
|
|
|
@@ -154,7 +154,7 @@ export class ITicketingProvider {
|
|
|
154
154
|
/**
|
|
155
155
|
* Create a child ticket within an Epic's structural hierarchy.
|
|
156
156
|
*
|
|
157
|
-
* @param {number} parentId - GitHub Issue number of the immediate structural parent (e.g. Epic
|
|
157
|
+
* @param {number} parentId - GitHub Issue number of the immediate structural parent (e.g. Epic or Story).
|
|
158
158
|
* @param {{
|
|
159
159
|
* epicId: number,
|
|
160
160
|
* title: string,
|
|
@@ -60,7 +60,7 @@ export function matchesAnyFilePattern(patterns, files) {
|
|
|
60
60
|
* callers MUST pass the requested Epic's own branch ref (e.g.
|
|
61
61
|
* `refs/heads/epic/<id>`) so the change set is pinned to that Epic's branch
|
|
62
62
|
* rather than whatever HEAD the shared checkout happens to sit on. Under two
|
|
63
|
-
* concurrent `/
|
|
63
|
+
* concurrent `/deliver` runs sharing one checkout, diffing against
|
|
64
64
|
* `HEAD` silently resolves the *other* Epic's change set (Story #3362). When
|
|
65
65
|
* `headRef` cannot be resolved in the repo, the selector returns a
|
|
66
66
|
* `degraded: true` envelope (or hard-fails in gate-mode) instead of diffing
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/audit-to-stories/seed-epic-from-findings.js
|
|
3
3
|
*
|
|
4
|
-
* Build the `--idea`-shaped seed markdown that `/
|
|
4
|
+
* Build the `--idea`-shaped seed markdown that `/plan` Phase 1
|
|
5
5
|
* consumes when the operator picks the Single-Epic grouping mode.
|
|
6
6
|
*
|
|
7
7
|
* The seed renders the canonical one-pager sections so the idea-refinement
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Recommended Direction (rollup of recommendations by dimension)
|
|
11
11
|
* - Key Assumptions (carries the source-report links forward)
|
|
12
12
|
* - MVP Scope (the proposed Stories, one bullet per group)
|
|
13
|
-
* - Key Files (explicit file paths so /
|
|
13
|
+
* - Key Files (explicit file paths so /plan Phase 7 decompose
|
|
14
14
|
* has concrete anchors)
|
|
15
15
|
* - Not Doing (out-of-scope items by convention)
|
|
16
16
|
*
|
|
@@ -28,8 +28,8 @@ import { calculateAll, scanDirectory } from './maintainability-utils.js';
|
|
|
28
28
|
* baseline-snapshot.js — per-Epic baseline lifecycle helpers.
|
|
29
29
|
*
|
|
30
30
|
* Story #1396 (Epic #1386). The Epic-snapshot scheme freezes the maintainability
|
|
31
|
-
* and crap baselines at /
|
|
32
|
-
* at /
|
|
31
|
+
* and crap baselines at /plan time and reconciles them back to `main`
|
|
32
|
+
* at /deliver time. Two helpers, both pure-ish (deterministic given the
|
|
33
33
|
* working tree + injected I/O):
|
|
34
34
|
*
|
|
35
35
|
* - forkMainToEpic({ epicId, cwd }) — copies the tracked main baselines
|
|
@@ -37,20 +37,20 @@ import { calculateAll, scanDirectory } from './maintainability-utils.js';
|
|
|
37
37
|
* source content produces the same destination bytes (no fs churn). When
|
|
38
38
|
* the source baseline is missing, emits a warn through the injected
|
|
39
39
|
* logger and returns `{ written: false, reason: 'source-missing' }` for
|
|
40
|
-
* that file — callers (e.g. /
|
|
40
|
+
* that file — callers (e.g. /plan Phase 7) treat the absence as
|
|
41
41
|
* non-fatal and stay in `--full-scope` mode.
|
|
42
42
|
*
|
|
43
43
|
* - regenerateMainFromTree({ cwd }) — re-scores maintainability + crap
|
|
44
44
|
* against the current working tree and writes the result to the tracked
|
|
45
45
|
* main baseline paths. Returns `{ didChange, paths }` where `didChange`
|
|
46
46
|
* is true iff any baseline file's content differs from what's already on
|
|
47
|
-
* disk. Callers in /
|
|
47
|
+
* disk. Callers in /deliver use `didChange === false` to skip the
|
|
48
48
|
* `baseline-refresh: epic-<id>` commit.
|
|
49
49
|
*
|
|
50
50
|
* Lifecycle note (Story #1467): per-epic ratchet snapshots are ephemeral
|
|
51
51
|
* scratch state under the `temp/epic-<id>/baselines/` namespace, NOT committed
|
|
52
52
|
* artifacts. They inherit the existing per-epic temp-tree cleanup contract —
|
|
53
|
-
* `/
|
|
53
|
+
* `/deliver` reaps the parent `temp/epic-<id>/` directory on merge, so
|
|
54
54
|
* no manual prune is required. Earlier versions of this module wrote under
|
|
55
55
|
* `baselines/epic/<id>/`, which committed them to git and accumulated obsolete
|
|
56
56
|
* snapshots forever.
|
|
@@ -103,7 +103,7 @@ export function epicSnapshotPathFor({ epicId, kind, cwd = process.cwd() }) {
|
|
|
103
103
|
* - Source baseline missing → returned per-file `{ written: false,
|
|
104
104
|
* reason: 'source-missing' }`. Logger warn fires once per missing file.
|
|
105
105
|
* Caller stays in `--full-scope` mode.
|
|
106
|
-
* - Source unreadable / not parseable → throws. Re-running /
|
|
106
|
+
* - Source unreadable / not parseable → throws. Re-running /plan
|
|
107
107
|
* with `--force` after fixing the source recovers.
|
|
108
108
|
*
|
|
109
109
|
* @param {{
|
|
@@ -428,7 +428,7 @@ export function commitSnapshotsToEpicBranch({
|
|
|
428
428
|
* via `delivery.quality.gates.crap.coveragePath`. When coverage is missing and
|
|
429
429
|
* `requireCoverage` is true, the crap regeneration is skipped (didChange stays
|
|
430
430
|
* false for that file) and a warn is emitted — the operator is expected to run
|
|
431
|
-
* `npm run test:coverage` before /
|
|
431
|
+
* `npm run test:coverage` before /deliver if a refresh is anticipated.
|
|
432
432
|
*
|
|
433
433
|
* @param {{
|
|
434
434
|
* cwd?: string,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* The verification is **static**: we inspect `package.json` for a known BDD
|
|
14
14
|
* runner dependency, and consult a small lookup table of which runners
|
|
15
15
|
* support which pending/skip tag. We do not boot the runner. This keeps
|
|
16
|
-
* `/
|
|
16
|
+
* `/plan` Phase 7 hermetic and offline.
|
|
17
17
|
*
|
|
18
18
|
* **Workspace awareness (Story #2956).** In a pnpm / npm / yarn monorepo the
|
|
19
19
|
* BDD runner is rarely a root devDependency — it lives in the workspace
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* bdd-scenario-scanner.js — Gherkin scenario index for /
|
|
2
|
+
* bdd-scenario-scanner.js — Gherkin scenario index for /plan Phase 7.
|
|
3
3
|
*
|
|
4
4
|
* Story #2637 (sibling to #2634 codebase-snapshot, #2635 spec-freshness,
|
|
5
5
|
* #2636 file-assumption gate). The Acceptance Engineer step of
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* alone — it never inspects the consumer project's existing `.feature`
|
|
8
8
|
* files. Planned ACs frequently duplicate scenarios that already exist or
|
|
9
9
|
* re-specify behaviour the codebase already proves; the duplication is
|
|
10
|
-
* only discovered (at best) during `/
|
|
10
|
+
* only discovered (at best) during `/deliver` or (at worst) after
|
|
11
11
|
* a redundant PR ships.
|
|
12
12
|
*
|
|
13
13
|
* `scanBddScenarios` walks every configured feature root, parses each
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* have a matching scenario.
|
|
21
21
|
*
|
|
22
22
|
* Determinism is load-bearing: the matcher is keyword-based, not
|
|
23
|
-
* embedding-based, so re-running `/
|
|
23
|
+
* embedding-based, so re-running `/plan` against the same
|
|
24
24
|
* acceptance spec produces the same disposition annotations.
|
|
25
25
|
*/
|
|
26
26
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*
|
|
16
16
|
* All three shapes are relocated under `<repoRoot>/temp/epic/<id>/baselines/`,
|
|
17
17
|
* where they inherit the existing per-epic temp-tree cleanup contract:
|
|
18
|
-
* `/
|
|
18
|
+
* `/deliver` reaps `temp/epic/<id>/` on merge, so the ratchet snapshots
|
|
19
19
|
* are ephemeral scratch state — never committed, no manual prune.
|
|
20
20
|
*
|
|
21
21
|
* The main-tracked `baselines/{maintainability,crap}.json` files are NOT
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - `enforce_admins: true` — admins do not bypass the prGate suite.
|
|
8
8
|
* - `required_pull_request_reviews.required_approving_review_count: 0` —
|
|
9
9
|
* CI is the gate; the operator monitors and iterates the open PR
|
|
10
|
-
* to green via `/
|
|
10
|
+
* to green via `/deliver`'s Phase 7 watch loop.
|
|
11
11
|
*
|
|
12
12
|
* Behaviour rules
|
|
13
13
|
* ---------------
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* `--full-scope` so a self-diff doesn't degrade to "no files in diff".
|
|
15
15
|
*
|
|
16
16
|
* 2. **Pass `--epic-ref` where the firing site is Epic-aware.** Inside
|
|
17
|
-
* `/
|
|
17
|
+
* `/deliver` the close-validation chain already threads
|
|
18
18
|
* `--epic-ref epic/<id>`; on CI the equivalent surface is the PR's
|
|
19
19
|
* base branch. The template wires `--epic-ref ${EPIC_REF}` through an
|
|
20
20
|
* env var the workflow computes from `github.head_ref` (story-N
|
|
@@ -28,6 +28,9 @@
|
|
|
28
28
|
* @module bootstrap/ci-workflow-template
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
|
+
import fs from 'node:fs';
|
|
32
|
+
import path from 'node:path';
|
|
33
|
+
|
|
31
34
|
/**
|
|
32
35
|
* @typedef {object} CiTemplateOptions
|
|
33
36
|
* @property {string} [nodeVersion='22'] - Node major to install via setup-node.
|
|
@@ -169,3 +172,46 @@ ${crapBlock}`;
|
|
|
169
172
|
* path without re-deriving it.
|
|
170
173
|
*/
|
|
171
174
|
export const CI_WORKFLOW_RELATIVE_PATH = '.github/workflows/ci.yml';
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Render the stabilized-quality-gates CI workflow template into a project
|
|
178
|
+
* checkout. Idempotent on the byte level: when `.github/workflows/ci.yml`
|
|
179
|
+
* already matches the rendered template, no write occurs and the action is
|
|
180
|
+
* `unchanged`. When the file is absent the action is `created`. When the
|
|
181
|
+
* file exists with operator-authored differences the helper preserves it
|
|
182
|
+
* and returns `custom-workflow-skip` along with the rendered body so the
|
|
183
|
+
* bootstrap caller (or `/agents-update`) can offer a side-by-side diff.
|
|
184
|
+
*
|
|
185
|
+
* Network-free; safe to invoke under tests with a tmp `projectRoot`.
|
|
186
|
+
*
|
|
187
|
+
* Moved from `agents-bootstrap-github.js` to this module so it lives
|
|
188
|
+
* alongside `renderCiWorkflow` and `CI_WORKFLOW_RELATIVE_PATH`
|
|
189
|
+
* (Story #4048 B2 — dead-code deletion from the bootstrap entry point).
|
|
190
|
+
*
|
|
191
|
+
* @param {object} args
|
|
192
|
+
* @param {string} args.projectRoot - Repo root (must contain or accept
|
|
193
|
+
* `.github/workflows/`).
|
|
194
|
+
* @param {object} [args.template] - Forwarded to `renderCiWorkflow`.
|
|
195
|
+
* @param {boolean} [args.write=true] - When `false`, the helper computes
|
|
196
|
+
* the would-be action without touching disk. Used by the
|
|
197
|
+
* bootstrap CLI's dry-run mode.
|
|
198
|
+
* @returns {{ action: 'created'|'unchanged'|'custom-workflow-skip',
|
|
199
|
+
* path: string, rendered: string }}
|
|
200
|
+
*/
|
|
201
|
+
export function ensureCiWorkflow(args) {
|
|
202
|
+
const projectRoot = args.projectRoot;
|
|
203
|
+
const rendered = renderCiWorkflow(args.template);
|
|
204
|
+
const target = path.join(projectRoot, CI_WORKFLOW_RELATIVE_PATH);
|
|
205
|
+
if (!fs.existsSync(target)) {
|
|
206
|
+
if (args.write !== false) {
|
|
207
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
208
|
+
fs.writeFileSync(target, rendered, 'utf8');
|
|
209
|
+
}
|
|
210
|
+
return { action: 'created', path: target, rendered };
|
|
211
|
+
}
|
|
212
|
+
const existing = fs.readFileSync(target, 'utf8');
|
|
213
|
+
if (existing === rendered) {
|
|
214
|
+
return { action: 'unchanged', path: target, rendered };
|
|
215
|
+
}
|
|
216
|
+
return { action: 'custom-workflow-skip', path: target, rendered };
|
|
217
|
+
}
|
|
@@ -112,8 +112,8 @@ export function buildManualInstructions({ stagePaths, baseBranch }) {
|
|
|
112
112
|
` git push -u origin ${baseBranch}`,
|
|
113
113
|
'',
|
|
114
114
|
'Story delivery runs in git worktrees that check out tracked files only,',
|
|
115
|
-
'so the .agents/ wiring MUST be committed before any /
|
|
116
|
-
'/
|
|
115
|
+
'so the .agents/ wiring MUST be committed before any /deliver or',
|
|
116
|
+
'/deliver run — otherwise the worktree has no scripts and breaks.',
|
|
117
117
|
].join('\n');
|
|
118
118
|
}
|
|
119
119
|
|
|
@@ -38,14 +38,12 @@ const GH_SCOPES_UNREADABLE_NOTE =
|
|
|
38
38
|
* Framework runtime deps the consumer must have installed in
|
|
39
39
|
* `node_modules/` before this script reaches the dynamic
|
|
40
40
|
* `config-resolver` import. `ajv` is the sentinel — if it cannot
|
|
41
|
-
* resolve, the
|
|
42
|
-
* Step 2c/2d dependency-install never ran). The list mirrors the floor
|
|
43
|
-
* in `agents-bootstrap-project.md` Step 2c; keep them in sync.
|
|
41
|
+
* resolve, the framework runtime dependencies are not installed.
|
|
44
42
|
*/
|
|
45
43
|
const REQUIRED_RUNTIME_DEPS = Object.freeze(['ajv']);
|
|
46
44
|
|
|
47
45
|
const RUNTIME_DEPS_HINT =
|
|
48
|
-
'Run
|
|
46
|
+
'Run `mandrel init` (for a fresh project) or `npm install mandrel` (for an existing one) to install the framework runtime dependencies, then re-run this command.';
|
|
49
47
|
|
|
50
48
|
/**
|
|
51
49
|
* Default runner: synchronously execs `gh <args>` and returns
|
|
@@ -273,12 +271,12 @@ function classifyProjectScopes(scopeLine) {
|
|
|
273
271
|
/**
|
|
274
272
|
* Preflight the framework's runtime dependencies before dynamic-importing
|
|
275
273
|
* `config-resolver.js` (which transitively pulls in `ajv` via
|
|
276
|
-
* `config-settings-schema.js`). A
|
|
277
|
-
*
|
|
278
|
-
*
|
|
274
|
+
* `config-settings-schema.js`). A consumer who has not installed the
|
|
275
|
+
* framework runtime deps will not have `ajv` available, and the raw
|
|
276
|
+
* `ERR_MODULE_NOT_FOUND` from the dynamic import is opaque. This
|
|
279
277
|
* preflight converts that into a {@link MissingRuntimeDepsError} that
|
|
280
|
-
* names the missing packages and points the operator at the
|
|
281
|
-
*
|
|
278
|
+
* names the missing packages and points the operator at the correct
|
|
279
|
+
* remediation (`mandrel init` / `npm install mandrel`).
|
|
282
280
|
*
|
|
283
281
|
* The `resolver` seam lets tests inject a stub without touching the real
|
|
284
282
|
* module graph; production uses `import.meta.resolve(specifier)`.
|
|
@@ -150,7 +150,17 @@ export function buildMutationManifest(ctx = {}) {
|
|
|
150
150
|
|
|
151
151
|
// --- repo-config ------------------------------------------------------
|
|
152
152
|
// Local repository configuration files the bootstrap seeds or extends.
|
|
153
|
+
// Also includes git-init, which is the most irreversible local mutation
|
|
154
|
+
// the bootstrap performs (B1 — uninstall ledger must record it).
|
|
153
155
|
entries.push(
|
|
156
|
+
{
|
|
157
|
+
phaseGroup: PHASE_GROUPS.REPO_CONFIG,
|
|
158
|
+
target: rel('.git'),
|
|
159
|
+
action: 'run',
|
|
160
|
+
detail:
|
|
161
|
+
'Initialize the local git repository (git init + first commit) when absent. No-op when already a git repo.',
|
|
162
|
+
reversible: false,
|
|
163
|
+
},
|
|
154
164
|
{
|
|
155
165
|
phaseGroup: PHASE_GROUPS.REPO_CONFIG,
|
|
156
166
|
target: rel('package.json'),
|
|
@@ -209,6 +219,16 @@ export function buildMutationManifest(ctx = {}) {
|
|
|
209
219
|
? `${ctx.answers.owner}/${ctx.answers.repo}`
|
|
210
220
|
: 'the GitHub repository';
|
|
211
221
|
entries.push(
|
|
222
|
+
{
|
|
223
|
+
// B1: GitHub repo creation is the most irreversible remote mutation —
|
|
224
|
+
// record it so the uninstall ledger always lists it.
|
|
225
|
+
phaseGroup: PHASE_GROUPS.GITHUB_ADMIN,
|
|
226
|
+
target: `${repoSlug} (repo)`,
|
|
227
|
+
action: 'create',
|
|
228
|
+
detail:
|
|
229
|
+
'Create the GitHub repository (gh repo create --source=. --push) when absent. No-op when already pushed.',
|
|
230
|
+
reversible: false,
|
|
231
|
+
},
|
|
212
232
|
{
|
|
213
233
|
phaseGroup: PHASE_GROUPS.GITHUB_ADMIN,
|
|
214
234
|
target: `${repoSlug} labels`,
|
|
@@ -238,7 +258,7 @@ export function buildMutationManifest(ctx = {}) {
|
|
|
238
258
|
target: `${repoSlug} merge methods`,
|
|
239
259
|
action: 'configure',
|
|
240
260
|
detail:
|
|
241
|
-
'Set the allowed pull-request merge methods to the framework stance.',
|
|
261
|
+
'Set the allowed pull-request merge methods to the framework stance (squash-only, auto-merge enabled).',
|
|
242
262
|
reversible: false,
|
|
243
263
|
},
|
|
244
264
|
);
|
|
@@ -17,10 +17,13 @@
|
|
|
17
17
|
* No drift (live settings already match the target): no-op, returns
|
|
18
18
|
* `{ status: 'unchanged' }`.
|
|
19
19
|
*
|
|
20
|
-
* Drift (any field differs from the target stance):
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* `{ status: 'skipped', reason: 'hitl-declined' }` without writing
|
|
20
|
+
* Drift (any field differs from the target stance): when a `hitlConfirm`
|
|
21
|
+
* gate is supplied, the proposed payload routes through it — on approval
|
|
22
|
+
* the PATCH is issued; on decline the module returns
|
|
23
|
+
* `{ status: 'skipped', reason: 'hitl-declined' }` without writing (a loud
|
|
24
|
+
* decline, never silent). When NO gate is supplied (non-TTY, no operator
|
|
25
|
+
* present — Story #4045 A4), the framework stance is default-applied with
|
|
26
|
+
* an explicit log line.
|
|
24
27
|
*/
|
|
25
28
|
|
|
26
29
|
export const TARGET_MERGE_METHODS = Object.freeze({
|
|
@@ -81,20 +84,32 @@ export async function applyMergeMethods({
|
|
|
81
84
|
return { status: 'unchanged' };
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
let approved;
|
|
88
|
+
if (typeof hitlConfirm === 'function') {
|
|
89
|
+
approved = await hitlConfirm({
|
|
90
|
+
summary:
|
|
91
|
+
'Repo merge-method settings diverge from the framework hands-off-pipeline stance.',
|
|
92
|
+
current,
|
|
93
|
+
proposed: target,
|
|
94
|
+
});
|
|
95
|
+
if (!approved) {
|
|
96
|
+
log(
|
|
97
|
+
'[bootstrap] Merge methods: HITL declined — leaving operator settings ' +
|
|
98
|
+
'untouched. Note: auto-merge will remain disabled until the merge-method ' +
|
|
99
|
+
'settings match the framework stance (allow_squash_merge: true, ' +
|
|
100
|
+
'allow_auto_merge: true, delete_branch_on_merge: true).',
|
|
101
|
+
);
|
|
102
|
+
return { status: 'skipped', reason: 'hitl-declined', diff };
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// Non-TTY: no operator present to confirm. Default-apply the framework
|
|
106
|
+
// stance and log explicitly so the consequence is never silent.
|
|
94
107
|
log(
|
|
95
|
-
'[bootstrap] Merge methods:
|
|
108
|
+
'[bootstrap] Merge methods: non-TTY — applying framework stance automatically ' +
|
|
109
|
+
'(allow_squash_merge, allow_auto_merge, delete_branch_on_merge). ' +
|
|
110
|
+
'To opt out, pass a hitlConfirm gate or set github.mergeMethods overrides in .agentrc.json.',
|
|
96
111
|
);
|
|
97
|
-
|
|
112
|
+
approved = true;
|
|
98
113
|
}
|
|
99
114
|
|
|
100
115
|
try {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* bootstrap/project-bootstrap — deterministic
|
|
3
|
-
* `/agents-bootstrap-project` workflow (Story #2074, hard cutover).
|
|
2
|
+
* bootstrap/project-bootstrap — deterministic project bootstrap steps.
|
|
4
3
|
*
|
|
5
4
|
* Each exported `ensure*` function is one step of the bootstrap. Every step
|
|
6
5
|
* is idempotent and additive — re-running on an already-bootstrapped clone
|
|
@@ -16,6 +15,8 @@ import { spawnSync as defaultSpawnSync } from 'node:child_process';
|
|
|
16
15
|
import fs from 'node:fs';
|
|
17
16
|
import os from 'node:os';
|
|
18
17
|
import path from 'node:path';
|
|
18
|
+
import { pathToFileURL } from 'node:url';
|
|
19
|
+
import { detectPackageManager as detectPm } from '../detect-package-manager.js';
|
|
19
20
|
import { LEDGER_RELATIVE_PATH } from './install-ledger.js';
|
|
20
21
|
import { PHASE_GROUPS, previewMutationManifest } from './manifest.js';
|
|
21
22
|
import { applyQualityBootstrap } from './quality-bootstrap.js';
|
|
@@ -61,8 +62,8 @@ export const GITIGNORE_BLOCKS = Object.freeze({
|
|
|
61
62
|
block:
|
|
62
63
|
'\n# Project-scoped MCP config carries secrets — keep out of git.\n.mcp.json\n',
|
|
63
64
|
},
|
|
64
|
-
// Story #3894: `.env` holds real secrets (
|
|
65
|
-
// put `GITHUB_TOKEN` here). It MUST be ignored by default so a cold-start
|
|
65
|
+
// Story #3894: `.env` holds real secrets (`mandrel init` instructs operators
|
|
66
|
+
// to put `GITHUB_TOKEN` here). It MUST be ignored by default so a cold-start
|
|
66
67
|
// provision never stages/pushes it. The pattern matches a bare `.env` (with
|
|
67
68
|
// an optional trailing slash) but deliberately NOT `.env.example`, the
|
|
68
69
|
// committed placeholder that `security-baseline.md` § Secrets Management
|
|
@@ -112,7 +113,23 @@ function writeJson(p, obj, fsImpl = fs) {
|
|
|
112
113
|
fsImpl.writeFileSync(p, `${JSON.stringify(obj, null, 2)}\n`, 'utf8');
|
|
113
114
|
}
|
|
114
115
|
|
|
115
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* The minimum Node.js patch version mandrel requires.
|
|
118
|
+
*
|
|
119
|
+
* Rationale: `22.22.1` is the Node 22 LTS patch that ships with the
|
|
120
|
+
* `node:sqlite` built-in (`--experimental-sqlite` removed from flag
|
|
121
|
+
* requirement in 22.5.0, but the module stabilised at 22.22.1 per the
|
|
122
|
+
* Node 22 LTS changelog). Pinning to this patch prevents silent failures on
|
|
123
|
+
* older 22.x installs that lack the built-in. CI exercises `node-version: 22`
|
|
124
|
+
* which resolves to the latest 22.x (always ≥ 22.22.1 in the current GHA
|
|
125
|
+
* environment), so CI validates the contract without exercising the exact
|
|
126
|
+
* patch floor — the floor is enforced at install time, not in CI.
|
|
127
|
+
*
|
|
128
|
+
* Single source of truth: `registry.js` and any other consumer MUST import
|
|
129
|
+
* this constant rather than duplicating it.
|
|
130
|
+
*
|
|
131
|
+
* Matches `package.json` `engines.node` (`>=22.22.1 <25`).
|
|
132
|
+
*/
|
|
116
133
|
export const REQUIRED_NODE_FLOOR = '22.22.1';
|
|
117
134
|
export const REQUIRED_NODE_CEILING_MAJOR = 25;
|
|
118
135
|
|
|
@@ -152,16 +169,18 @@ export function checkNodeVersion(version = process.versions.node) {
|
|
|
152
169
|
|
|
153
170
|
/**
|
|
154
171
|
* Detect the package manager based on lockfile presence. Defaults to
|
|
155
|
-
* `npm` when no lock is found
|
|
172
|
+
* `npm` when no lock is found (including the `null` case from the shared
|
|
173
|
+
* helper where no Node manifest exists at all).
|
|
174
|
+
*
|
|
175
|
+
* Delegates to the shared `detectPackageManager` helper
|
|
176
|
+
* (Story #4048 B3 — one implementation per concept).
|
|
156
177
|
*
|
|
157
178
|
* @param {string} projectRoot
|
|
158
179
|
* @param {typeof fs} [fsImpl]
|
|
180
|
+
* @returns {'pnpm'|'yarn'|'npm'}
|
|
159
181
|
*/
|
|
160
182
|
export function detectPackageManager(projectRoot, fsImpl = fs) {
|
|
161
|
-
|
|
162
|
-
return 'pnpm';
|
|
163
|
-
if (fsImpl.existsSync(path.join(projectRoot, 'yarn.lock'))) return 'yarn';
|
|
164
|
-
return 'npm';
|
|
183
|
+
return detectPm(projectRoot, (p) => fsImpl.existsSync(p)) ?? 'npm';
|
|
165
184
|
}
|
|
166
185
|
|
|
167
186
|
/**
|
|
@@ -328,7 +347,9 @@ export async function validateAgentrc(ctx) {
|
|
|
328
347
|
if (!fsImpl.existsSync(schemaModule)) {
|
|
329
348
|
return { ok: false, errors: ['config-settings-schema.js not found'] };
|
|
330
349
|
}
|
|
331
|
-
|
|
350
|
+
// pathToFileURL handles Windows drive letters and percent-encoding
|
|
351
|
+
// correctly (same fix as commit 2e3d210b in lib/transpile.js).
|
|
352
|
+
const mod = await import(pathToFileURL(schemaModule).href);
|
|
332
353
|
const validate = mod.getAgentrcValidator();
|
|
333
354
|
const data = readJsonIfExists(
|
|
334
355
|
path.join(ctx.projectRoot, '.agentrc.json'),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* codebase-snapshot.js — Bounded structural view of the consumer repo.
|
|
3
3
|
*
|
|
4
|
-
* Story #2634 (sibling to #2635 spec-freshness). `/
|
|
4
|
+
* Story #2634 (sibling to #2635 spec-freshness). `/plan` Phase 7
|
|
5
5
|
* authors PRD + Tech Spec from documentation alone — `architecture.md`,
|
|
6
6
|
* `data-dictionary.md`, `decisions.md`, `patterns.md`. When those docs
|
|
7
7
|
* drift from the real source tree, the Architect persona cites modules
|
|
@@ -113,7 +113,7 @@ const KEY_MEANINGS = Object.freeze({
|
|
|
113
113
|
|
|
114
114
|
// planning.*
|
|
115
115
|
'planning.maxTickets':
|
|
116
|
-
'Upper bound on tickets a single /
|
|
116
|
+
'Upper bound on tickets a single /plan run may create.',
|
|
117
117
|
'planning.context.maxBytes':
|
|
118
118
|
'Byte budget for the planning-context payload before summary mode kicks in.',
|
|
119
119
|
'planning.context.summaryMode':
|
|
@@ -29,7 +29,7 @@ export const DEFAULT_DECOMPOSER = Object.freeze({
|
|
|
29
29
|
* host has adequate parallel-agent quota, operators should raise
|
|
30
30
|
* `delivery.deliverRunner.concurrencyCap` — wall-clock time falls
|
|
31
31
|
* proportionally to the extra concurrency. The safe default is a tuning
|
|
32
|
-
* knob, not a performance ceiling. See `epic
|
|
32
|
+
* knob, not a performance ceiling. See `helpers/deliver-epic.md` § Phase 2b and
|
|
33
33
|
* `agentrc-reference.json` `delivery.deliverRunner.concurrencyCap` for details.
|
|
34
34
|
*
|
|
35
35
|
* **`verifyConcurrencyCap`** (Epic #3019 Tech Spec §1.4 / Story #3024) is a
|
|
@@ -44,7 +44,7 @@ const DEFAULT_DELIVER_RUNNER = Object.freeze({
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* Default auto-fix loop ceilings for /
|
|
47
|
+
* Default auto-fix loop ceilings for /deliver Phase 4 (epic-audit)
|
|
48
48
|
* and Phase 5 (code-review). Operators override via
|
|
49
49
|
* `delivery.epicAudit.*` and `delivery.codeReview.*` in `.agentrc.json`
|
|
50
50
|
* (Story #2611, Epic #2586).
|
|
@@ -77,7 +77,7 @@ export function resolveWorkingPath({
|
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* One-shot environment-aware runtime resolution. Returns the trio of runtime
|
|
80
|
-
* signals consumed across `/
|
|
80
|
+
* signals consumed across `/deliver`: whether worktree isolation is on
|
|
81
81
|
* for this process, the session id for claim labels, and whether we're in a
|
|
82
82
|
* Claude Code web session. Each signal also records its **source** so the
|
|
83
83
|
* `story-init` startup log can name why the value is what it is.
|
|
@@ -69,7 +69,7 @@ export function syncAgentrc(opts) {
|
|
|
69
69
|
status: 'missing-config',
|
|
70
70
|
changes: [],
|
|
71
71
|
errors: [
|
|
72
|
-
`No .agentrc.json at ${configPath}. Run /
|
|
72
|
+
`No .agentrc.json at ${configPath}. Run \`mandrel init\` (new project) or \`node .agents/scripts/bootstrap.js\` to create it.`,
|
|
73
73
|
],
|
|
74
74
|
configPath,
|
|
75
75
|
wrote: false,
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
* parent of `git rev-parse --git-common-dir`) rather than `process.cwd()`.
|
|
45
45
|
* Without this, a story child that `cd`s into `.worktrees/story-<id>/` before
|
|
46
46
|
* calling `story-phase.js` would append `story.heartbeat` records to
|
|
47
|
-
* `<worktree>/temp/epic-N/lifecycle.ndjson`, while the `/
|
|
47
|
+
* `<worktree>/temp/epic-N/lifecycle.ndjson`, while the `/deliver` host
|
|
48
48
|
* (running from the main checkout) reads the main-checkout copy — so the
|
|
49
49
|
* idle-watchdog never sees heartbeats and the Epic-lease guard silently
|
|
50
50
|
* reclaims live foreign claims (the audit-#3513 bug class). Anchoring the
|
|
@@ -335,7 +335,7 @@ export const epicPerfReportPath = (eid, config) =>
|
|
|
335
335
|
|
|
336
336
|
/**
|
|
337
337
|
* `temp/epic-<eid>/epic-perf-report.json` — canonical JSON snapshot of
|
|
338
|
-
* the `epic-perf-report` payload persisted at /
|
|
338
|
+
* the `epic-perf-report` payload persisted at /deliver close
|
|
339
339
|
* (Epic #3019 / Story #3029 / Task #3040). When present alongside the
|
|
340
340
|
* `epic-perf-report` structured comment, the report is discoverable
|
|
341
341
|
* from the file system without round-tripping the ticketing provider,
|
|
@@ -203,7 +203,7 @@ const MERGE_WATCH_SCHEMA = {
|
|
|
203
203
|
};
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
* `delivery.epicAudit` — bounded-retry knobs for /
|
|
206
|
+
* `delivery.epicAudit` — bounded-retry knobs for /deliver Phase 4
|
|
207
207
|
* (epic-audit). `maxFixAttempts` caps how many times the auto-fix loop
|
|
208
208
|
* retries a single finding (Story #2611, Epic #2586). `maxFixScopeFiles`
|
|
209
209
|
* caps how many files a single auto-fix may touch before escalating to
|
|
@@ -237,7 +237,7 @@ const CI_DELIVERY_SCHEMA = {
|
|
|
237
237
|
// Story #2899 (Epic #2880) — `delivery.preflight.*` thresholds consumed
|
|
238
238
|
// by `epic-deliver-preflight.js`. When any value is exceeded the CLI
|
|
239
239
|
// surfaces a breach in its envelope and the workflow flips the Epic to
|
|
240
|
-
// `agent::blocked` (see /
|
|
240
|
+
// `agent::blocked` (see /deliver Phase 1 prelude).
|
|
241
241
|
const PREFLIGHT_SCHEMA = {
|
|
242
242
|
type: 'object',
|
|
243
243
|
properties: {
|
|
@@ -102,7 +102,7 @@ export const QUALITY_SCHEMA = {
|
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
104
|
* `delivery.codeReview` — sibling to `delivery.epicAudit`. Same bounded
|
|
105
|
-
* retry + scope cap, applied to /
|
|
105
|
+
* retry + scope cap, applied to /deliver Phase 5 (code-review).
|
|
106
106
|
*/
|
|
107
107
|
export const CODE_REVIEW_SCHEMA = {
|
|
108
108
|
type: 'object',
|