Skip to content

fix: honor deferred_until and route guidelines by declared category - #264

Draft
jlevy wants to merge 1 commit into
mainfrom
claude/fix-deferred-filters-and-guideline-grouping
Draft

fix: honor deferred_until and route guidelines by declared category#264
jlevy wants to merge 1 commit into
mainfrom
claude/fix-deferred-filters-and-guideline-grouping

Conversation

@jlevy

@jlevy jlevy commented Aug 26, 2026

Copy link
Copy Markdown
Owner

Three defects from the open bug beads, each verified live against the code before
fixing and pinned by a test that fails against the old behavior. No release in this PR —
packages/tbd/package.json stays at 0.8.1.

tbd-5av0 — deferred_until was recorded and then ignored

--defer wrote the field and tbd ready went on offering the bead. Verified on the
0.8.1 build before the fix: a bead deferred to 2027-01-01 appeared in tbd ready
today, so the field read as scheduling while scheduling nothing. The bead reports this
found during a real triage — 21 beads deferred to 2027 all stayed in ready until each
was separately set to --status deferred.

readyIssueIds now treats a future deferral as the hold it already documents for
blocked and paused. A deferral exactly at now counts as arrived.

The cutoff is a parameter, not a Date.now() read inside the filter.
issue-changes.ts computes this set twice to diff two snapshots; two independent clock
reads could report a deferral that merely elapsed between them as a ready transition no
edit caused. That path now passes one instant to both calls. planMirror is documented
as Pure., so it takes an explicit readyAt rather than quietly acquiring a clock
dependency.

This changes readiness everywhere it is computed — ready, web, and the Linear
mirror — which is the point: issue-query.ts exists so two surfaces answering the same
question cannot disagree.

tbd-v8lv — --defer-before was a silent no-op

ListOptions declared deferBefore and the flag appeared in --help, but no filter
ever read it. Verified before the fix: tbd list --defer-before 2020-01-01 returned a
bead whose deferred_until was 2027.

It now filters. A bead with no deferred_until is not "deferred before" any date, so it
is excluded whenever the flag is set, rather than treated as deferred since the
beginning of time.

An unusable date is now rejected at the CLI boundary instead of degrading into a
filter that matches everything — that would be the same silent-no-op failure in a new
place:

$ tbd list --defer-before not-a-date
Error: Invalid --defer-before value: not-a-date. Expected a date or timestamp.
$ echo $?
2

parseDateOption moves from update.ts into cli/lib/issue-input-validation.ts so
--due, --defer, and --defer-before all fail the same way.

tbd-dado — guidelines routed by filename prefix, not declared category

Every bundled guideline declares a category, and doc-categories.ts states plainly
that "the old name-based inference is retired in favor of the declared field." Grouping
was still matching name prefixes, and the two disagreed:

Guideline Declared Was filed under
electron-app-development-patterns desktop TypeScript & JS ecosystem
electrobun-app-development-patterns desktop Docs, process & tooling
tauri-app-development-patterns desktop Docs, process & tooling

n.startsWith('electron-') is false for electrobun-, and tauri- had no matcher at
all — so an agent asked to work on a Tauri app would not find the guidance under any
framework heading. Tauri's core is Rust, so widening the TypeScript matcher would have
been wrong in the other direction.

Grouping now reads the declared field. The three frameworks share a Desktop app
frameworks
heading, and the catch-all is left holding only genuinely miscellaneous
docs.

The regression guard is the general one, not a spot check: every declared category
with bundled documents must route out of the catch-all
, so the next category added
cannot land there unnoticed — which is exactly how this shipped.

Also moved release-notes-guidelines into the cross-cutting group beside
release-engineering-rules, whose own description tells the reader to load the two
together. Flagging it separately since the bead only called it "arguably" misfiled —
easy to drop if you disagree.

Verification

Gate Result
pnpm test 2464 passed, 164 files (was 2449 / 163)
pnpm lint:check pass
pnpm format:check pass

Red-green on all three: the 9 new assertions were confirmed failing against the old code
before any fix, reproducing each reported symptom. Then re-verified through the built
CLI end to end — ready excludes the deferred bead, --defer-before filters and
rejects a bad date with exit 2.

Beads

