Skip to content

scaffold-init: jig's runtime-state .gitignore entries never reach scaffolded projects (tracked telemetry churns + blocks merges) #107

Description

@Kyarha

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:

git rm --cached <path>

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions