mandrel 1.76.0 → 1.78.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.
Files changed (68) hide show
  1. package/.agents/docs/configuration.md +2 -2
  2. package/.agents/docs/workflows.md +19 -0
  3. package/.agents/schemas/agentrc.schema.json +1 -1
  4. package/.agents/schemas/dispatch-manifest.json +1 -1
  5. package/.agents/schemas/lifecycle/loop.tick.schema.json +20 -0
  6. package/.agents/schemas/loop-unit.schema.json +70 -0
  7. package/.agents/schemas/validation-evidence.schema.json +2 -1
  8. package/.agents/scripts/audit-to-stories.js +43 -1
  9. package/.agents/scripts/check-doc-links.js +24 -1
  10. package/.agents/scripts/check-loop-units.js +204 -0
  11. package/.agents/scripts/epic-deliver-prepare.js +31 -0
  12. package/.agents/scripts/evidence-gate.js +48 -12
  13. package/.agents/scripts/generate-workflows-doc.js +37 -4
  14. package/.agents/scripts/lib/audit-to-stories/build-story-body.js +141 -34
  15. package/.agents/scripts/lib/cli-args.js +6 -0
  16. package/.agents/scripts/lib/close-validation/process.js +61 -5
  17. package/.agents/scripts/lib/close-validation/runner.js +42 -9
  18. package/.agents/scripts/lib/config/temp-paths.js +1 -1
  19. package/.agents/scripts/lib/config/worktree-isolation.js +18 -3
  20. package/.agents/scripts/lib/config-resolver.js +4 -1
  21. package/.agents/scripts/lib/config-settings-schema-delivery.js +1 -1
  22. package/.agents/scripts/lib/git-branch-lifecycle.js +90 -0
  23. package/.agents/scripts/lib/loop-units/validate-loop-unit.js +197 -0
  24. package/.agents/scripts/lib/mandrel-catalog.js +36 -0
  25. package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +128 -0
  26. package/.agents/scripts/lib/orchestration/column-sync.js +88 -9
  27. package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +183 -0
  28. package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +20 -2
  29. package/.agents/scripts/lib/orchestration/project-meta-cache.js +238 -0
  30. package/.agents/scripts/lib/orchestration/reassert-status-column.js +3 -1
  31. package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -2
  32. package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +80 -14
  33. package/.agents/scripts/lib/orchestration/single-story-close/runner.js +74 -25
  34. package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +10 -1
  35. package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +48 -1
  36. package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +148 -4
  37. package/.agents/scripts/lib/orchestration/ticketing/transition.js +8 -1
  38. package/.agents/scripts/lib/story-body/story-body.js +76 -7
  39. package/.agents/scripts/lib/story-init/branch-initializer.js +29 -43
  40. package/.agents/scripts/lib/story-init/hierarchy-tracer.js +25 -4
  41. package/.agents/scripts/lib/story-init/task-graph-builder.js +22 -12
  42. package/.agents/scripts/lib/templates/decomposer-prompts.js +23 -0
  43. package/.agents/scripts/lib/validation-evidence.js +63 -25
  44. package/.agents/scripts/lib/worktree/node-modules-strategy.js +239 -31
  45. package/.agents/scripts/providers/github/branch-protection.js +1 -1
  46. package/.agents/scripts/providers/github/errors.js +53 -2
  47. package/.agents/scripts/providers/github/labels.js +1 -1
  48. package/.agents/scripts/providers/github/projects-v2-graphql.js +1 -1
  49. package/.agents/scripts/resync-status-column.js +5 -0
  50. package/.agents/scripts/run-coverage.js +85 -45
  51. package/.agents/scripts/run-lint.js +11 -0
  52. package/.agents/scripts/single-story-init.js +22 -29
  53. package/.agents/scripts/story-init.js +38 -63
  54. package/.agents/scripts/story-phase.js +46 -4
  55. package/.agents/scripts/sync-claude-commands.js +112 -29
  56. package/.agents/scripts/update-maintainability-baseline.js +19 -76
  57. package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +5 -3
  58. package/.agents/workflows/helpers/acceptance-self-eval.md +27 -0
  59. package/.agents/workflows/helpers/deliver-epic.md +19 -2
  60. package/.agents/workflows/helpers/epic-deliver-story.md +50 -14
  61. package/.agents/workflows/helpers/single-story-deliver.md +12 -0
  62. package/.agents/workflows/loops/README.md +65 -0
  63. package/.agents/workflows/loops/fix-failing-tests.md +74 -0
  64. package/.agents/workflows/loops/nightly-audit.md +71 -0
  65. package/.agents/workflows/loops/watch-ci.md +68 -0
  66. package/docs/CHANGELOG.md +51 -0
  67. package/package.json +1 -1
  68. package/.agents/scripts/providers/github/transient-retry.js +0 -62
