B3: derived doc generators (timeline, file-structure, tree, rebase) - #119
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @thrillmot's task in 7m 8s —— View job 🐛 Clud Bug reviewThis round: 1 critical · 0 minor · 0 resolved from prior · 0 still open Found: 1 🔴 / 0 🟡 / 0 🟣 Per-skill scan
Critical findings🔴 [critical-issues-only]: Sort-order divergence from Python for symlinks-to-directories ( Reasoning
Fix: use Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions] |
🐛 Clud Bug reviewThis round: 1 critical · 0 minor · 0 resolved from prior · 0 still open Found: 1 🔴 / 0 🟡 / 0 🟣 Per-skill scan
Critical findings🔴 [critical-issues-only]: sort-order diverges from Python for symlinks-to-directories, breaking byte-identity contract (internal/tree/tree.go:275). Reasoning
Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions] |
…nternal config/decisions/timeline/tree packages with byte-identical Python v0.6.14 output
…ches Python pathlib.is_dir) — clud-bug PR #119
4d2fb9b to
4a66f4b
Compare
|
Deployment failed with the following error: Learn More: https://vercel.com/thrillmot-s-projects?upgradeToPro=build-rate-limit |
|
Claude finished @thrillmot's task in 2m 43s —— View job Re-review: B3 derived doc generators (fix-push)
Fix verified: |
🐛 Clud Bug reviewThis round: 0 critical · 0 minor · 0 resolved from prior · 0 still open Found: 0 🔴 / 0 🟡 / 0 🟣 Per-skill scan
Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions] |
Summary
Wave B3 of the logmind Go rewrite — ports the derived-doc generators and the rebase wrapper.
timeline,file-structure,tree,rebaseinternal/config,internal/decisions,internal/timeline,internal/treeinternal/gitcli:DefaultBranch(5-step search mirror),RunCaptured(stderr-exposing variant for rebase)MakefileSNAPSHOT_PKGSextended withinternal/timeline/...+internal/tree/...Built on top of B1 + B2 (PRs #116 + #117). Targets
v1-go-rewrite.Byte-identical output vs Python v0.6.14
Verified against the actual logmind repo (112 decisions across 6 months, ~120KB unbounded tree):
logmind timeline(brief, default)logmind timeline --fulllogmind file-structure(depth 2)logmind file-structure --max-depth 0(unbounded)logmind file-structure --max-depth 1logmind file-structure --max-depth 3logmind timeline --check(clean)logmind file-structure --check(clean)logmind treelogmind timeline --check(stale)logmind rebase(failure path on dirty WT)Confirmation method:
diff <(venv/bin/logmind <cmd>) <(bin/logmind <cmd>)against/Users/ludlow/logminditself — the largest real fixture available.Snapshot tests (49 total + 7 golden fixtures)
internal/timeline/— 4 goldens covering brief/full × elision-vs-not, plus the empty/single-newline property testsbrief-mixed.golden— 3 months (4-entry elision, 2-entry verbatim, 1-entry singleton)brief-singular-elision.golden— exactly 3 entries →... 1 more decision ...singular nounbrief-two-elided-months.golden— two elided months back-to-back for inter-month spacingfull-mixed.golden— full mode pins lack of count suffix on month headersinternal/tree/—generate-file-structure.goldenpins the template head + tail bytesinternal/cli/—timeline_stdout_brief.golden,timeline_stdout_full.goldenfor end-to-end stdout shapemake testBrief-mode byte-identical proof
Brief-mode month grouping is the highest-risk algorithm — Python uses
lines = [HEADER, ""]then"\\n".join(lines), which produces three newlines between the---footer of the header and the first## YYYY-MM. Go'sstrings.Builderbyte-level approach would emit only two unless you mirror the join semantics precisely.Resolution: assemble a
[]stringmirror of Python'slinesand usestrings.Join(lines, "\\n")rather than emitting bytes directly. The trailinglines.append("")gives the final\\nafter the last entry. Seeinternal/timeline/timeline.gocomments for the trace.Verified against the actual
docs/timeline.mdwhich carries the brief-mode elision lines for 6 months of real history.Tree-walk byte-identical proof
Tree walk uses pure Go (
filepath.WalkDirnot invoked — explicitos.ReadDirper frame for sort control). Sort key matches Python:(not is_dir, name.lower())→ directories first then case-insensitive alphabetical. Pattern matching is path-aware (matches full relative path, components, and basename).One known semantic divergence:
filepath.Match's*does NOT cross/, but Python'sfnmatch.fnmatchcase's*DOES. For the patterns logmind actually ships (DEFAULT_IGNORES+ typical.gitignore), this is invisible — patterns are either segment-level (*.pyc,__pycache__) or fully literal (site/.next). Documented inpatternSetMatches.Verified at depth 0/1/2/3 against the logmind repo itself which is the most realistic fixture I have (it has
site/.next/,venv/,__pycache__/,.git/, mixed casing, et al).Known divergence vs Python v0.6.14
--checkwithout--write:The stdout message is unchanged so consumers diffing output see no difference. The exit-code divergence is a documented known issue. Closing it would require an
ErrSilentExit2sentinel incmd/logmind/main.go(deferred to a coordinated cross-wave change — B4 lands first and is a candidate).Open questions for human reviewer
ErrSilentExit2sentinel as a coordinated patch alongside B4?*-vs-/divergence is theoretical but real. Want a custom fnmatch port that's*=anything-including-/strict, or accept the practical equivalence?--no-fetch: Python exposes--no-fetchso tests can rebase against a fixture without anorigin/. I ported it for parity; any objection?feat/go-b4-agents. The B4 branch will need to addroot.AddCommand(newAgentsCmd())next to the B3 wires.Design decisions
[]stringmirror +strings.Joinrather than byte-level emission. See comment ininternal/timeline/timeline.go:106-119.tree(1)even when available. Consuming repos get identical output across OSes. Seeinternal/tree/tree.go:1-22.Configstruct withyaml:tags; user keys overlay defaults leaf-by-leaf viayaml.Unmarshalinto a pre-populated default struct. Mirrors Python's_deep_updateshape without recursive Map traversal in Go. Seeinternal/config/config.go:118-140.-1 sentinelfor unbounded depth: CLI's--max-depth 0translates to internal-1soRendercan use a simplemaxDepth >= 0 && depth >= maxDepthguard. Documented at the function boundary.internal/gitcli(not duplicated ininternal/rebase) because B5 (init) and B4 (agents in some paths) will also want it.Test plan
make build && make test— all packages greenmake snapshotregenerates 7 golden files without drift--checkstale + clean paths verifiedrebasefailure paths verified (not-a-repo, detached HEAD, refusing-self, fetch fail, rebase fail)🤖 Generated with Claude Code