feat(decisions): structured ledger + deterministic render for decisions/pitfalls#241
Conversation
…fields Extract formatting logic from json-helper.cjs into decisions-format.cjs (single source of truth for byte-compat output strings). Add render-decisions.cjs as the new pure renderer — renderDecisionsFile(rows, kind) filters/sorts/emits from ledger rows, supports raw_body verbatim passthrough for migrated entries, and provides render+--check CLI ops with mkdir-based atomic locking. Extend LearningObservation with optional ledger fields (anchor_id, date, decisions_status, amendments, raw_body) and update isLearningObservation to validate them. Update merge-observation passthrough to preserve new fields. 86 new tests; all 1628 tests green. Co-Authored-By: Claude <noreply@anthropic.com>
…ledger ops Implements Phase 3 of the decisions-ledger-render plan: - assign-anchor <type> <obs_id>: assigns next ADR/PF number from anchored ledger (max+1 incl. Retired), writes anchored row to decisions-ledger.jsonl, marks log row as created, registers usage entry, re-renders both .md files. Entire op is atomic under a single .decisions.lock acquisition (no deadlock). O(anchored) — single pass for max numeric suffix (AC-P2). - retire-anchor <anchor_id> <status>: flips decisions_status on the ledger row (Deprecated|Superseded|Retired). Idempotent. Row otherwise byte-intact. Retired entry vanishes from rendered .md but stays in committed ledger. Numbers with retired anchors are never reused (AC-F5, AC-F7). - rotate-observations [log] [archive]: moves stale observing rows (>30 days, no anchor_id) to decisions-log.archive.jsonl. Never touches anchored or created/ready rows. Runs under .observations.lock (AC-F9). - renderAndWriteAll(worktree, rows): lock-free helper in render-decisions.cjs. Lets assign-anchor/retire-anchor re-render both .md without re-acquiring the lock they already hold. The `render` CLI subcommand now calls it too. - nextAnchorFromLedger / countActiveLedgerRows: new pure helpers in json-helper.cjs. count-active op updated to prefer ledger over .md heading scan; backward compat with legacy file-path callers preserved. - project-paths.cjs: add getDecisionsLedgerPath, getDecisionsArchivePath, getObservationsLockDir. 53 new tests covering AC-A2, AC-A3, AC-F5, AC-F7, AC-F9, AC-P2, AC-P3. All 1681 tests pass.
…gitignore split Phase 4 of the decisions ledger split. Dry-run against live data confirmed 25 anchored, 1 synthesized, 3 retired rows; both .md files byte-identical to originals (except TL;DR Key list). Live data untouched. Key changes: - decisions-ledger-migration.ts: pure lock-aware migration; captures raw_body verbatim for every .md entry; synthesizes ADR-001 (obs_c9d3m1 absent from log); marks hand-deletions (ADR-002/PF-003/PF-005) as Retired; extracts amendments; idempotent; calls bundled renderer (not installed ~/.devflow) - migrations.ts: registers decisions-ledger-unify-v1 and sync-devflow-gitignore-v3 - project-paths.ts + project-paths.cjs: add decisions-ledger.jsonl re-include to .devflow/.gitignore template; add getDecisionsLedgerPath/getDecisionsArchivePath - ensure-devflow-init: sync heredoc with canonical CJS template Applies ADR-001 exception, ADR-008, ADR-012, ADR-017. Avoids PF-007.
…, remove decisions-append Phase 5 of the decisions ledger split. - dream-decisions SKILL.md: rewritten creation bar with abstain-by-default stance, negative examples, positive bar, dedup-before-create rule, and ADR-XOR-PF hard rule (one incident yields exactly one ADR or PF, never both). Confidence is now honest LLM metadata with no numeric gate, applying ADR-008. Iron Law updated: assign-anchor owns numbering; render owns the .md; never hand-edit. - json-helper.cjs: hard-cut decisions-append op and dead helpers nextDecisionsId and buildUpdatedTldr. Zero live callers remain. assign-anchor is the sole promotion writer. - tests/decisions/decisions-format.test.ts: decisions-append CLI tests rewritten as assign-anchor flow tests; added 9 SKILL content-presence assertions covering AC-F1/AC-F2 contract, ADR-008 no-gate, Iron Law, XOR rule, abstain stance, dedup, and negative examples. - docs-framework SKILL.md, decisions-format.cjs, observation-io.ts: stale comments updated to reference assign-anchor instead of decisions-append. Applies ADR-008. Avoids PF-007.
Phase 6 of the decisions-ledger-render refactor. Changes: dream-curation SKILL.md - full rewrite: - Iron Law "RETIRE BY STATUS - THE LEDGER IS THE SOURCE OF TRUTH" - Replace 3-call lock/Edit dance with retire-anchor, which self-locks, flips decisions_status on the ledger, and re-renders - Wire rotate-observations as first step in curation pass (under .observations.lock, never .decisions.lock - per ADR-017) - 7-day protection window keyed off ledger date field, not .md content - ADR-XOR-PF awareness and dedup guidance mirrored from dream-decisions - Recoverability documented (AC-F6: flip status back + render restores) - assign-anchor adds entries; curation flips status only observation-io.ts - remove dead updateDecisionsStatus: - Function had zero callers at time of removal - .md files are pure renders; status changes go through retire-anchor - Removal note in file header explains migration path legacy-decisions-purge.ts - add ordering + deprecation comment: - Documents that this file operates on PRE-LEDGER .md files - Clarifies it runs BEFORE decisions-ledger-unify-v1 (ordering preserved) - Marks it superseded for future purge operations tests/decisions/dream-curation.test.ts - new file, 31 tests: - SKILL content assertions: Iron Law, retire-anchor, rotation step, no direct .md edit, ADR-XOR-PF, dedup, 7-day window, recoverability - AC-F4: renderDecisionsFile excludes Deprecated/Superseded/Retired - AC-F5: retire-anchor hides entry from .md, keeps Retired in ledger - AC-F6: re-activate + render restores entry identically - AC-F7: retired numbers never reused - AC-F9: rotation contract - anchored rows never archived tests/learning/review-command.test.ts - migrate tests: - Remove 5 tests asserting direct .md editing via updateDecisionsStatus - Add 1 test confirming the function is no longer exported Applies ADR-008, ADR-017. All 1737 tests pass. Co-Authored-By: Claude <noreply@anthropic.com>
…By: Claude <noreply@anthropic.com>
…s/curation/knowledge Implements Phase 7 of the decisions ledger split: - scripts/hooks/dream-commit: deterministic plumbing helper that creates chore(dream): commits after successful Dream writes. Stages only allowed .devflow/ paths. Never git add -A. Skips safely during rebase/merge/ cherry-pick/detached-HEAD and when clean. Config gate: autoCommit in .devflow/dream/config.json (default ON). - shared/skills/dream-decisions/SKILL.md: run dream-commit after assign-anchor succeeds and .decisions.lock is released. - shared/skills/dream-curation/SKILL.md: run dream-commit after all retire-anchor calls complete. - shared/skills/dream-knowledge/SKILL.md: run dream-commit after all slugs refreshed. - src/cli/utils/dream-config.ts: adds autoCommit: boolean to DreamConfig interface (default ON). - src/cli/commands/decisions.ts: --status reports auto-commit state (ON/OFF). - src/cli/commands/init.ts: preserves existing autoCommit value on reinit. - tests/decisions/dream-commit.test.ts: 50 tests covering format/trailers, path scope, no-op, safety rails, config gate, SKILL wiring assertions, and DreamConfig autoCommit key/default. Applies ADR-008 (dream-commit is deterministic plumbing, no LLM judgment). Applies ADR-012 (.devflow artifacts committed as shared team knowledge). Avoids PF-007 (edited scripts/hooks/ source, not installed copies). Co-Authored-By: Claude <noreply@anthropic.com>
…back
Phase 8 cleanup unlocked by the one-tier render model:
1. decisions-index.cjs: remove filterDecisionsContext, isDeprecatedOrSuperseded,
and the Deprecated/Superseded filter call in extractIndexEntries. The renderer
(render-decisions.cjs) guarantees .md files only contain active entries, so
in-memory filtering is dead code. KNOWN_STATUSES trimmed to [Active, Accepted]
for the formatEntryLine tag; filterDecisionsContext removed from module.exports.
2. json-helper.cjs: remove countActiveHeadings function and the legacy .md-file-
path fallback branch in count-active (the .endsWith('.md') / isFile() detection
+ the legacy read path). count-active now reads exclusively from
decisions-ledger.jsonl via countActiveLedgerRows. Remove countActiveHeadings
from module.exports.
3. Tests rewritten (net zero delta — 1787 tests before and after):
- index-generator.test.ts: replace filterDecisionsContext import and the two
"strips Deprecated/Superseded" tests with active-only contract tests; replace
"returns (none) when all Deprecated" with empty-file variant.
- decisions-citation.test.ts: replace the 8 filterDecisionsContext unit tests
with active-only contract tests + "filterDecisionsContext not exported" guard.
- review-command.test.ts: replace 3 legacy .md-path count-active tests with
3 ledger-based count-active tests.
AC-A4: index output byte-identical for active-only input (verified before/after).
AC-A8 grep: zero live callers remain; only prohibition text + op-rejection test.
Applies ADR-008 (deterministic plumbing, no LLM judgment in filter removal).
Avoids PF-007 (edited scripts/hooks/ source, not installed copies).
Co-Authored-By: Claude <noreply@anthropic.com>
…de - Extract acquireMkdirLock/releaseLock into scripts/hooks/lib/mkdir-lock.cjs; both json-helper.cjs and render-decisions.cjs had identical copies - Remove the passthrough initDecisionsContent wrapper in json-helper.cjs; import directly from decisions-format.cjs as the canonical source - Fix dollar-EXIT -> dollar-underscore-EXIT variable reference bug in dream-commit debug log - Remove unused consumedObsIds Set in decisions-ledger-migration.ts
Byte-compat (Pillar 6): buildTldrLine([]) now emits 'Key: -->' (single space) so the empty-corpus render is byte-identical to initDecisionsContent's header. Previously emitted 'Key: -->' (two spaces), creating drift between the init header and a freshly-rendered empty file — violating the 'render is the SOLE format authority' contract. Updated decisions-format + render-decisions golden tests. Consistency (Pillar 2 — TS/CJS mirror): add getObservationsLockDir to src/cli/utils/project-paths.ts. This branch added it to the CJS project-paths.cjs but not the TS counterpart, violating the documented 'must mirror exactly' contract. Also closed the parity-test gap that let the drift through: the hardcoded function list omitted the three functions added on this branch (getDecisionsLedgerPath, getDecisionsArchivePath, getObservationsLockDir). Added them and a structural full-export-set parity test that fails fast on any future one-sided addition.
…rash-safety) When migrateDecisionsLedger detected newRowsAdded === 0 it returned early without calling renderAndWriteAll, leaving decisions.md/pitfalls.md stale if a prior run was killed between the atomic ledger write and the render step. A subsequent re-run would see the ledger as complete, skip, and never heal the stale .md files. Fix: when the existing ledger is non-empty and newRowsAdded === 0, acquire .decisions.lock and call renderAndWriteAll from the existing ledger rows, reconciling any stale .md against the committed ledger. An empty ledger is a true no-op (no crash window exists) and returns early as before. Test: adds crash-window-heal test that writes a complete ledger, then overwrites decisions.md with stale content and deletes pitfalls.md, runs migration, asserts both .md are reconciled from the ledger even though newRowsAdded === 0. Co-Authored-By: Claude <noreply@anthropic.com>
…it, not merge-observation)
Runs decisions-ledger-unify-v1 on this repo's own .devflow/decisions/: 25 anchored + 1 synthesized (ADR-001) + 3 retired (ADR-002/PF-003/PF-005), 12 observing rows kept in the (gitignored) log. decisions.md/pitfalls.md bodies byte-identical; only the TL;DR Key line repopulates. Ledger now tracked via the gitignore re-include.
TypeScript: Dead and Divergent Status EnumLines 13 & 54: Problem:
The type has a phantom Fix: Delete the dead type, or consolidate to one source of truth: export type DecisionsEntryStatus = 'Accepted' | 'Active' | 'Deprecated' | 'Superseded' | 'Retired';Reviewers: TypeScript (90%), Architecture (90%), Consistency (90%), Regression PR #241 Review — Architecture, TypeScript, Consistency, Regression |
Architecture: Committed Ledger Leaks Observation MetadataLine 687 (assign-anchor): Ledger rows copy the entire observation record via Problem: The renderer only reads 8 fields ( Live inspection: 28 of 29 ledger rows carry these extra fields. This:
Fix: Project to render-relevant fields only before atomic append. Create a small Reviewer: Architecture | Confidence: 90% | Category: Blocking PR #241 Review — Architecture |
Reliability: CPU-Pegging Busy-Wait on Lock ContentionLines 40–45: Fallback for unavailable const end = Date.now() + 50;
while (Date.now() < end) { /* spin */ }\n```
**Problem**: With a 30s timeout, a contended lock burns a full CPU core for up to 30 seconds during a background Dream run. Violates reliability principle "minimize work in hot paths." The TS counterpart (`mkdir-lock.ts:40`) correctly uses `setTimeout` (truly idle).
**Fix**: Hoist buffer, drop spin, use idle sleep:
```js
// module scope: allocate once\nlet waitBuf = null;\ntry { waitBuf = new Int32Array(new SharedArrayBuffer(4)); } catch { waitBuf = null; }\n\n// in loop: use idle sleep on fallback\nif (waitBuf) {\n Atomics.wait(waitBuf, 0, 0, 50);\n} else {\n require('child_process').execSync('sleep 0.05', { stdio: 'ignore' });\n}\n```
**Reviewer**: Reliability | **Confidence**: 90% | **Category**: Blocking
---
*PR #241 Review — Reliability* |
Reliability: Missing Precondition Assertions on Source-of-Truth WriteLines 641–717 (assign-anchor): No assertions for core invariants before atomic ledger write:
Impact: Silent corruption of the committed source-of-truth ledger. Violation invisible until a human notices duplicate ADR numbers. Fix: Add cheap preconditions inside the lock, before atomic write: Reviewer: Reliability | Confidence: 82% | Category: Blocking PR #241 Review — Reliability |
Code Review Summary: Lower-Confidence & Non-Anchorable FindingsMedium-Confidence Issues (60–80%)
Documentation Drift (Not in Changed Lines)CRITICAL: The PR ships a major subsystem refactor (decisions-append → assign-anchor, append-only → deterministic render) but leaves reference docs dangerously stale: Project CLAUDE.md (lines 43, 49, 160, 162) — Still references removed
This is the highest-traffic onboarding doc; it actively misdirects new contributors. Requires explicit approval before editing. Feature KB Drift (
In-Changed-File Documentation Contradiction (
SummaryThree HIGH blocking issues require fixes before merge:
Type consolidation strongly recommended (three reviewers converge on Documentation drift should be corrected in-PR (CLAUDE.md requires approval; feature KBs can auto-refresh). Overall Status: CHANGES_REQUESTED Claude Code Review — PR #241 |
…e hook-bootstrap, add cross-ref comment Issue 1 (complexity): replace node -e multi-line heredoc with config interpolated into JS source with the single-line json-helper.cjs get-field-file op — path arrives via argv with no in-source interpolation, logic lives in one tested place. Issue 2 (consistency): replace hand-rolled source debug-trace + devflow_debug_init inline with source hook-bootstrap "dream-commit" (the shared pattern). Adds explanatory comment that no set -e is intentional: agent-invoked best-effort, not a registered Claude Code hook. Issue 3 (consistency): add cross-reference comment on AUTO_COMMIT="true" pointing at src/cli/utils/dream-config.ts DEFAULT_CONFIG.autoCommit as the canonical source. All 50 dream-commit tests pass. Co-Authored-By: Claude <noreply@anthropic.com>
…nders index filter Issue 1: The .devflow/.gitignore header comment in project-paths.ts and its CJS mirror listed decisions.md/pitfalls.md but omitted decisions-ledger.jsonl even though the allowlist already re-includes it. Add the missing bullet to both files, keeping them byte-mirrored. Issue 2: decisions-index.cjs relied solely on render-decisions.cjs to exclude Deprecated/Superseded/Retired entries. Add a defense-in-depth INACTIVE_STATUSES guard inside extractIndexEntries so active-only correctness does not rest on the renderer alone. Mirrors the INACTIVE_STATUSES set in render-decisions.cjs. Add regression tests covering Deprecated and Superseded ADR/PF entries and mixed-content cases. avoids PF-007 applies ADR-008 Co-Authored-By: Claude <noreply@anthropic.com>
Issue 1 (reliability/security): hoist SharedArrayBuffer/Int32Array to module scope (allocated once at load time, not per retry). Replace the hot busy-wait spin loop with execSync sleep 0.05 in the fallback path so restricted Dream worker environments no longer peg a CPU core for up to 30 s. CJS/TS parity achieved: both paths now use truly idle sleeps. Issue 2 (security): document the 60 s stale-break TOCTOU window in the acquireMkdirLock JSDoc. Current callers do only synchronous file I/O and complete well under 60 s so the window is not reachable in practice. Add refreshLock(lockDir) export: long-running callers can touch the lock dir mtime periodically to push the stale deadline out by another staleMs interval. Applies ADR-017. Co-Authored-By: Claude <noreply@anthropic.com>
…-timeout + write-path bound batch-6-test-robustness - render-decisions.test.ts + ledger-ops.test.ts: replace vacuous early-return in both AC-P1/AC-P2 perf tests with expect.assertions(2) + an absolute ceiling (medianLarge must be <500ms / <100ms respectively), so an O(N²) regression can never slip through on fast CI where medianSmall < 0.01ms. Raised SMALL/LARGE from 20/200 to 50/500 and RUNS from 5 to 7 to make sub-0.01ms median less likely. Applies ADR-014. - ledger-ops.test.ts: add AC-P2b suite that times full assign-anchor CLI invocations at ~50 vs ~500 seeded ledger rows; absolute ceiling of 10s on the large run is the primary regression guard; ratio check fires only when startup noise is not dominant (smallMs > 200ms). - decisions-ledger-migration.test.ts: add lock-timeout-throw test that pre-holds .decisions.lock via mkdir, then calls migrateDecisionsLedger with timeoutMs:100 and asserts the expected Error is thrown without hanging 30s. Wires timeoutMs option through to acquireMkdirLock in the migration source (minimal change, no behaviour change in production). Co-Authored-By: Claude <noreply@anthropic.com>
… renderer, extracted seams - Issue 1: Remove private interface LedgerRow (loose type?: string / decisions_status?: string). Rename to LogRow for raw decisions-log.jsonl input. Import LedgerRow + DecisionsEntryStatus + DECISIONS_ENTRY_STATUSES from observations.ts for all output/ledger rows. Applies ADR-008 (deterministic plumbing must not re-invent types owned by the data layer). - Issue 2: All synthesized and enriched LedgerRow objects now satisfy the shared LedgerRow type (id, type, pattern, details, anchor_id, decisions_status all required). details defaults to the section title for synthesized rows so the required field is always present. - Issue 3: Validate renderer shape after require() — throws a descriptive error naming the path when renderAndWriteAll export is missing, instead of a bare TypeError at call site (D308). Avoids PF-007 (bundled vs installed path confusion). - Issue 4: Status normalization (normalizeDecisionsStatus) now maps over DECISIONS_ENTRY_STATUSES so Retired, Active, Deprecated, Superseded are preserved verbatim. Unrecognized statuses push a result.warnings entry and fall back to 'Accepted' — no more silent downgrade of Retired entries. Applies ADR-008. - Issue 5: Extract three pure seams — readMigrationInputs(), buildLedgerRows() (with a single synthesizeRow() helper collapsing the two obsId/syntheticId branches by computing id upfront), and writeAndRender(). Top-level migrateDecisionsLedger reads as ~6 named calls. All 369 existing tests green unchanged. - Issue 6: Malformed JSONL lines in the existing ledger (idempotency-heal path) now push a result.warnings entry instead of silently vanishing — surfaces ledger corruption. Co-Authored-By: Claude <noreply@anthropic.com>
…e bullet Batch-5 added the decisions-ledger.jsonl bullet to the gitignore policy comment in project-paths.ts/.cjs but missed the third copy embedded in the ensure-devflow-init heredoc, breaking the byte-equality test (tests/shell-hooks.test.ts 'heredoc matches getDevflowGitignoreContent'). Sync the heredoc to the canonical CJS template. avoids PF-007 Co-Authored-By: Claude <noreply@anthropic.com>
…implifier pass over the resolution fixes: - observations.ts: remove the completed 'migration batch note' to-do from the LedgerRow JSDoc (the private interface it referenced is already gone). - json-helper.cjs: existingArchiveIds let→const (never rebound). Behavior-preserving. tsc clean, decisions + shell-hooks tests green. Co-Authored-By: Claude <noreply@anthropic.com>
…ender model Resolves the 4 documentation-drift findings from the PR #241 review (held for explicit approval per markdown policy): - docs-framework/SKILL.md: decisions.md/pitfalls.md are 'Rendered from decisions-ledger.jsonl (active rows; retired dropped)', not 'Append-only' (fixes the in-file contradiction with the already-updated line 172). - CLAUDE.md: LLM-vs-plumbing + decisions-pipeline sections and the .devflow tree now describe assign-anchor/retire-anchor/render-decisions and the ledger/log/archive files instead of the removed decisions-append. - .devflow/features/hooks/KNOWLEDGE.md: rewrote the ops block and curation section to assign-anchor/retire-anchor/rotate-observations + the retire-by-status (never hand-edit the rendered .md) model; refreshed the LLM-vs-plumbing table, anti-patterns, decisions-index filter (now incl. Retired), and file-reference list. - features/index.json: synced the hooks-entry discovery keywords. The only remaining 'decisions-append' mention is the intentional 'replaces the removed decisions-append' note in the new assign-anchor doc. Co-Authored-By: Claude <noreply@anthropic.com>
CI failed on Node 18/20 (passed on 22 + locally): AC-P1 render perf asserted '17.8 < 15'. Batch-6 fixed the vacuous-pass but replaced it with a flaky wall-clock ratio — a <15x tolerance for a 10x input is only 50% headroom, which a single GC/scheduling spike on a shared runner blows through. Fix (all three ratio guards in render-decisions + ledger-ops): - Estimate scaling with MIN, not median. Timing noise only ADDS time, so the fastest run is the cleanest approximation of true compute cost; min ignores the transient spike that inflated the median to 17.8x. - Raise the ratio bound to a documented SUPER_LINEAR_RATIO (30 for in-memory, 25 for the CLI write-path). Linear ~10x, O(N²) ~100x — 30 robustly catches super-linear blowup while tolerating CI noise. The always-on absolute ceiling (medianLarge < 500ms / 100ms / 10s) remains the wall-clock budget, so the test still can't pass vacuously. Not a weakening: the bound now matches the test's actual purpose (detect super-linear regression), and the vacuous-pass guard + absolute ceiling are preserved. 1810/1810 green; perf tests stable across repeated local runs. Co-Authored-By: Claude <noreply@anthropic.com>
Summary
Reworks the decisions/pitfalls system so
decisions.md/pitfalls.mdbecome a deterministic, active-only render of a new committed anchored ledger (.devflow/decisions/decisions-ledger.jsonl) — eliminating the prior dual-source-of-truth (parallel.md+.jsonlwrites bridged by a brittle regex, which had already drifted). Also tightens the Dream creation bar (fewer, higher-quality entries), makes removal recoverable ("retire", not delete), bounds the observation log via rotation, and makes the Dream pipeline commit its own maintenance with recognizable commits.What changed
Data model (two-file split)
decisions-ledger.jsonl— committed, anchored rows only (incl. Deprecated/Superseded/Retired); the render source of truth + recoverability.decisions-log.jsonl(raw observations) + NEWdecisions-log.archive.jsonl— stay gitignored.decisions.md/pitfalls.md— committed, rendered from the ledger (active entries only — one tier).Creation bar (Dream) — abstain-by-default, negative examples, ADR-XOR-PF (one incident → an ADR or a PF, never both), dedup-before-create.
confidenceis now honest LLM metadata, not a gate (the dead ≥0.65 gate + 0.95 default are gone).Ops — new
assign-anchor(numbering owned by the ledger: max+1 over all anchored incl. Retired, ADR/PF independent, 3-digit pad),retire-anchor(flipsdecisions_status, recoverable),rotate-observations(archives observing rows >30d).decisions-appendis removed (hard cut, no shim).Render + format — pure
render-decisions.cjs(render/--check) + a shareddecisions-format.cjsimported by both the renderer and the writer so the byte-compat format can't drift; locking consolidated intomkdir-lock.cjs.Migration —
decisions-ledger-unify-v1(per-project) backfills the ledger from existing.md+ log, preserving every body verbatim (raw_body), synthesizing entries whose source obs is missing, marking hand-deleted anchorsRetired(numbers reserved, never resurrected), and preserving inline amendments. Idempotent + crash-safe (always reconciles.mdfrom the ledger).sync-devflow-gitignore-v3pushes the gitignore change to existing projects.Dream auto-commit —
dream-commithelper writeschore(dream): <action>commits withDream-Task:/Dream-Session:/Co-Authored-By:trailers, staging ONLY allowed.devflowmaintenance paths (nevergit add -A), skipping cleanly mid-rebase/merge/cherry-pick/detached-HEAD and when clean. Default ON via.devflow/dream/config.jsonautoCommit; reported bydevflow decisions --status.Breaking changes / rollout
.md(they live in the committed ledger, recoverable).decisions-ledger.jsonlon nextdevflow init; the unify migration then materializes it and re-renders the.md.decisions-appendremoved — zero callers remain.npm run build+devflow init(PF-007).Verification
.mdbodies byte-identical except the repopulated TL;DR Key line; ADR-002/PF-003/PF-005 Retired + absent from render; ADR-016 amendment preserved.🤖 Generated with Claude Code