@@ -0,0 +1,68 @@
1
+ ---
2
+ description: >-
3
+ Interval watch loop that polls a pull request's CI checks until they settle.
4
+ Each round runs `gh pr checks` and reports the delta; the host (`/loop 5m`)
5
+ owns the cadence and re-invokes the unit on its schedule. verify is optional
6
+ for an interval loop — the externally-scheduled host owns iteration, so this
7
+ unit ships the action and goal, not a terminating oracle.
8
+ loop:
9
+ cadence: interval
10
+ goal: >-
11
+ Keep an eye on the current pull request's required CI checks each interval,
12
+ surfacing the first failed or stuck check the moment it appears so a human
13
+ can act before the checks finish.
14
+ maxRounds: 60
15
+ onExhaust: report
16
+ ---
17
+
18
+ # /loops:watch-ci — poll a PR's checks until they settle
19
+
20
+ An **interval watch loop**. The host (`/loop <interval> /loops:watch-ci`, e.g.
21
+ `/loop 5m …`) owns the cadence and re-runs this unit on each tick. Because the
22
+ host schedules iteration externally, this unit carries **no `verify` oracle** —
23
+ per the loop-unit schema, `verify` is required only for `self-paced` cadence and
24
+ optional for `interval` / `cron`. The unit's job each round is to observe and
25
+ report, not to converge.
26
+
27
+ ## Action
28
+
29
+ Each interval:
30
+
31
+ 1. **Read the current check state.** Run `gh pr checks` for the PR under watch
32
+ (the host supplies the PR number, or it is inferred from the current
33
+ branch's open PR). Capture the per-check status: pending, passed, or failed.
34
+ 2. **Compute the delta since last round.** Compare against the prior round's
35
+ snapshot. A check that flipped `pending → failed` is the headline; a check
36
+ that flipped `pending → passed` is progress.
37
+ 3. **Surface failures immediately.** On the first failed or cancelled required
38
+ check, report it — name the check, link the run, and quote the first error
39
+ line if cheaply available — so a human can act before the rest of the matrix
40
+ finishes. Do not wait for the whole suite to settle to raise a red check.
41
+ 4. **Report and yield.** Emit a one-line status summary
42
+ (`N passed, M pending, K failed`) and return control to the host, which
43
+ sleeps until the next interval.
44
+
45
+ ## Goal & done-signal
46
+
47
+ - **Goal:** the operator learns about a CI failure on the watched PR as early as
48
+ the polling interval allows, and knows when all required checks have gone
49
+ green.
50
+ - **Done-signal:** all required checks have a terminal status (every check
51
+ passed, or at least one has failed). An interval loop has no self-evaluated
52
+ oracle — the host stops the loop when the operator cancels it, when a failure
53
+ is surfaced and acted on, or when `maxRounds` is reached.
54
+ - **Backstop:** `maxRounds: 60`. At a 5-minute interval that is ~5 hours of
55
+ watching; `onExhaust: report` emits a final status and stops rather than
56
+ polling forever on a wedged check.
57
+
58
+ ## Stop & escalate
59
+
60
+ - **A required check failed.** Surface it and let the operator decide whether to
61
+ keep watching the remaining checks or stop. A failed required check is the
62
+ signal the watch existed to catch.
63
+ - **The PR cannot be resolved** (no open PR for the branch, `gh` not
64
+ authenticated, the PR was merged or closed out from under the watch). Report
65
+ the condition and stop — there is nothing left to watch.
66
+ - **`maxRounds` is reached with checks still pending.** Emit a final summary of
67
+ the stuck checks (`onExhaust: report`) so the operator can investigate the
68
+ wedged run.
package/docs/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.78.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.77.0...mandrel-v1.78.0) (2026-06-24)
6
+
7
+
8
+ ### Added
9
+
10
+ * Epic [#4284](https://github.com/dsj1984/mandrel/issues/4284) ([#4297](https://github.com/dsj1984/mandrel/issues/4297)) ([0f7c543](https://github.com/dsj1984/mandrel/commit/0f7c543e1ceb1c38ac425c388e338e3bfaaec49e))
11
+
12
+
13
+ ### Fixed
14
+
15
+ * **baselines:** apply ignoreGlobs in the maintainability baseline CLI diff-scope branch (refs [#4293](https://github.com/dsj1984/mandrel/issues/4293)) ([#4294](https://github.com/dsj1984/mandrel/issues/4294)) ([c0c7dd5](https://github.com/dsj1984/mandrel/commit/c0c7dd5948314800e5a9d5808fb5eb0b01b09833))
16
+ * **close-validation:** treat biome "No files were processed" as a clean format-gate skip (refs [#4292](https://github.com/dsj1984/mandrel/issues/4292)) ([#4295](https://github.com/dsj1984/mandrel/issues/4295)) ([bf6840d](https://github.com/dsj1984/mandrel/commit/bf6840dd69abbece4eb221797e6432519b6614a0))
17
+
18
+
19
+ ### Changed
20
+
21
+ * **github-provider:** unify the two divergent withTransientRetry into one canonical primitive (refs [#4298](https://github.com/dsj1984/mandrel/issues/4298)) ([#4299](https://github.com/dsj1984/mandrel/issues/4299)) ([e5668dc](https://github.com/dsj1984/mandrel/commit/e5668dcca73b37c37d38e52e1eb8d2930a0e1790))
22
+
23
+ ## [1.77.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.76.0...mandrel-v1.77.0) (2026-06-24)
24
+
25
+
26
+ ### Added
27
+
28
+ * **close-validation:** unify evidence keyspace — critic verify[] sharing + standalone parity (refs [#4250](https://github.com/dsj1984/mandrel/issues/4250)) ([#4266](https://github.com/dsj1984/mandrel/issues/4266)) ([0555f3b](https://github.com/dsj1984/mandrel/commit/0555f3b0aaeabfb09c2ab02ed2bf7f5182a96c28))
29
+ * **decompose-gates:** emit a deterministic soft missing-reason-to-exist finding + document reason_to_exist in the author SKILL (refs [#4273](https://github.com/dsj1984/mandrel/issues/4273)) ([#4280](https://github.com/dsj1984/mandrel/issues/4280)) ([98d3d5b](https://github.com/dsj1984/mandrel/commit/98d3d5b3d4ba8aa1c5485f5728e44bda09a6f3d3))
30
+ * **decompose:** single-source the binding-vs-advisory authoring altitude + New-File Contract into the decomposer prompt (refs [#4272](https://github.com/dsj1984/mandrel/issues/4272)) ([#4279](https://github.com/dsj1984/mandrel/issues/4279)) ([3fc9adb](https://github.com/dsj1984/mandrel/commit/3fc9adb848226e4ef90463e31d5cbabb3f350c7f))
31
+ * **deliver:** add --epic/--branch flags to story-phase.js and drop duplicate closing snapshot (refs [#4256](https://github.com/dsj1984/mandrel/issues/4256)) ([#4261](https://github.com/dsj1984/mandrel/issues/4261)) ([0f75eda](https://github.com/dsj1984/mandrel/commit/0f75eda48568d136803cd714ab0fd04bc341033d))
32
+ * overhaul worktree install: copy-on-write node_modules + correct, PM-aware retry ([#4249](https://github.com/dsj1984/mandrel/issues/4249)) ([#4267](https://github.com/dsj1984/mandrel/issues/4267)) ([ce92a71](https://github.com/dsj1984/mandrel/commit/ce92a71b47a7851a04ce4d48ce6fa16c9ea41988))
33
+ * persist Projects v2 board metadata to a disk cache ([#4252](https://github.com/dsj1984/mandrel/issues/4252)) ([#4263](https://github.com/dsj1984/mandrel/issues/4263)) ([277d9ec](https://github.com/dsj1984/mandrel/commit/277d9ecd8ac05fa291bb873a57966fc67d768205))
34
+ * **story-body:** add optional advisory non_goals negative-scope section (refs [#4275](https://github.com/dsj1984/mandrel/issues/4275)) ([#4281](https://github.com/dsj1984/mandrel/issues/4281)) ([e118482](https://github.com/dsj1984/mandrel/commit/e118482aa5aa6ae2874c1da9d470d8c53b26bdf8))
35
+
36
+
37
+ ### Fixed
38
+
39
+ * **decompose-gates:** parse string Story bodies in sizing/conflict gates (refs [#4271](https://github.com/dsj1984/mandrel/issues/4271)) ([#4277](https://github.com/dsj1984/mandrel/issues/4277)) ([6c804a4](https://github.com/dsj1984/mandrel/commit/6c804a4b6645f0742ba21ef145a5dc5a351b21bb))
40
+ * rebuild the audit-to-stories Story body to the canonical inline-contract shape and gate it before issue creation ([#4270](https://github.com/dsj1984/mandrel/issues/4270)) ([#4278](https://github.com/dsj1984/mandrel/issues/4278)) ([058ff7b](https://github.com/dsj1984/mandrel/commit/058ff7b13ec799c11ed2ddf134adaf82c289168f))
41
+ * **single-story-close:** release assignee-lease on recoverable-blocked exits (refs [#4257](https://github.com/dsj1984/mandrel/issues/4257)) ([#4268](https://github.com/dsj1984/mandrel/issues/4268)) ([108fa5f](https://github.com/dsj1984/mandrel/commit/108fa5f834121bf53c5cb59005415ea30b6e990f))
42
+ * **story-body:** close section on unrecognized heading to stop content bleed (refs [#4274](https://github.com/dsj1984/mandrel/issues/4274)) ([#4276](https://github.com/dsj1984/mandrel/issues/4276)) ([808d508](https://github.com/dsj1984/mandrel/commit/808d5080fee460f24cae8ef43b2c8c1b442de1f3))
43
+
44
+
45
+ ### Performance
46
+
47
+ * **coverage:** make run-coverage.js host-aware for --test-concurrency (refs [#4254](https://github.com/dsj1984/mandrel/issues/4254)) ([#4260](https://github.com/dsj1984/mandrel/issues/4260)) ([0078195](https://github.com/dsj1984/mandrel/commit/0078195afeee50fafefdaf4dea3d59b33b172cc6))
48
+ * **deliver:** resolve Epic once and thread --prd/--tech-spec into story-init (refs [#4253](https://github.com/dsj1984/mandrel/issues/4253)) ([#4269](https://github.com/dsj1984/mandrel/issues/4269)) ([8dbc006](https://github.com/dsj1984/mandrel/commit/8dbc006882379ccdfb5dca90d8d8bca11c2dbb0e))
49
+ * **story-init:** short-circuit childless 2-tier sub-issues + Search probe in buildTaskGraph (refs [#4251](https://github.com/dsj1984/mandrel/issues/4251)) ([#4265](https://github.com/dsj1984/mandrel/issues/4265)) ([7c8fcd2](https://github.com/dsj1984/mandrel/commit/7c8fcd21a9c781bd2e4592d7c8e7b4bf8476cd22))
50
+
51
+
52
+ ### Changed
53
+
54
+ * **story-init:** single-home the duplicated branch-seed action switch (refs [#4255](https://github.com/dsj1984/mandrel/issues/4255)) ([#4264](https://github.com/dsj1984/mandrel/issues/4264)) ([7a6f2ec](https://github.com/dsj1984/mandrel/commit/7a6f2ec9c2e7ce0b680c620e9bb52541ec37db1b))
55
+
5
56
  ## [1.76.0](https://github.com/dsj1984/mandrel/compare/mandrel-v1.75.0...mandrel-v1.76.0) (2026-06-20)
6
57
 
7
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mandrel",
3
- "version": "1.76.0",
3
+ "version": "1.78.0",
4
4
  "description": "Claude Code-first opinionated workflow framework: instructions, personas, skills, and SDLC workflows that govern AI coding assistants.",
5
5
  "files": [
6
6
  ".agents/",
@@ -1,62 +0,0 @@
1
- /**
2
- * transient-retry — shared retry-with-backoff for GitHub provider calls.
3
- *
4
- * Resilience for flaky connections (e.g. cell hotspots): retry ONLY on
5
- * connectivity blips, never on auth / scope / not-found / already-exists /
6
- * validation errors. Works for both surfaces the provider uses:
7
- * - the gh CLI path — errors carry the Go HTTP error on `err.stderr`
8
- * (e.g. `dial tcp ...: i/o timeout`); and
9
- * - the direct `fetch` path — errors are a `TypeError: fetch failed` with
10
- * the real reason on `err.cause` (e.g. `ETIMEDOUT`, `ENOTFOUND`).
11
- *
12
- * Retrying a non-idempotent create is acceptable for the dominant hotspot
13
- * failure (`dial tcp ... i/o timeout` means the connection never opened, so
14
- * the request never reached GitHub). Callers still gate retry per call so
15
- * the genuinely non-idempotent project-create can opt out.
16
- */
17
-
18
- const TRANSIENT_RE =
19
- /i\/o timeout|dial tcp|TLS handshake timeout|connection reset|connection refused|temporary failure|could not resolve host|no such host|network is unreachable|socket hang up|fetch failed|ConnectTimeoutError|UND_ERR_CONNECT_TIMEOUT|ETIMEDOUT|ECONNRESET|ECONNREFUSED|ENOTFOUND|EAI_AGAIN|\b50[234]\b/i;
20
-
21
- /** True when an error looks like a retryable network/connectivity blip. */
22
- export function isTransientNetworkError(err) {
23
- const hay = [
24
- err?.stderr,
25
- err?.message,
26
- err?.code,
27
- err?.cause?.message,
28
- err?.cause?.code,
29
- ]
30
- .filter(Boolean)
31
- .join(' ');
32
- return TRANSIENT_RE.test(hay);
33
- }
34
-
35
- /**
36
- * Run `fn`, retrying with exponential backoff ONLY on transient network
37
- * errors (1s, 2s, 4s by default). Non-transient errors throw immediately so
38
- * real failures stay loud. `sleep` is injectable for tests.
39
- *
40
- * @template T
41
- * @param {() => Promise<T>} fn
42
- * @param {{ retries?: number, baseDelayMs?: number,
43
- * sleep?: (ms: number) => Promise<void> }} [opts]
44
- * @returns {Promise<T>}
45
- */
46
- export async function withTransientRetry(
47
- fn,
48
- {
49
- retries = 3,
50
- baseDelayMs = 1000,
51
- sleep = (ms) => new Promise((r) => setTimeout(r, ms)),
52
- } = {},
53
- ) {
54
- for (let attempt = 0; ; attempt++) {
55
- try {
56
- return await fn();
57
- } catch (err) {
58
- if (attempt >= retries || !isTransientNetworkError(err)) throw err;
59
- await sleep(baseDelayMs * 2 ** attempt);
60
- }
61
- }
62
- }