Skip to content

Commit 371d50c

Browse files
agent-era-aiclaude
andauthored
tracker: gitignore per-item status.json runtime state (#232)
* tracker: gitignore per-item status.json + warn agents in protocol text Per-item status.json is agent runtime state — rewritten on every stage transition by ralph and the stages-progression skill. Committing it produces diff noise and bakes wall-clock timestamps into history. - Add tracker/items/*/status.json to .gitignore. - git rm --cached the 8 already-tracked copies (kept on disk so ralph and the kanban keep reading them). - Add a bolded "Never commit or stage status.json" paragraph to the Agent status protocol block emitted into every stage of the regenerated stages-progression skill. - Test: assert the new sentence appears in every stage's emitted body. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * tracker: discovery + requirements + implementation notes for item-s-status-json-s Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * simplify: tighten status.json gitignore comment + protocol sentence Per the simplify pass: shorten the .gitignore comment to one line to match house style, drop the test comment that restated the assertion, and trim the agent-facing protocol sentence to drop reviewer framing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 339eb4a commit 371d50c

14 files changed

Lines changed: 151 additions & 48 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,3 +146,6 @@ vite.config.ts.timestamp-*
146146
# Devteam local cache
147147
.devteam
148148
.cpuprofile
149+
150+
# Per-item agent runtime state (rewritten on every stage transition)
151+
tracker/items/*/status.json

src/services/TrackerService.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,6 +1312,8 @@ Read the generated stages progression skill for the project's preferred working
13121312
13131313
You must keep \`tracker/items/<slug>/status.json\` current. It's the canonical live state for this item and ralph reads it to decide whether you're stuck or legitimately waiting. The kanban renders \`brief_description\` directly on the card, so write about *substance*, not stage identity.
13141314
1315+
**Never commit or stage \`status.json\`** — it's gitignored runtime state that rewrites on every update.
1316+
13151317
Schema:
13161318
\`\`\`json
13171319
{

tests/unit/tracker.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ describe('defaultStageFileContent renders status + gate protocol', () => {
351351
// The three-state enum is the canonical waiting signal.
352352
expect(content).toContain('waiting_for_input');
353353
expect(content).toContain('waiting_for_approval');
354+
expect(content).toContain('Never commit or stage `status.json`');
354355
});
355356

356357
test.each(STAGES)('every stage renders the Input mode section', (stage) => {

tracker/items/archive-kills-sessions/status.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

tracker/items/config-resets-clearing/status.json

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
title: Implementation — gitignore tracker/items/*/status.json
3+
slug: item-s-status-json-s
4+
updated: 2026-04-26
5+
---
6+
7+
## What was built
8+
9+
Three small changes, single PR:
10+
11+
1. **`.gitignore`** — added `tracker/items/*/status.json` with a 4-line comment explaining what it is and why it's ignored. Placed in the project-specific section after `.devteam` / `.cpuprofile`.
12+
13+
2. **`src/services/TrackerService.ts`** — appended one bolded paragraph to the `Agent status protocol` block (around line 1313, inside the protocol body that gets emitted into every stage section of every regenerated SKILL.md):
14+
15+
> **Never commit or stage `status.json`** — it's gitignored runtime state. Each write rewrites the timestamp, so committing it bakes wall-clock churn into history.
16+
17+
Placed right after the existing "canonical live state" paragraph and before the `Schema:` block, so the rule lands where the agent first reads the file's purpose. Both `.claude/skills/stages-progression/SKILL.md` and `.agents/skills/stages-progression/SKILL.md` regenerate from this source via `writeStagesProgressionSkillFiles` (called from `ensureStageFiles`, `saveWorkStyle`, `saveStagesConfig`); neither generated file is tracked in git, so no extra commit needed for them.
18+
19+
3. **`tests/unit/tracker.test.ts`** — added one assertion to the existing `defaultStageFileContent renders status + gate protocol` suite: every stage's emitted content must contain ``Never commit or stage `status.json` ``. This guards against accidental removal during future skill-text edits.
20+
21+
## Untracking the 8 already-committed copies
22+
23+
Ran `git rm --cached` on each of the 8 status.json files identified in `notes.md` § Findings (archive-kills-sessions, config-resets-clearing, merged-indicator-kanban, merged-item-stays-green, render-markdown-nati, running-status-chips, stages-progression-skill, terminal-ui-state-detection). The `--cached` flag drops them from the index without touching the worktree files, per AC3.
24+
25+
`git ls-files 'tracker/items/*/status.json'` is now empty. `git check-ignore` confirms both an existing untracked `status.json` (e.g. `render-markdown-nati`) and a newly-written one (this item's own `status.json`) match the new pattern.
26+
27+
## Key decisions
28+
29+
- **Sentence wording.** Bolded `Never commit or stage` so it stands out at the top of the protocol section. Included the *why* (timestamp churn) so the rule isn't cargo-culted.
30+
- **Placement.** New paragraph between the existing intro and the `Schema:` block — gives the rule visual weight without breaking the flow of the explanation. Alternative was inlining into the opening sentence; rejected because the sentence is already long.
31+
- **Did not add a `/submit` clause or pre-commit hook.** Per requirements AC7, gitignore is the only mechanical enforcement.
32+
- **Did not delete on-disk copies.** Per AC3 / user choice "Untrack only — keep files on disk".
33+
- **Did not regenerate the SKILL.md files in this commit.** They're not git-tracked anywhere (`.claude/` is in `.gitignore`; `.agents/` is currently untracked in the parent repo). They will regenerate locally next time the user opens the stages screen, advances an item, or otherwise triggers `ensureStageFiles``syncGeneratedTrackerArtifacts`.
34+
35+
## Notes for cleanup
36+
37+
- Verify `git status` on a clean checkout doesn't show any `status.json` under "Untracked files".
38+
- Verify the new test assertion runs (`npx jest tests/unit/tracker.test.ts -t "every stage includes the status.json protocol section"`).
39+
- Optional: trigger a regen of the SKILL.md files in the local checkout (e.g. by saving the work style from the Style tab, or just running the typecheck which exercises the codepath via tests) to confirm the new sentence renders. Not required for the PR — generated files aren't tracked.
40+
41+
## Test + typecheck
42+
43+
- `npx jest tests/unit/tracker.test.ts` → 138/138 passing (includes the new assertion).
44+
- `npx tsc -p tsconfig.test.json --noEmit` → clean.
45+
- Full `npx jest` had 3 unrelated pre-existing flaky e2e failures (project-filter.test.tsx among them) that pass when run in isolation; not caused by this change.
46+
47+
## Stage review
48+
49+
Implemented as scoped: `.gitignore` + `git rm --cached` + a one-paragraph addition to the protocol generator + a test assertion. No surprises during implementation; the skill regen path turned out to be a non-issue because the generated SKILL.md files aren't git-tracked. No deviations from requirements.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
---
2+
title: Discovery — status.json should not be committed
3+
slug: item-s-status-json-s
4+
updated: 2026-04-26
5+
---
6+
7+
## Problem
8+
9+
`tracker/items/<slug>/status.json` files are agent runtime state (current stage, working/waiting state, brief_description, ISO timestamp) that live inside the tracked `tracker/items/` tree. They are getting committed alongside real changes, producing diff noise on PRs and leaving stale snapshots in `main`. The user wants them gitignored, and asked where the rule (or enforcement) should live.
10+
11+
## Why it matters
12+
13+
Per the stages-progression skill, agents must update `status.json` "at every meaningful transition" — it's the canonical signal ralph and the kanban UI read to decide whether the agent is working, waiting for input, or waiting for approval. So the file churns constantly during normal work; timestamp + brief_description rewrite even when no decision was made. None of that belongs in commit history.
14+
15+
## Findings
16+
17+
1. **Files are tracked.** `git ls-files` lists 8 status.json files on this branch:
18+
- `tracker/items/{archive-kills-sessions,config-resets-clearing,merged-indicator-kanban,merged-item-stays-green,render-markdown-nati,running-status-chips,stages-progression-skill,terminal-ui-state-detection}/status.json`
19+
- History shows 10 distinct slugs have ever had a status.json committed; some have been touched by multiple PRs (render-markdown-nati = 4 commits, running-status-chips and merged-indicator-kanban = 3 each).
20+
2. **No gitignore entry.** `.gitignore` covers `node_modules`, `.devteam`, `.claude`, build outputs — nothing for `tracker/items/*/status.json`.
21+
3. **No skill or doc says "don't commit it".** The stages-progression skill (`.claude/skills/stages-progression/SKILL.md`, source: `tracker/stages.json` + the generator) repeatedly tells agents to update status.json but never warns them off committing it. The `/submit` skill (`/home/mserv/projects/devteam/.claude/skills/submit/SKILL.md`) calls `gh pr create --fill` after `/simplify`, no exclusion list. So whether status.json reaches a PR depends on whether the agent uses `git add <files>` (safe) or `git add -A` / `git add tracker/items/<slug>/` (picks it up). Both patterns appear in history.
22+
4. **It's pure runtime state.** `TrackerService.writeItemStatus` (`src/services/TrackerService.ts:487`) atomically writes `{stage, state, brief_description, timestamp}`. Timestamp is `new Date().toISOString()` on every write — every commit that includes it bakes a wall‑clock timestamp into history.
23+
5. **Tracked copies on `main` are stale snapshots.** They reflect whatever the state was when the PR was authored — typically `cleanup / waiting_for_approval`. They don't reflect post‑merge / archived state, so they're misleading even as a historical record.
24+
6. **TrackerService writes status.json from two contexts.** From a worktree (the agent advancing through stages — `writeItemStatus` at line 487) and from the main project checkout (the user advancing via the kanban — line 580 mirrors the canonical stage). Only worktree writes get committed in practice (the main checkout is rarely git‑added); both go to the same `tracker/items/<slug>/status.json` path.
25+
7. **Items with no worktree get a stub status.json materialised in the main repo** (lines 488–492 in `TrackerService.ts`). Same path, same gitignore implications.
26+
27+
## Recommendation
28+
29+
Two‑part, both small:
30+
31+
**1. Untrack and gitignore.**
32+
- Add `tracker/items/*/status.json` to `.gitignore`.
33+
- Run `git rm --cached tracker/items/*/status.json` once on the cleanup PR so the existing 8 tracked copies are removed from the index without deleting them on disk (ralph + kanban keep reading the local file).
34+
35+
**2. One‑line note in the skill source.**
36+
- Add a sentence right next to the existing "keep status.json current" line in `tracker/stages.json` (the source of truth that generates `.claude/skills/stages-progression/SKILL.md` and `.agents/skills/stages-progression/SKILL.md`): *"status.json is gitignored runtime state — never commit or stage it."* That puts the rule where the agent reads the obligation it's about to break.
37+
38+
That's enough. `gitignore` is the mechanical enforcement (covers `git add <file>`, `git add -A`, `git add tracker/items/<slug>/`); the only escape is `git add -f`, which agents shouldn't be doing. The skill note explains the rule for humans skimming the source.
39+
40+
## Where the guidance lives — answering the explicit question
41+
42+
- **Mechanical enforcement → `.gitignore`.** Single source of truth. One line.
43+
- **Pedagogical rule → `tracker/stages.json` (regenerated into the stages-progression skill).** Right beside the existing instruction to maintain the file.
44+
- **Probably not needed:** AGENTS.md changes, pre‑commit hook, CI check, `/submit` skill clause. They duplicate what `.gitignore` already enforces and add maintenance.
45+
46+
## Open trade-offs (defer to requirements)
47+
48+
- Whether the cleanup also deletes status.json from disk on the main checkout (probably no — harmless; kanban renders fine when absent).
49+
- Whether to also gitignore other ephemeral per‑item artifacts. None exist today. Punt unless the pattern broadens.
50+
- Whether to add a belt‑and‑suspenders pre‑commit hook — recommend skipping unless the gitignore approach proves insufficient.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
title: Requirements — gitignore tracker/items/*/status.json
3+
slug: item-s-status-json-s
4+
updated: 2026-04-26
5+
---
6+
7+
## Problem
8+
9+
`tracker/items/<slug>/status.json` files are agent runtime state (current stage, working/waiting state, brief_description, ISO timestamp) that live inside the tracked `tracker/items/` tree. They are getting committed alongside real changes, producing diff noise on PRs and leaving stale snapshots in `main`. The user wants them gitignored, and asked where the rule (or enforcement) should live.
10+
11+
## Why it matters
12+
13+
Per the stages-progression skill, agents must update `status.json` "at every meaningful transition" — it's the canonical signal ralph and the kanban UI read to decide whether the agent is working, waiting for input, or waiting for approval. So the file churns constantly during normal work; timestamp + brief_description rewrite even when no decision was made. None of that belongs in commit history.
14+
15+
## Summary
16+
17+
Stop committing per-item `status.json` files. Add one `.gitignore` entry, untrack the 8 currently-tracked copies (without deleting them on disk), and add a one-line rule to the generated stages-progression skill so agents reading the skill see it next to the existing "keep status.json current" instruction. No pre-commit hook, no `/submit` clause — `.gitignore` is the mechanical enforcement.
18+
19+
The skill text lives in `src/services/TrackerService.ts` (the `Agent status protocol` block around line 1310), so the rule lands there in TypeScript. Both `.claude/skills/stages-progression/SKILL.md` and `.agents/skills/stages-progression/SKILL.md` regenerate from that source.
20+
21+
## Acceptance criteria
22+
23+
1. `.gitignore` contains a new entry `tracker/items/*/status.json` (or equivalent pattern that matches every per-item status file). Running `git check-ignore tracker/items/<any-existing-slug>/status.json` on a fresh checkout reports it as ignored.
24+
25+
2. The 8 currently-tracked status.json files (listed in `notes.md` § Findings) are removed from the git index via `git rm --cached`, so `git ls-files | grep 'tracker/items/.*/status.json'` returns nothing on the resulting branch.
26+
27+
3. The on-disk copies of those 8 files are **not** deleted by this PR. After the change, `ls tracker/items/*/status.json` on the worktree still shows the existing files (untracked, but present), so ralph and the kanban keep reading them.
28+
29+
4. `git status` after running this branch's changes does not list any status.json under `Untracked files:` — the gitignore entry suppresses them.
30+
31+
5. The "Agent status protocol" template in `src/services/TrackerService.ts` (the block beginning `You must keep \`tracker/items/<slug>/status.json\` current.`) gains one sentence stating that status.json is gitignored runtime state and must never be committed or staged. The sentence is placed where an agent reading the skill cannot miss it — adjacent to or appended to that opening paragraph.
32+
33+
6. Both generated skill files (`.claude/skills/stages-progression/SKILL.md` and `.agents/skills/stages-progression/SKILL.md`) include the new sentence after regeneration. If a regen helper exists, it's run as part of this PR; if not, the generated files are updated to match the source.
34+
35+
7. No pre-commit hook is added. No `/submit` skill change is made. No CI check is added. `.gitignore` + the skill sentence are the entire enforcement surface.
36+
37+
8. No behavioural changes to `TrackerService.writeItemStatus`, `getItemStatus`, ralph polling, or the kanban: the file path stays at `tracker/items/<slug>/status.json`, the schema is unchanged, and a fresh write still creates the file (which gitignore does not block — gitignore only affects `git add`).
38+
39+
9. Existing unit tests in `tests/unit/tracker.test.ts` continue to pass. No new tests are required; the change is config + a documentation sentence.
40+
41+
## Edge cases / non-goals
42+
43+
- **Other ephemeral artifacts**: out of scope. None exist today; revisit if a new pattern appears.
44+
- **Archived item history**: out of scope. We don't preserve a "last status" for archived items — that information is captured by the move into `archive` in `tracker/index.json`.
45+
- **`git add -f tracker/items/<slug>/status.json`**: still works (gitignore can be overridden). Acceptable; agents shouldn't be force-adding ignored files, and the skill sentence makes the rule explicit if anyone tries.
46+
- **Status writes from the main checkout** (e.g., the user advancing via the kanban triggers `TrackerService.writeItemStatus` mirroring the new stage): now also gitignored, so the user's local checkout will no longer show those writes in `git status`. Intended.

tracker/items/merged-indicator-kanban/status.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

tracker/items/merged-item-stays-green/status.json

Lines changed: 0 additions & 6 deletions
This file was deleted.

0 commit comments

Comments
 (0)