Skip to content

B6: config + doctor + init + self-update (+ v0.6.16 PATH probe carry-forward) - #125

Merged
thrillmot merged 8 commits into
v1-go-rewritefrom
feat/go-b6-config-doctor
Jun 2, 2026
Merged

B6: config + doctor + init + self-update (+ v0.6.16 PATH probe carry-forward)#125
thrillmot merged 8 commits into
v1-go-rewritefrom
feat/go-b6-config-doctor

Conversation

@thrillmot

Copy link
Copy Markdown
Collaborator

Summary

Wave B6 lands the foundation that makes brew install logmind && logmind init a working install. Replaces the pip-distribution path with four Go-native commands.

Commands shipped

  • logmind config list | get <key> | set <key> <value> — reads/writes .logmind/config.yml with dot-notation paths + Python-style type coercion (true/false → bool, digits → int, etc.). Insertion-ordered YAML round-trip preserves key order across set operations.
  • logmind doctor [--json] [--offline] [--exit-zero] — probes workflow drift, AGENTS.md block versions, .gitattributes merge-driver state, hook drift (post-merge, post-rewrite, commit-msg v0.6.16+), and PATH-resolution drift (v0.6.16 carry-forward — surfaces tokenomics-recurrence root cause at doctor time). PyPI probe is best-effort with 2s timeout.
  • logmind init [--no-git] [--agents <list>] [--all-agents] [--github-actions/--no-github-actions] — scaffolds docs/, .logmind/config.yml, AGENTS.md slim block, per-agent stubs, all 4 GitHub workflows (verbatim from internal/templates/github/), .gitignore + .gitattributes blocks, post-merge/post-rewrite/commit-msg hooks, first decision log entry. Refresh-mode no-op when re-run on an already-initialised repo.
  • logmind self-update — refreshes AGENTS.md block, per-agent stub marker bodies, and hooks (post-merge + post-rewrite + commit-msg) from the running binary.

v0.6.16 carry-forward

  • internal/doctor.probePathResolution() — ports Python _probe_path_resolution(). Reports current when PATH logmind --version matches running binary; stale when versions differ (marker includes both versions + conflicting path for one-glance remediation); missing when no logmind on PATH; markerless when --version is unparseable.
  • internal/hooks.BuildCommitMsgBody() + InstallCommitMsg() — v0.6.16 commit-msg hook (warns on [skip-logmind] subjects).
  • AGENTS.md template + slim variant continue at v5 / v7-pointer (the bundled templates carry the current markers; the doctor accepts either since logmind init chooses one based on skills.sh availability).

Byte-identical parity status

End-to-end harness comparing PYENV_VERSION=3.11.8 logmind init --no-git (v0.6.14) vs ./logmind-go init --no-git against fresh empty dirs:

Files byte-identical: .logmind/config.yml, docs/decisions-archive.md, docs/timeline.md, AGENTS.md, .gitignore, .gitattributes, .github/workflows/check-decisions.yml, .github/workflows/logmind-self-update.yml.

Documented deltas (acceptable for B6):

  1. docs/decisions.md — differs only by timestamp (%Y-%m-%d %H:%M).
  2. docs/file-structure.md — differs only by repo-root directory name (run-dependent).
  3. .github/workflows/regen-timeline.yml + check-doc-links.yml — differ only by the pip install \"logmind==X.Y.Z\" pin (Go uses version.Version constant 1.0.0-dev; Python pinned 0.6.14). Expected and correct — each binary pins its own version.
  4. logmind config list YAML indent — Go's yaml.v3 indents sequence items one level deeper than PyYAML ( - item vs - item). Both parse equivalently; the on-disk config.yml.template is shipped verbatim from internal/templates/, so the actual install file is byte-identical — only the live config list re-emit differs.

Scope trims (tracked for follow-up PRs)

  • logmind init --configure-github — requires GitHub API auth + ruleset POST flow. Surfaces a Note in init output; users currently apply thrillmade/protocol/rulesets/canonical-v1.json manually.
  • logmind init --with-skdd — requires Node.js subprocess. Surfaces a Note in init output; users run npx clud-bug@latest init after logmind init.
  • logmind init --install-hook — surfaces a Note pointing at logmind install-hook (already shipped in B2).
  • logmind init --skill-install yes — surfaces a Note pointing at the npx skills add command.
  • Doctor probes deferredclud-bug tool status (reads .claude/skills/.clud-bug.json), check_stale_derived_docs_warning (Phase-D divergence detection), check_clud_bug_skill_usage_integration (v0.6.6 upload-step gate), LOGMIND_AUTO_REGEN_PAT secret probe.

These don't block downstream propagation — the core init+config+doctor+self-update flow works end-to-end against a fresh repo today.

