Skip to content

fix(decisions): list reads the decisions directory, not INDEX.md (sc-2332) - #599

Merged
norvalbv merged 1 commit into
mainfrom
benjinorval/sc-2332/guard-decisions-list-reports-n
Sep 5, 2026
Merged

fix(decisions): list reads the decisions directory, not INDEX.md (sc-2332)#599
norvalbv merged 1 commit into
mainfrom
benjinorval/sc-2332/guard-decisions-list-reports-n

Conversation

@norvalbv

@norvalbv norvalbv commented Sep 5, 2026

Copy link
Copy Markdown
Owner

guard-decisions list printed No decisions recorded. in any checkout whose <decisionsDir> held committed axis files but no git-tracked INDEX.md, while query and categories saw every axis in that same directory. It was not an empty answer — it was a positive claim of emptiness for a repo that had decided.

skills/brainstorming/SKILL.md:96 tells an agent to run list to find a prior ruling before recording a new one. A false "nothing decided" there mints a duplicate axis for an already-ruled question. devkit's own INDEX.md is committed and complete (76/76), which is why self-host never saw this: the harm was consumer-side.

Cause

cmdList gated on existsSync(<decisionsDir>/INDEX.md) and never read decisionsDir at all. Since INDEX.md is upserted one row at a time by add --target and full-rebuilt only by amend --target, it is routinely absent or merely incomplete — the 23-of-86 case measured under the governing Target. So the fix removes the gate rather than adding a missing-file fallback.

