sd-3wp.1: epic buckets, the what's-next cascade, and a change gate that doesn't defeat itself - #112
Merged
Conversation
…ignal FNV-1a over the noms manifest bytes instead of its mtime. StoreMTime moves on every bd read (including a pure one), so gating on it never lets a repo settle. Content only moves on a genuine write, preserving st-nm5's out-of-band detection. Diagnosis re-verified live before coding: 21/23 counts-mtimes rows pending=1, bd stats rewrites the manifest mtime with byte-identical content (sha256 unchanged), ~0.4s/exec x 4 reads per repo.
… section Roadmap() now resolves $root/ROADMAP.md first (the sdlc standard), falling back to the legacy NORTH_STAR.md ## roadmap parse only when it's absent. New format: numbered arrow lines inside ## Epics (legacy ## Milestones/## Route tolerated), id = first token after the first arrow, multi-id lines keep the first, wrapped continuation lines fold into their numbered row (sd-r59). Go twin of roadmap-epics.sh's roadmap_file/roadmap_parse, reduced to its two non-kg branches per this bead's non-goals.
Row gains three additive fields (epics/next/claimed, JSON null when absent). computeRow now derives insight.Lanes once and shares it across the repo-level buckets, the per-epic buckets, and pickNext — zero new bd execs. - liveRoadmapEpics/currentEpicID: roadmap-ordered live (non-closed) epics; a deferred epic still counts as current (deliberately, station() keeps its own open/in-progress rule); ghost ids skipped. - epicBuckets: per-live-epic ◆○◐● partition over direct children, nested epics excluded, bw/bh overlap on a gated in-progress child. - pickNext: 4-rung cascade (repo-wide claimed -> current-epic open child -> repo-wide waiting -> next live epic's open child), epics excluded from every rung, no repo-wide ready-bead catch-all (bead-verbatim exclusion, pinned by test). Tie-break: lowest priority (nil->P2) then id. laneCounts signature changed to take the shared lanes map.
changeKey() now calls bd.StoreContentKey instead of bd.StoreMTime, falling back to lastTouched exactly as before. This is the actual perf fix: the refresher's own 4 bd reads/repo rewrote the manifest mtime with identical content, so the old mtime-keyed gate never let a repo settle — every cycle re-armed the st-3p8 pending bit for every repo it had just visited. refresh_test.go: setStoreMTime -> setStoreState(content, mtime); TestRefreshOutOfBandStoreChangeRefreshes now varies CONTENT in its out-of-band step (mtime-only would no longer trip a content-keyed gate). New TestRefreshConvergesDespiteReadSelfChurn is the bug's own regression test: a source that self-churns the manifest mtime every read must still let the repo settle after the guaranteed st-3p8 follow-up (calls==2 after 3 runs) — confirmed red against the old gate before the fix, green after.
… work goconst: one issueTypeEpic constant replaces 5 of the 6 "epic" literals in counts.go (the cascade reason string "epic" stays literal — different meaning). gocritic rangeValCopy/hugeParam: pickNext's filter/pick chain and epicBuckets now index/pointer into []bd.Issue instead of copying the ~240-byte struct per iteration; refresh.go's writeRowsAtomic loop switched to the same pattern (flagged only because Row grew past the threshold with this bead's new fields). modernize: strandmd.epicLineID uses strings.Cut instead of strings.Index+slicing. gofmt: counts_test.go table alignment. make check green: vet, lint (0 issues), test, build, conform.
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 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. Comment |
dkoosis
added a commit
that referenced
this pull request
Aug 20, 2026
…lot (#115) * feat(counts): sd-3wp.2 — epic titles and the current epic's roadmap slot wrap's rewritten session banner names epics and states "epic 2 of 7". counts.json could supply neither: epics[] carried ids without titles, and its index counts only live epics, so it cannot express a position among all of them. Two additive fields, from reads computeRow already makes: - EpicRow.title — bd's own epic title, via the EpicStatus read the buckets derive from (bd.EpicRef gains Title to carry it). - Row.roadmap {k, n} — the current epic's 1-based slot among ALL roadmap ids, closed included, so k/n reads as progress through the project. Explicit null when there is no roadmap or no live epic — the same two conditions that null epics. Additive only, same rule as #112: every consumer decodes by name into its own struct, so a key it never mentions cannot affect it. Verified live against sdlc: roadmap {k:2,n:7}, seven epic titles rendered. * feat(counts): carry bd's closed/total child roll-up per epic The four lane buckets partition only LIVE children — bd list omits closed — so epics[] could not answer "how far through this epic are we", which the banner's ✓ tally and progress bar both need. EpicRow gains bcl (closed children) and n (total children), taken verbatim from the EpicStatus read epicBuckets already walks. They are bd's own roll-up, not a re-derivation: a nested epic child counts there and not in the buckets, and the comment says so. epicTitles becomes epicMeta — the lookup now carries the whole EpicStatus row rather than just the title. Live against sdlc: sd-ev2 bcl 12 / n 13, matching bd epic status.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
counts.json rows gain three additive fields — epics (per-live-roadmap-epic ◆○◐● buckets, roadmap order), next {id,title,reason} via the four-rung cascade (claimed → current-epic top ready → waiting-on-dk → next epic), claimed — consumed next by the session-open banner (sd-3wp.2).
Perf: the 35.7s bare run was a self-defeating change gate — changeKey keyed on the dolt noms-manifest MTIME, which a pure bd read rewrites with identical bytes, so the refresher's own reads marked every repo changed forever. Now keys on manifest CONTENT (bd.StoreContentKey, FNV-1a). Measured after: bare warm 3.0s, single-repo warm 1.1s (<2s acceptance).
Plan: sdlc docs/plans/'Strand counts epic buckets, next pick, fast writer.md' (approved). Reviews: plan-adherence pass · concurrency pass (5 hunts refuted; one pre-existing multi-manifest caveat noted). make check green.
One acceptance criterion is verified-by: human — statusline render unchanged; eyeball the badge in a live session after merge.
Closes sd-3wp.1