Test plan

  • go test ./... all green (37 new tests across config, doctor, init, self-update)
  • go vet ./... clean
  • go build ./cmd/logmind produces a working binary
  • Diff harness against Python v0.6.14: only the 4 documented deltas above
  • logmind doctor --json round-trips a downstream-parseable shape with the same field names as Python (project_root, tools, overall, network_used, suggestions, installed_version, latest_version, workflows, drift)
  • PATH-probe test coverage: current / stale / missing / markerless paths each unit-tested with fake logmind shell scripts on a temp PATH

Carry-forward verification

  • internal/doctor.probePathResolution reports stale when fake binary's --version differs from version.Version; marker contains both versions + binary path
  • internal/hooks.BuildCommitMsgBody body matches Python _build_commit_msg_hook_body exactly
  • internal/hooks.InstallCommitMsg writes to .git/hooks/commit-msg with 0o755 perms + version marker
  • internal/doctor reports commit-msg hook row with appropriate drift category

🤖 Generated with Claude Code

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 3m 55s —— View job


🐛 Clud Bug review

This round: 1 critical · 1 minor · 0 resolved from prior · 0 still open

Found: 1 🔴 / 1 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned all 28 files. 1 critical finding below.
  • [evidence-based-review]: applied to all findings — both anchored to file:line.
  • [respect-existing-conventions]: no pattern fights found.

🔴 [critical-issues-only]: Non-atomic write in SaveMap corrupts config on any write failure (internal/config/config.go:232)

Reasoning

os.Create(path) truncates the file immediately. If enc.Encode or enc.Close then fails (disk full, I/O error after the fd is open), the caller gets an error but .logmind/config.yml is now empty — the truncation already happened. Every user setting is silently gone. Fix: write to os.CreateTemp in the same directory, then os.Rename atomically to path.


🟡 [evidence-based-review]: io.ReadAll in httpGetJSON without a size bound (internal/doctor/doctor.go:139)

Reasoning

The 2-second context timeout bounds wall-clock time, not bytes — a fast connection to a misbehaving server can buffer many MB before the timer fires. Wrapping the body with io.LimitReader(resp.Body, 1<<20) is sufficient for any real PyPI JSON payload and prevents accidental memory pressure in constrained CI environments.


Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

Comment thread internal/config/config.go Outdated
Comment thread internal/doctor/doctor.go Outdated
@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🐛 Clud Bug review

This round: 1 critical · 1 minor · 0 resolved from prior · 0 still open

Found: 1 🔴 / 1 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned all 28 files. 1 critical finding below.
  • [evidence-based-review]: applied to all findings. ✓ all anchored to file:line.
  • [respect-existing-conventions]: scanned for pattern fights. 0 findings.

Critical findings

🔴 [critical-issues-only]: SaveMap truncates config file before write completes — any I/O error leaves config empty (internal/config/config.go:232).

Reasoning

os.Create(path) at line 232 truncates the existing file immediately. If enc.Encode or enc.Close subsequently fails (disk full, permission error), the caller receives an error but .logmind/config.yml is now a zero-byte file — the truncation already happened before the error. Every user customisation is silently destroyed. Fix: write to os.CreateTemp in the same directory, then os.Rename atomically onto path.

Minor findings

🟡 [evidence-based-review]: httpGetJSON reads unbounded response body — a fast connection can buffer many MB before the 2s context fires (internal/doctor/doctor.go:139).

Reasoning

io.ReadAll(resp.Body) at line 139 has no byte cap. The context timeout bounds wall-clock time, not bytes transferred, so a fast link to a misbehaving server could buffer significant memory. Wrapping with io.LimitReader(resp.Body, 1<<20) is sufficient for any real PyPI JSON payload.

Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

@vercel

vercel Bot commented Jun 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
logmind-site Ignored Ignored Preview Jun 2, 2026 9:25pm

Request Review

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 2m 5s —— View job


Review complete

  • Loaded tools
  • Fetch prior review SHA + incremental diff
  • Check if prior critical issue (non-atomic SaveMap) was fixed — still open
  • Check if prior minor issue (unbounded io.ReadAll) was addressed — still open
  • Scan new commits for new issues — 2 new commits (skill/ cleanup + docs only), no new issues
  • Resolve fixed threads — neither issue fixed, 0 threads resolved
  • Emit structured output

Summary

The two new commits (afbda8c, c03b508) are housekeeping only — deleted internal/skill/ files that leaked from B5 worktree, and docs updates. Neither the non-atomic SaveMap write nor the unbounded io.ReadAll in httpGetJSON were addressed. Both prior inline threads remain open.

No new issues introduced by the new commits.

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 1m 49s —— View job


Review complete

  • Load tools
  • Fetch incremental diff (prior SHA → HEAD)
  • Check prior threads (non-atomic SaveMap, unbounded io.ReadAll)
  • Scan new commits for new issues — none found
  • Resolve fixed threads / emit structured output

The three new commits after the original B6 commit are test-determinism fixes (c03b508, aaa9043) and a cleanup of leaked internal/skill/ files (afbda8c). Neither internal/config/config.go nor internal/doctor/doctor.go were touched — both prior issues remain open.

No new issues introduced.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🐛 Clud Bug review

This round: 0 critical · 0 minor · 0 resolved from prior · 2 still open

Found: 0 🔴 / 0 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned incremental diff (3 new commits). 0 new findings; 1 prior critical still open.
  • [evidence-based-review]: applied to all findings. prior threads both anchored to file:line.
  • [respect-existing-conventions]: 0 findings; new commits follow existing test and cleanup patterns.

Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 1m 38s —— View job


Review (fix-push)

  • Load tools
  • Fetch prior review SHA + incremental diff
  • Verify atomic SaveMap fix (internal/config/config.go) — ✅ os.CreateTemp + os.Rename pattern, correct cleanup with nil-guarded defer
  • Verify bounded io.ReadAll fix (internal/doctor/doctor.go) — ✅ io.LimitReader(resp.Body, maxProbeBodyBytes) with maxProbeBodyBytes = 1<<20
  • Resolve fixed threads — both resolved ✅
  • Emit structured output

Both prior issues are fixed cleanly. No new issues introduced by commit 14c4e15.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🐛 Clud Bug review

This round: 0 critical · 0 minor · 2 resolved from prior · 0 still open

Found: 0 🔴 / 0 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned incremental diff (fix commit 14c4e15). 0 new findings; 2 prior issues resolved.
  • [evidence-based-review]: both fixes anchored to file:line. 0 unanchored claims.
  • [respect-existing-conventions]: fix commit follows existing patterns. 0 findings.

Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

@thrillmot

Copy link
Copy Markdown
Collaborator Author

Status after second push

Go tests pass on all 3 matrices (ubuntu/macos × go1.22/1.24).

The 6 pytest failures are pre-existing — same set fails on main's v0.6.16 release CI run (https://github.com/thrillmade/logmind/actions/runs/26838927623). They're caused by a pin-version regression in v0.6.16 that hardcodes 0.6.16 somewhere in the workflow-pin update flow despite __version__ being 0.6.14 on this branch (v1-go-rewrite). Not in B6 scope — track separately.

clud-bug findings addressed in commit 14c4e15:

  • SaveMap: atomic temp-file + rename pattern (no more truncate-then-fail data loss)
  • httpGetJSON: io.LimitReader 1 MiB cap (no more unbounded buffering on misbehaving servers)

Both threads auto-resolved after the second clud-bug review pass.

