Summary
jig knows which of its runtime files are per-checkout local state — its own
.gitignore lists them — but that knowledge never reaches scaffolded projects.
The scaffolder ships a .gitignore secret floor and nothing else, so every
project jig scaffolds ends up tracking jig's own hook telemetry and
per-checkout markers. They then churn on every session and collide at merge
time.
Observed on jig 2.7.0.
Evidence
jig's own repo git-ignores six runtime paths:
.claude/skill-usage.jsonl
.claude/context-growth-read-events.jsonl
.claude/review-queue.json
.claude/settings.local.json
.claude/scheduled_tasks.lock
.jig/spec-ref
.jig/spec-ref even carries jig's own comment, which states the rule plainly:
Working-tree-local spec-attribution marker (slice 056-03) … Per-checkout state
— must NOT be tracked or travel across branches. Scoped to the file (NOT a
blanket .jig/) because .jig/test-command IS tracked.
But the plugin ships no .gitignore template at all (find <plugin> -iname '*gitignore*' → nothing). The only .gitignore writing is
_write_gitignore_secret_block() / _render_gitignore_block() in
skills/scaffold-init/scaffold.py, a marker-delimited block containing secret
patterns only (ADR-0013 security floor). Nothing covers .claude/ runtime
state.
Net effect: the ignore list exists only in the maintainers' working copy. Every
downstream project re-discovers the problem by hand.
Symptoms (one project, one day)
In a jig-scaffolded project, .claude/context-growth-read-events.jsonl was
tracked from the very first jig commit (swept in by the initial git add -A).
It's written by 8 hooks via hooks/scripts/lib/read_attribution.py, and — as far
as I can tell — read by nothing in 2.7.0. Results:
- 19 churn commits of pure telemetry noise.
- It blocked a
git merge --ff-only — a tracked file the hooks rewrite every
session is always dirty, so landing work stops until you discard it.
.jig/spec-ref produced a real merge conflict between two worktrees —
exactly the failure its own comment predicts ("must not travel across
branches"). This is worse than noise: with a multi-worktree flow, per-checkout
markers guarantee conflicts.
Proposal
Propagate the list the maintainers already maintain into the scaffolder — e.g. a
second marker-delimited runtime-state block alongside the secret block,
reusing the existing _upsert_marked_block() mechanic so re-runs stay idempotent:
# >>> jig runtime state (do not track) >>>
.claude/skill-usage.jsonl
.claude/context-growth-read-events.jsonl
.claude/review-queue.json
.claude/settings.local.json
.claude/scheduled_tasks.lock
.jig/spec-ref
# <<< jig runtime state <<<
Keeping it a separate block from the secret floor seems right: different
rationale, different ADR, and users may want to opt out of one without the other.
Note .jig/ must stay file-scoped, not blanket — .jig/test-command is meant to
be tracked (jig's own comment says so).
Migration wrinkle (please don't skip this)
A .gitignore entry is a no-op on an already-tracked file. Every project
already scaffolded is still broken after upgrading, because git keeps tracking
what it already tracks. Fixing it requires:
So the ignore-block alone only helps new projects. Options: have migrate
offer the untrack, or at minimum document the git rm --cached step in the
release notes — otherwise the existing install base silently stays broken.
Open question
Is context-growth-read-events.jsonl still earning its keep? I couldn't find any
reader in 2.7.0 — two write sites, zero consumers (unlike skill-usage.jsonl,
which routing-stats / gate-stats do read, though from the local checkout —
so it still shouldn't be tracked). If it's substrate for a future analyzer that's
completely reasonable; just noting it currently costs every downstream project
churn for no local benefit.
Summary
jig knows which of its runtime files are per-checkout local state — its own
.gitignorelists them — but that knowledge never reaches scaffolded projects.The scaffolder ships a
.gitignoresecret floor and nothing else, so everyproject jig scaffolds ends up tracking jig's own hook telemetry and
per-checkout markers. They then churn on every session and collide at merge
time.
Observed on jig 2.7.0.
Evidence
jig's own repo git-ignores six runtime paths:.jig/spec-refeven carries jig's own comment, which states the rule plainly:But the plugin ships no
.gitignoretemplate at all (find <plugin> -iname '*gitignore*'→ nothing). The only.gitignorewriting is_write_gitignore_secret_block()/_render_gitignore_block()inskills/scaffold-init/scaffold.py, a marker-delimited block containing secretpatterns only (ADR-0013 security floor). Nothing covers
.claude/runtimestate.
Net effect: the ignore list exists only in the maintainers' working copy. Every
downstream project re-discovers the problem by hand.
Symptoms (one project, one day)
In a jig-scaffolded project,
.claude/context-growth-read-events.jsonlwastracked from the very first jig commit (swept in by the initial
git add -A).It's written by 8 hooks via
hooks/scripts/lib/read_attribution.py, and — as faras I can tell — read by nothing in 2.7.0. Results:
git merge --ff-only— a tracked file the hooks rewrite everysession is always dirty, so landing work stops until you discard it.
.jig/spec-refproduced a real merge conflict between two worktrees —exactly the failure its own comment predicts ("must not travel across
branches"). This is worse than noise: with a multi-worktree flow, per-checkout
markers guarantee conflicts.
Proposal
Propagate the list the maintainers already maintain into the scaffolder — e.g. a
second marker-delimited runtime-state block alongside the secret block,
reusing the existing
_upsert_marked_block()mechanic so re-runs stay idempotent:Keeping it a separate block from the secret floor seems right: different
rationale, different ADR, and users may want to opt out of one without the other.
Note
.jig/must stay file-scoped, not blanket —.jig/test-commandis meant tobe tracked (jig's own comment says so).
Migration wrinkle (please don't skip this)
A
.gitignoreentry is a no-op on an already-tracked file. Every projectalready scaffolded is still broken after upgrading, because git keeps tracking
what it already tracks. Fixing it requires:
So the ignore-block alone only helps new projects. Options: have
migrateoffer the untrack, or at minimum document the
git rm --cachedstep in therelease notes — otherwise the existing install base silently stays broken.
Open question
Is
context-growth-read-events.jsonlstill earning its keep? I couldn't find anyreader in 2.7.0 — two write sites, zero consumers (unlike
skill-usage.jsonl,which
routing-stats/gate-statsdo read, though from the local checkout —so it still shouldn't be tracked). If it's substrate for a future analyzer that's
completely reasonable; just noting it currently costs every downstream project
churn for no local benefit.