Closes tbd-5av0, tbd-v8lv, tbd-dado. Separately verified tbd-az97 as already fixed
(a single-branch clone now discovers the remote sync branch, shares its history, and
converges) — not closed here since it is unrelated to this diff.

Left open deliberately: tbd-pht1 / tbd-iiys, which are live. An orphaned
data-sync.lock blocks every bead write and tbd doctor reports the repository
healthy; I reproduced a tbd create hanging until killed. The 30-minute staleMs is a
deliberate safety contract (lockfile.ts says so in as many words), so the fix is
observability — doctor flagging the lock, and the blocked command saying what it waits
on — not a timing change. Worth its own PR.


🤖 Generated with Claude Code

https://claude.ai/code/session_013eaEeT9CYT5TDyNGAw95t9

Three defects found by reviewing the open bug beads against the code.
Each is verified live before the fix and pinned by a test that fails
against the old behavior.

deferred_until was recorded and then ignored (tbd-5av0). `tbd ready`
offered a bead deferred to 2027 as available work today, so the field
read as scheduling while scheduling nothing. readyIssueIds now treats a
future deferral as the hold it already documents for `blocked` and
`paused`. The cutoff is a parameter, not a clock read inside the filter:
issue-changes.ts computes the set twice to diff two snapshots, and two
reads could report a deferral that merely elapsed between them as a ready
transition no edit caused. planMirror keeps its documented purity via an
explicit readyAt.

tbd list --defer-before was declared in the option table and help text
but never read by any filter (tbd-v8lv), so it returned the same rows as
no flag at all. It now filters, and an unusable date is rejected at the
CLI boundary rather than degrading into a filter that matches
everything — the same silent-no-op failure in a new place. parseDateOption
moves to the shared validation helper so every date flag fails the same
way.

Guideline grouping matched filename prefixes while every bundled doc
already declares a category, and doc-categories.ts calls name inference
retired (tbd-dado). The two disagreed: electron- matched the TypeScript
prefix while its siblings electrobun- and tauri- matched nothing and fell
into the catch-all, where an agent looking for desktop guidance would not
find them. Grouping now reads the declared field, the three frameworks
share a heading, and a test asserts every declared category routes out of
the catch-all so the next one added cannot land there unnoticed.
release-notes-guidelines joins the cross-cutting group beside
release-engineering-rules, which its own description tells the reader to
load with it.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013eaEeT9CYT5TDyNGAw95t9
@deepsource-io

deepsource-io Bot commented Aug 26, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in 2c2beff...cb771d2 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
Secrets Aug 26, 2026 6:14p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

@github-actions

Copy link
Copy Markdown

Coverage Report for packages/tbd

Status Category Percentage Covered / Total
🔵 Lines 46.14% 7674 / 16629
🔵 Statements 46.22% 7857 / 16996
🔵 Functions 51.15% 1243 / 2430
🔵 Branches 44.58% 4627 / 10379
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/tbd/src/cli/commands/list.ts 5.88% 0% 22.22% 5.97% 57-227, 238-241
packages/tbd/src/cli/commands/update.ts 0.3% 0% 0% 0.3% 73-788, 808-822
packages/tbd/src/cli/lib/integration-runner.ts 8.96% 3.92% 9.8% 8.41% 73-179, 199, 202, 254-696
packages/tbd/src/cli/lib/issue-input-validation.ts 92.3% 90% 100% 92.3% 33
packages/tbd/src/file/doc-cache.ts 64.32% 64.93% 82.35% 64.59% 146, 180-184, 186-191, 245, 544-666
packages/tbd/src/integrations/core/mirror.ts 94.82% 76.35% 95% 94.64% 313-318, 384-387, 400, 417, 424, 431
packages/tbd/src/lib/issue-changes.ts 91.5% 91.13% 84% 91.26% 147-153, 167-170, 198, 340, 364, 386-389, 480-481, 496
packages/tbd/src/lib/issue-query.ts 79.41% 73.86% 100% 79.41% 191-208, 219, 225, 231, 234, 237, 240, 243
packages/tbd/src/lib/issue-selection.ts 94.11% 90.9% 100% 93.54% 23, 90
Generated in workflow #1397 for commit cb771d2 by the Vitest Coverage Report Action

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.

2 participants