This finishes a migration already ruled on. decision-retrieval-candidate-set (Target 2026-07-25, Scope gate-engine/decisions/**) demoted INDEX.md to a rendered view; six other readers already enumerate the directory. cmdList was the last holdout. Its Rejected option (b) forecloses repairing INDEX.md on write, so list stays read-only.

Change

renderSpine() in recall/full-print.mts: loadAxisRows → narrow cells → renderIndex. Placed there per that file's own documented precedent for printRankeddecisions.mts is at 498 of its 500-line cap.

The narrowing is the whole subtlety. loadAxisRows is a retrieval shape: why is the axis's full Context and updated is the last date anywhere in the body, notes included, while both INDEX.md writers clamp why through whyHook and record the Target's date. Rendering the rows raw grew this repo's own spine 71,292 → 114,634 bytes (+61%, 76/76 why cells) and moved 37/76 updated cells onto note dates, collapsing the liveRulingId-vs-updated staleness signal that axis records as load-bearing. With the narrowing, list output is byte-identical to the committed INDEX.md on all 76 rows.

Tests

Ten cases in __tests__/cli.test.mts, all driven through the real bin, asserting against the fixture's own file set — never against loadAxisRows (tautological post-fix) or query (ranks, caps at --top 5, and lazily writes .decisions/, destroying the fixture precondition).

  • no INDEX.md → every axis listed; incomplete INDEX.md → the omitted axis still listed; orphan row whose file is gone → omitted
  • Target-less legacy axis kept as a well-formed row (the case amend's regenerateIndex silently drops)
  • why clamped to a hook; updated from the Target, not a later note — on both the target: and entry: branches
  • a ruling containing | still renders 4 cells; an empty axis file renders a blank row instead of throwing
  • list under a broken bundle names the outage and never claims the log is empty — list parses markdown now, so the sc-2692 contract became load-bearing here, and an outage rendering as an empty log would re-open this bug by a second door

Both date tests were mutation-verified: dropping the narrowing fails them.

Acceptance criteria

AC1 and AC3 as written. AC2 ("name the command that builds the index") is vacuously satisfied, not droppedlist depends on no index now, and no devkit command rebuilds INDEX.md, so that hint would name a remediation the user cannot perform.

Out of scope: skills/brainstorming/SKILL.md:96 keeps calling list — a corrected list is exactly what that call site needs. Moving it to query belongs to decision-log-informs-before-work's Scope.

Closes sc-2332.

Summary by CodeRabbit

  • Bug Fixes
    • The list command now reflects decisions found in the decisions directory, even when INDEX.md is missing or outdated.
    • Empty results are reported only when no decisions are available, avoiding misleading “No decisions recorded” messages.
    • The command now clearly reports parser outages as unavailable rather than indicating an empty decision log.
    • Listing output preserves expected formatting, including legacy entries, field limits, and date selection.

…2332)

`guard-decisions list` printed `No decisions recorded.` in any checkout whose `<decisionsDir>` held committed axis files but no git-tracked `INDEX.md`, while `query` and `categories` saw every axis in that same directory. It was not an empty answer — it was a positive claim of emptiness for a repo that had decided.

`skills/brainstorming/SKILL.md:96` tells an agent to run `list` to find a prior ruling before recording a new one. A false "nothing decided" there mints a duplicate axis for an already-ruled question. devkit's own `INDEX.md` is committed and complete (76/76), which is why self-host never saw this: the harm was consumer-side.

## Cause

`cmdList` gated on `existsSync(<decisionsDir>/INDEX.md)` and never read `decisionsDir` at all. Since `INDEX.md` is upserted one row at a time by `add --target` and full-rebuilt only by `amend --target`, it is routinely absent *or* merely incomplete — the 23-of-86 case measured under the governing Target. So the fix removes the gate rather than adding a missing-file fallback.

This finishes a migration already ruled on. `decision-retrieval-candidate-set` (Target 2026-07-25, Scope `gate-engine/decisions/**`) demoted `INDEX.md` to a rendered view; six other readers already enumerate the directory. `cmdList` was the last holdout. Its Rejected option (b) forecloses repairing `INDEX.md` on write, so `list` stays read-only.

## Change

`renderSpine()` in `recall/full-print.mts`: `loadAxisRows` → narrow cells → `renderIndex`. Placed there per that file's own documented precedent for `printRanked` — `decisions.mts` is at 498 of its 500-line cap.

The narrowing is the whole subtlety. `loadAxisRows` is a *retrieval* shape: `why` is the axis's full Context and `updated` is the last date anywhere in the body, notes included, while both `INDEX.md` writers clamp `why` through `whyHook` and record the **Target's** date. Rendering the rows raw grew this repo's own spine 71,292 → 114,634 bytes (+61%, 76/76 `why` cells) and moved 37/76 `updated` cells onto note dates, collapsing the `liveRulingId`-vs-`updated` staleness signal that axis records as load-bearing. With the narrowing, `list` output is **byte-identical to the committed `INDEX.md` on all 76 rows**.

## Tests

Ten cases in `__tests__/cli.test.mts`, all driven through the real bin, asserting against the fixture's own file set — never against `loadAxisRows` (tautological post-fix) or `query` (ranks, caps at `--top 5`, and lazily writes `.decisions/`, destroying the fixture precondition).

- no `INDEX.md` → every axis listed; incomplete `INDEX.md` → the omitted axis still listed; orphan row whose file is gone → omitted
- Target-less legacy axis kept as a well-formed row (the case `amend`'s `regenerateIndex` silently drops)
- `why` clamped to a hook; `updated` from the Target, not a later note — on both the `target:` and `entry:` branches
- a ruling containing `|` still renders 4 cells; an empty axis file renders a blank row instead of throwing
- `list` under a broken bundle names the outage and never claims the log is empty — `list` parses markdown now, so the sc-2692 contract became load-bearing here, and an outage rendering as an empty log would re-open this bug by a second door

Both date tests were mutation-verified: dropping the narrowing fails them.

## Acceptance criteria

AC1 and AC3 as written. AC2 ("name the command that builds the index") is **vacuously satisfied, not dropped** — `list` depends on no index now, and no devkit command rebuilds `INDEX.md`, so that hint would name a remediation the user cannot perform.

Out of scope: `skills/brainstorming/SKILL.md:96` keeps calling `list` — a corrected `list` is exactly what that call site needs. Moving it to `query` belongs to `decision-log-informs-before-work`'s Scope.

Closes sc-2332.
@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Team

Run ID: f0c99bb7-6eab-452a-b874-65fbfaf1d12b

📥 Commits

Reviewing files that changed from the base of the PR and between 3a8a88d and e1a9521.

📒 Files selected for processing (4)
  • docs/decisions/decision-retrieval-candidate-set.md
  • gate-engine/decisions/__tests__/cli.test.mts
  • gate-engine/decisions/decisions.mts
  • gate-engine/decisions/recall/full-print.mts

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

The list command now sources decision rows from the decisions directory instead of reading INDEX.md. A new renderSpine helper formats those rows, while CLI tests cover empty, legacy, stale, malformed, and unavailable-input cases.

Changes

Directory-sourced decision listing

Layer / File(s) Summary
Directory-backed spine rendering
gate-engine/decisions/recall/full-print.mts
Adds renderSpine, which loads axis rows, derives update dates, applies whyHook, and renders the index format.
List command integration and validation
gate-engine/decisions/decisions.mts, gate-engine/decisions/__tests__/cli.test.mts, docs/decisions/decision-retrieval-candidate-set.md
Updates cmdList to use renderSpine. Tests cover directory membership, formatting, dates, empty logs, and parser outages. The decision log records the behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to e1a95

The decision list now reliably shows committed decision files even when INDEX.md is missing or stale, while preserving formatted output and read-only behavior. No merge-blocking risk remains.

Sequence Diagram(s)

sequenceDiagram
  participant cmdList
  participant renderSpine
  participant loadAxisRows
  participant renderIndex
  cmdList->>renderSpine: renderSpine(paths(cwd))
  renderSpine->>loadAxisRows: Load rows from the decisions directory
  loadAxisRows-->>renderSpine: Return axis rows
  renderSpine->>renderIndex: Render rows with dates and why text
  renderIndex-->>renderSpine: Return formatted spine
  renderSpine-->>cmdList: Return spine or null
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files. (1 skipped: 1… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely states the main change: the list command now reads the decisions directory instead of INDEX.md. The issue identifier is relevant and does not obscure the change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch benjinorval/sc-2332/guard-decisions-list-reports-n

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@norvalbv
norvalbv merged commit 2c87515 into main Sep 5, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant