mandrel 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +59 -28
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +41 -46
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +51 -44
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +32 -56
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +14 -16
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/agentrc.schema.json +34 -45
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/audit-suite/selector.js +323 -23
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
- package/.agents/scripts/lib/checks/index.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli-args.js +23 -2
- package/.agents/scripts/lib/close-validation/gates.js +13 -13
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/delivery-routing.js +7 -6
- package/.agents/scripts/lib/config/explain.js +10 -16
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +15 -25
- package/.agents/scripts/lib/config/quality.js +11 -14
- package/.agents/scripts/lib/config/runners.js +8 -21
- package/.agents/scripts/lib/config/temp-paths.js +18 -56
- package/.agents/scripts/lib/config-settings-schema-delivery.js +31 -13
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
- package/.agents/scripts/lib/config-settings-schema.js +48 -22
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/duplicate-search.js +38 -7
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -189
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
- package/.agents/scripts/lib/git-utils.js +0 -14
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +0 -15
- package/.agents/scripts/lib/label-taxonomy.js +1 -12
- package/.agents/scripts/lib/observability/active-story-env.js +42 -163
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +2 -2
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
- package/.agents/scripts/lib/orchestration/code-review.js +58 -168
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +114 -24
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +3 -7
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -75
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
- package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
- package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +3 -12
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -32
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
- package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
- package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
- package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
- package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -286
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +8 -7
- package/.agents/scripts/lib/story-body/story-body.js +6 -5
- package/.agents/scripts/lib/templates/decomposer-prompts.js +7 -3
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/ticket-body-sections.js +0 -14
- package/.agents/scripts/lib/validation-evidence.js +31 -59
- package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/plan-context.js +38 -6
- package/.agents/scripts/plan-persist.js +145 -35
- package/.agents/scripts/plan-run-epilogue.js +83 -38
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +1 -11
- package/.agents/scripts/providers/github/mappers.js +5 -0
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -153
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +9 -10
- package/.agents/scripts/single-story-close.js +173 -18
- package/.agents/scripts/single-story-confirm-merge.js +288 -15
- package/.agents/scripts/single-story-init.js +6 -10
- package/.agents/scripts/stories-wave-tick.js +79 -4
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/update-ticket-state.js +8 -50
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
- package/.agents/skills/core/scope-triage/SKILL.md +3 -0
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/workflows/audit-architecture.md +3 -4
- package/.agents/workflows/audit-clean-code.md +4 -4
- package/.agents/workflows/audit-documentation.md +4 -5
- package/.agents/workflows/audit-lighthouse.md +8 -0
- package/.agents/workflows/audit-navigability.md +10 -0
- package/.agents/workflows/audit-performance.md +2 -3
- package/.agents/workflows/audit-quality.md +8 -9
- package/.agents/workflows/audit-security.md +1 -2
- package/.agents/workflows/audit-seo.md +10 -0
- package/.agents/workflows/audit-ux-ui.md +7 -0
- package/.agents/workflows/deliver.md +98 -45
- package/.agents/workflows/git-cleanup.md +2 -2
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +21 -13
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +12 -10
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +193 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +184 -19
- package/.agents/workflows/qa-assist.md +6 -6
- package/.agents/workflows/qa-explore.md +3 -3
- package/.agents/workflows/qa-run.md +1 -5
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +40 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -9
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/package.json +1 -1
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -53
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
- package/.agents/scripts/resolve-plan-run.js +0 -117
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* hook-heartbeat.js — Epic #4476 (M5): heartbeats OFF the token stream.
|
|
3
|
-
*
|
|
4
|
-
* Before M5, the `story.heartbeat` / `slice.heartbeat` forward-progress
|
|
5
|
-
* signal that idle-watchdog consumers read from the lifecycle ledger was an
|
|
6
|
-
* **LLM obligation**: the delivery workflow instructed the agent to run
|
|
7
|
-
* `story-phase.js` / `slice-phase.js --event heartbeat` at least once per
|
|
8
|
-
* meaningful step, and every such call was a full-priced LLM turn re-reading
|
|
9
|
-
* ~100k of cached context just to append one NDJSON line.
|
|
10
|
-
*
|
|
11
|
-
* This module makes that liveness signal a **free byproduct of the agent
|
|
12
|
-
* doing any work**. It is invoked from the existing PostToolUse trace hook
|
|
13
|
-
* (`tool-trace-hook.js`), so every tool call the agent makes refreshes the
|
|
14
|
-
* heartbeat — no dedicated bookkeeping turn required. The record it appends is
|
|
15
|
-
* byte-for-byte the same `story.heartbeat` / `slice.heartbeat` shape the
|
|
16
|
-
* watchdog already consumes (emitted through the same
|
|
17
|
-
* `emit-story-heartbeat.js` / `emit-slice-lifecycle.js` emitters), so the
|
|
18
|
-
* watchdog needs no change: the signal is preserved, only its emission
|
|
19
|
-
* mechanism moves off the token stream.
|
|
20
|
-
*
|
|
21
|
-
* ## Robustness contract (mirrors `tool-trace-hook.js`)
|
|
22
|
-
* - **No-op outside an active Story with an Epic-scoped ledger.** When a
|
|
23
|
-
* valid `{ CC_EPIC_ID, CC_STORY_ID }` pair is not present in the
|
|
24
|
-
* environment (v2 standalone Stories omit `CC_EPIC_ID`), `emitHeartbeatFromHook`
|
|
25
|
-
* returns without touching the filesystem.
|
|
26
|
-
* - **Best-effort.** Every failure is swallowed. A heartbeat is
|
|
27
|
-
* observability, not state; a hook must never block tool execution.
|
|
28
|
-
* - **Throttled across processes.** Command hooks are spawned as fresh
|
|
29
|
-
* `node` processes per tool call, so in-process state cannot throttle. The
|
|
30
|
-
* throttle is anchored to the **mtime of a sidecar marker file** under the
|
|
31
|
-
* Epic temp dir: at most one heartbeat per `HEARTBEAT_MIN_INTERVAL_MS`
|
|
32
|
-
* lands, regardless of how many tool calls fire in that window. This keeps
|
|
33
|
-
* the ledger from ballooning while still refreshing liveness faster than
|
|
34
|
-
* the watchdog's threshold.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
import { statSync, utimesSync, writeFileSync } from 'node:fs';
|
|
38
|
-
import path from 'node:path';
|
|
39
|
-
|
|
40
|
-
import { epicTempDir } from '../config/temp-paths.js';
|
|
41
|
-
import { emitStoryHeartbeat } from '../orchestration/lifecycle/emit-story-heartbeat.js';
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Default minimum wall-clock gap between two hook-emitted heartbeats for the
|
|
45
|
-
* same target. Comfortably below the watchdog's default 30-minute staleness
|
|
46
|
-
* threshold, so an actively-working agent never trips a false stall, while
|
|
47
|
-
* still bounding ledger growth to ~1 line/minute of activity.
|
|
48
|
-
*/
|
|
49
|
-
const HEARTBEAT_MIN_INTERVAL_MS = 60_000;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Resolve the heartbeat target from the active-Story / active-slice env vars.
|
|
53
|
-
*
|
|
54
|
-
* A `story.heartbeat` requires a parent `epicId` (its schema pins
|
|
55
|
-
* `epicId >= 1`), so a **standalone** Story (`CC_STORY_ID` set, `CC_EPIC_ID`
|
|
56
|
-
* absent) yields `null` — there is no Epic-scoped ledger to write to and the
|
|
57
|
-
* standalone path is not watched by the Epic idle watchdog. The trace hook
|
|
58
|
-
* still records that context's traces; only the heartbeat is skipped.
|
|
59
|
-
*
|
|
60
|
-
* Inert `slice.*` lifecycle (and `CC_SLICE_ID` heartbeats) were removed in
|
|
61
|
-
* the v2 ceremony lock-in — `## Slicing` is prose-only.
|
|
62
|
-
*
|
|
63
|
-
* @param {NodeJS.ProcessEnv} [env]
|
|
64
|
-
* @returns {{ kind: 'story', epicId: number, storyId: number, operator?: string }
|
|
65
|
-
* | null}
|
|
66
|
-
*/
|
|
67
|
-
export function resolveHeartbeatTarget(env = process.env) {
|
|
68
|
-
const epicRaw = env.CC_EPIC_ID;
|
|
69
|
-
const storyRaw = env.CC_STORY_ID;
|
|
70
|
-
const operator =
|
|
71
|
-
typeof env.CC_OPERATOR === 'string' && env.CC_OPERATOR.length > 0
|
|
72
|
-
? env.CC_OPERATOR
|
|
73
|
-
: undefined;
|
|
74
|
-
|
|
75
|
-
const epicId = epicRaw ? Number.parseInt(epicRaw, 10) : Number.NaN;
|
|
76
|
-
const epicOk = Number.isInteger(epicId) && epicId > 0;
|
|
77
|
-
|
|
78
|
-
if (storyRaw) {
|
|
79
|
-
const storyId = Number.parseInt(storyRaw, 10);
|
|
80
|
-
if (!Number.isInteger(storyId) || storyId <= 0) return null;
|
|
81
|
-
// story.heartbeat has no meaning without a parent Epic ledger.
|
|
82
|
-
if (!epicOk) return null;
|
|
83
|
-
return {
|
|
84
|
-
kind: 'story',
|
|
85
|
-
epicId,
|
|
86
|
-
storyId,
|
|
87
|
-
...(operator ? { operator } : {}),
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Stable, filesystem-safe marker basename for a target. The marker's mtime is
|
|
96
|
-
* the throttle anchor; its contents are irrelevant.
|
|
97
|
-
*
|
|
98
|
-
* @param {{ kind: string, storyId?: number, sliceId?: string }} target
|
|
99
|
-
* @returns {string}
|
|
100
|
-
*/
|
|
101
|
-
export function heartbeatMarkerName(target) {
|
|
102
|
-
return `.heartbeat-story-${target.storyId}`;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Throttle predicate. Returns `true` when no marker exists yet, or the marker
|
|
107
|
-
* is older than `intervalMs`. Any stat error (missing file / unreadable) is
|
|
108
|
-
* treated as "emit" — the safe direction for a liveness signal.
|
|
109
|
-
*
|
|
110
|
-
* @param {{ markerPath: string, now: Date, intervalMs: number, statFn?: typeof statSync }} args
|
|
111
|
-
* @returns {boolean}
|
|
112
|
-
*/
|
|
113
|
-
export function shouldEmitHeartbeat({
|
|
114
|
-
markerPath,
|
|
115
|
-
now,
|
|
116
|
-
intervalMs,
|
|
117
|
-
statFn = statSync,
|
|
118
|
-
}) {
|
|
119
|
-
try {
|
|
120
|
-
const st = statFn(markerPath);
|
|
121
|
-
return now.getTime() - st.mtimeMs >= intervalMs;
|
|
122
|
-
} catch {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Update (or create) the throttle marker so its mtime is `now`. Best-effort:
|
|
129
|
-
* a failure here at worst lets the next tool call emit a second heartbeat.
|
|
130
|
-
*
|
|
131
|
-
* @param {string} markerPath
|
|
132
|
-
* @param {Date} now
|
|
133
|
-
*/
|
|
134
|
-
function touchMarker(markerPath, now) {
|
|
135
|
-
try {
|
|
136
|
-
writeFileSync(markerPath, '', { flag: 'a' });
|
|
137
|
-
utimesSync(markerPath, now, now);
|
|
138
|
-
} catch {
|
|
139
|
-
// swallow — throttle degrades to "emit again next call", never fatal.
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Emit one throttled heartbeat for the active target, if any. The whole body
|
|
145
|
-
* is best-effort: any failure returns a `{ emitted: false }` envelope rather
|
|
146
|
-
* than throwing, so the calling PostToolUse hook never blocks the tool.
|
|
147
|
-
*
|
|
148
|
-
* @param {object} [opts]
|
|
149
|
-
* @param {NodeJS.ProcessEnv} [opts.env] Defaults to `process.env`.
|
|
150
|
-
* @param {Date} [opts.now] Injected clock (tests).
|
|
151
|
-
* @param {number} [opts.intervalMs] Throttle window.
|
|
152
|
-
* @param {object} [opts.config] Resolved config (tempRoot).
|
|
153
|
-
* @returns {{ emitted: boolean, reason?: string, kind?: string, ledgerPath?: string }}
|
|
154
|
-
*/
|
|
155
|
-
export function emitHeartbeatFromHook({
|
|
156
|
-
env = process.env,
|
|
157
|
-
now = new Date(),
|
|
158
|
-
intervalMs = HEARTBEAT_MIN_INTERVAL_MS,
|
|
159
|
-
config,
|
|
160
|
-
} = {}) {
|
|
161
|
-
try {
|
|
162
|
-
const target = resolveHeartbeatTarget(env);
|
|
163
|
-
if (!target) return { emitted: false, reason: 'no-target' };
|
|
164
|
-
|
|
165
|
-
const markerPath = path.join(
|
|
166
|
-
epicTempDir(target.epicId, config),
|
|
167
|
-
heartbeatMarkerName(target),
|
|
168
|
-
);
|
|
169
|
-
if (!shouldEmitHeartbeat({ markerPath, now, intervalMs })) {
|
|
170
|
-
return { emitted: false, reason: 'throttled', kind: target.kind };
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const timestamp = now.toISOString();
|
|
174
|
-
const res = emitStoryHeartbeat({
|
|
175
|
-
epicId: target.epicId,
|
|
176
|
-
storyId: target.storyId,
|
|
177
|
-
phase: 'implementing',
|
|
178
|
-
timestamp,
|
|
179
|
-
...(target.operator ? { operator: target.operator } : {}),
|
|
180
|
-
config: config ?? undefined,
|
|
181
|
-
});
|
|
182
|
-
touchMarker(markerPath, now);
|
|
183
|
-
return { emitted: true, kind: target.kind, ledgerPath: res?.ledgerPath };
|
|
184
|
-
} catch {
|
|
185
|
-
return { emitted: false, reason: 'error' };
|
|
186
|
-
}
|
|
187
|
-
}
|