Protect claude-custom main from untracked config drift #61
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/claude-custom!61
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fix/claude-custom-main-protection"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
~/claude-customChanges
.gitignore: addplugins/known_marketplaces.jsonto ignored auto-updated fileshooks/check-claude-custom-clean.sh(new): SessionStart hook — runsgit status --porcelainon tracked files, warns if dirtysettings.json: register new SessionStart hookhooks/block-mcp-merge.sh: useallow_with_user_confirmationinstead ofask(ensures merge gate works in dontAsk mode)hooks/check-note-template.sh: check.contentfield (not just.html_content) — fixes template validation for create_notehooks/forgejo-helper.sh: check ALL remotes for Forgejo detection, not just origin. Forgejo takes priority over GitHub.plugins/blocklist.json,plugins/known_marketplaces.json: removed from tracking (now gitignored)Test Plan
~/claude-customhas dirty tracked files, session start shows warninggit statusis_forgejo_repocorrectly detects repos with Forgejo as non-origin remoteReview Checklist
Related Notes
phase-postgres-7d1-claude-custom-protection— sub-phase planbug-claude-custom-worktree-pollution— prior incident this preventssop-claude-config-development— config change SOPPR #61 Review
BLOCKERS
None found.
NITS
check-claude-custom-clean.sh line 26 — misleading wording. The warning says "These changes are untracked and at risk of being lost." The word "untracked" is confusing here because the script explicitly filters OUT untracked files (
grep -v '^??'). The changes it reports are uncommitted tracked changes. Suggest: "These tracked files have uncommitted changes and are at risk of being lost."forgejo_owner_repo hostname matching is less strict than is_forgejo_repo.
is_forgejo_repouses regex to extract the hostname from the URL, then checks*forgejo*against just the hostname. Butforgejo_owner_repodoes a simpler*forgejo*substring match against the entire URL ([[ "$url" == *"forgejo"* ]]). This would false-positive on a repo path containing "forgejo" (e.g.github.com/user/my-forgejo-tools.git). Low risk given current usage, but worth noting the inconsistency.check-note-template.sh line 62 — stale error message. The deny reason still says "Note html_content is empty" but the field being checked is now
.content // .html_content. Minor user-facing inconsistency if someone passescontentbut nothtml_content.SOP COMPLIANCE
fix/claude-custom-main-protection, not{issue-number}-description. There is no corresponding issue in the claude-custom repo issue list. The PR references a pal-e-docs plan slug (phase-postgres-7d1-claude-custom-protection) but no Forgejo issue. This appears to be a cleanup/protection PR done directly off a sub-phase plan rather than a tracked issue. Acceptable given this is config hardening, but noting the deviation.phase-postgres-7d1-claude-custom-protection,bug-claude-custom-worktree-pollution, andsop-claude-config-development.known_marketplaces.jsoncontained only plugin metadata (marketplace source, install location) — no secrets.CODE REVIEW
.gitignore(correct): Addingplugins/known_marketplaces.jsonto the ignore list alongside the already-ignoredblocklist.jsonandinstall-counts-cache.jsonis correct. These are auto-updated by Claude Code with timestamps that change every session, causing permanent dirty state. The two deleted files (plugins/blocklist.json,plugins/known_marketplaces.json) are properly removed from tracking to match.check-claude-custom-clean.sh(correct): Clean implementation.set -euo pipefailis good. The|| trueon the grep preventsset -efrom killing the script when there are no dirty files. Exits 0 unconditionally so it warns but never blocks session start. Correctly skips if the repo directory does not exist.settings.json(correct): New hook appended to the existing SessionStart hooks array, aftersession-start-context.sh. Ordering is sensible — context loads first, then the cleanliness check.block-mcp-merge.sh(correct and improved): Switching from"ask"to"allow_with_user_confirmation"is the right call."ask"can be bypassed in dontAsk mode;"allow_with_user_confirmation"always prompts. The added context (repo name and PR number in the confirmation message) is a good UX improvement. Reading stdin intoINPUTbefore using jq is correct.check-note-template.sh(correct): The change from.tool_input.html_contentto.tool_input.content // .tool_input.html_contentfixes template validation forcreate_notecalls that use thecontentfield. The jq//operator correctly falls back: trycontentfirst, thenhtml_content. This is a real bug fix.forgejo-helper.sh(correct): Three functions updated:is_forgejo_repo: Now iterates all remotes instead of just origin. Correct fix for repos where Forgejo is a non-origin remote.is_github_repo: Now callsis_forgejo_repofirst and returns 1 if any Forgejo remote exists. This ensures Forgejo priority, which matches the documented convention.forgejo_owner_repo: Now scans all remotes for a Forgejo URL before falling back to origin. Correct.VERDICT: APPROVED
Clean, well-scoped PR. The .gitignore fix and new SessionStart hook directly address the config drift problem. The three triaged hook improvements are all legitimate fixes with clear rationale. No secrets, no scope creep. The nits above are non-blocking suggestions.