thrillmot added a commit that referenced this pull request Jun 2, 2026
Workflow-template audit tests substitute `__version__` into expected
pip-install pin; main is at 0.6.16, this branch was at 0.6.14, all 3
audit-test assertions fail pre-existingly on every Go PR opened against
v1-go-rewrite (B5 #124, B6 #125, future waves).

20/20 tests in `tests/test_v0_2_1_audit_fixes.py` pass locally with the
bump.

Once merged, re-running CI on B5 #124 + B6 #125 unblocks both for
auto-merge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: thrillmot <thrillmot@users.noreply.github.com>
thrillmot added 2 commits June 2, 2026 17:19
# Conflicts:
#	.claude/worktrees/agent-a6c88d2dd73054cdc
#	docs/file-structure.md
#	docs/timeline.md
@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 1m 22s —— View job


Review (fix-push)

  • Load tools
  • Fetch prior review SHA + incremental diff (since 14c4e15)
  • Check prior threads — both resolved ✅
  • Scan new commits for new issues — 2 new commits (version bump rebase + merge), docs/Python version only, 0 new issues
  • Emit structured output

The two new commits after the last review pass are purely a Python version bump rebase (5fe3298) and a merge commit (23c723e). No Go source files touched. Both prior issues remain fixed; no new issues introduced.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🐛 Clud Bug review

This round: 0 critical · 0 minor · 0 resolved from prior · 0 still open

Found: 0 🔴 / 0 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned incremental diff (2 new commits: version bump + merge). 0 new findings; 0 prior threads open.
  • [evidence-based-review]: no findings to anchor; incremental diff is docs/version-only.
  • [respect-existing-conventions]: 0 findings; new commits follow existing patterns.

Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

# Conflicts:
#	docs/file-structure.md
#	docs/timeline.md
#	internal/cli/root.go
@claude

claude Bot commented Jun 2, 2026

Copy link
Copy Markdown

Claude finished @thrillmot's task in 4m 2s —— View job


Review (fix-push)

  • Load tools
  • Fetch prior review SHA + incremental diff (23c723e22144ff)
  • Scan new commits for new issues — 2 new commits (B5 skill CLI merge), 0 new findings
  • Check prior threads — both prior issues fixed in 14c4e15, 0 still open
  • Emit structured output

The two new commits merge the B5 skill CLI into B6: internal/skill/ (scaffold, bench, audit, suggest, validate, provenance, suggest_llm) + internal/cli/skill*.go. All new HTTP response bodies use io.LimitReader (same pattern applied in 14c4e15); git exec calls use context.WithTimeout; API key flows via env var only. No critical issues found.

@github-actions

github-actions Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

🐛 Clud Bug review

This round: 0 critical · 0 minor · 0 resolved from prior · 0 still open

Found: 0 🔴 / 0 🟡 / 0 🟣

Per-skill scan

  • [critical-issues-only]: scanned incremental diff (B5 skill CLI merge, 2 new commits). 0 new findings; 0 prior threads open.
  • [evidence-based-review]: no findings to anchor; all HTTP/IO patterns verified at file:line against known-good patterns.
  • [respect-existing-conventions]: new commits follow existing patterns (LimitReader, context timeouts, env-var key handling). 0 findings.

Skills referenced: [critical-issues-only, evidence-based-review, respect-existing-conventions]

@thrillmot
thrillmot merged commit 2d15030 into v1-go-rewrite Jun 2, 2026
17 checks passed
@thrillmot
thrillmot deleted the feat/go-b6-config-doctor branch June 2, 2026 21:32
thrillmot added a commit that referenced this pull request Jun 2, 2026
Carries the v0.6.16 Python release's B2/B3/B4 deltas into the Go rewrite
so the Go binary has byte-identical parity with Python v0.6.16. B6 (PR
#125) already shipped the PATH probe + commit-msg hook — this PR
addresses the three remaining waves.

## Summary

- **B2 (post-merge hook body)** — Go body absorbs the v0.6.16
HEAD-vs-origin skip block that replaced v0.6.15's blanket default-branch
skip. Local merges that introduce new commits (multi-branch self-heal
case) now correctly trigger regen instead of being silently skipped.
Byte-identical to Python via `TestPostMergeBody_ByteIdenticalToPython`.

- **B3 (multi-branch self-heal regression tests)** — New
`internal/timeline/merge_driver_test.go` ports the three v0.6.16 Python
tests:
  - `TestMergeDriverSelfHealsTwoConcurrentBranches`
  - `TestMergeDriverSelfHealsThreeConcurrentBranches`
  - `TestMergeDriverSelfHealsSquashMerge`

All three use `exec.Command` against real `git` + `logmind` binaries (no
mocks). Gated by `//go:build integration` since the merge driver shells
out to `logmind`. CI must install the binary first: `go install
./cmd/logmind && go test -tags=integration ./internal/timeline/...`.

- **B4 (AGENTS.md templates)** — Embedded full template bumped v5→v6 +
slim variant v7-pointer→v8-pointer with REQUIRED-framing heading +
DO-NOT-git-commit blockquote that pairs with the v0.6.16 commit-msg
hook. `inserter.matchingTemplate` accepts both old and new markers so
existing repos refresh into the new body without manual intervention.

## Cross-binary parity contract

Python source-of-truth files (`src/logmind/core/gitattributes.py`,
`src/logmind/templates/AGENTS.md.template`,
`src/logmind/templates/AGENTS.md.slim.template`) on this branch were
also overwritten with v0.6.16 content so the existing
byte-identical-to-Python parity tests can compare against the same
shape. Without this the parity helpers
(`TestPostMergeBody_ByteIdenticalToPython`,
`TestTemplates_ByteIdenticalToPython`) would fail because they shell to
in-tree `src/logmind`.

## Verification

```bash
go test ./...
PYENV_VERSION=3.11.8 go test -tags=integration ./internal/timeline/...
```

All Go tests green.

## Subtle gotcha: PYENV_VERSION must propagate into git hooks

The integration tests need every subprocess to inherit `PYENV_VERSION`
so git's post-merge hook resolves `logmind` to the right Python binary.
Without it, the pyenv shim resolves to "system" Python which may have an
older `logmind` installed locally (observed 0.3.4) → hook regen produces
wrong format → assertions fail. Documented in the `testEnv()` helper's
doc comment.

## Test plan

- [x] Unit tests pass: `go test ./...`
- [x] Integration tests pass: `PYENV_VERSION=3.11.8 go test
-tags=integration ./internal/timeline/...`
- [x] Post-merge hook body byte-identical vs Python v0.6.16
- [x] AGENTS.md templates byte-identical vs Python v0.6.16
- [ ] Cross-binary diff harness deferred until B6 (#125) merges and Go
`init` command exists

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: thrillmot <thrillmot@users.noreply.github.com>
@thrillmot thrillmot mentioned this pull request Jun 3, 2026
7 tasks
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