Skip to content

T-SGIN-check-entities-gates-staged-content

Status: open/ready · Impact: medium · Complexity: small

AUTO-DEFINED: this spec was best-effort machine-authored by /sdlc:task-auto-define on 2026-07-18 because the task is autonomy: autonomous/pr. Review the Goal, Approach, Today, Files-to-touch, and Acceptance-criteria carefully before trusting it.

.claude/skills/project-check/check_entities.ts (wired as a pre-commit hook in lefthook.yml) reads entity files from the working tree, so any schema-inconsistent prose — for example a mid-edit definition.md — blocks EVERY commit, even ones that do not stage that file. During T-2KK8-capability-kind-grains-and-locations this forced the capability definition rewrite ahead of the task’s intended step order:

check_entities gates the working tree, not the index — definition.md had to be schema-consistent before ANY commit could land, forcing the definition rewrite ahead of the task’s step order

T-2KK8-capability-kind-grains-and-locations post-mortem

Gate the staged (index) content instead — e.g. resolve entity prose via git show :<path> for files with staged changes, or skip entity dirs with no staged changes — so unstaged WIP prose cannot block unrelated commits.

LocationRole today
.claude/skills/project-check/check_entities.tsEntity-prose consistency checker; main resolves --entities-dir (default apps/sdlc/lib/model/entities/) and reads every entity *.md from the working tree via readFileSync
.claude/skills/project-check/check_entities.ts#checkOneEntityPer-entity checks (schema load, backticked field and status-enum references in prose, body-only template) — all evaluated against working-tree file contents
lefthook.ymlWires the checker as the project-check-entities pre-commit command with no staged scoping, so unstaged WIP prose fails commits that never staged it
apps/sdlc/lib/services/docs/generate.ts#checkStagedRepo precedent for index-scoped gating: materializes the git index via git checkout-index into a temp tree so the docs-drift hook checks what the commit will actually contain
.claude/skills/project-check/tests/check_entities.test.tsFixture-driven harness invoking the checker with --entities-dir; no staged-mode coverage

Teach check_entities.ts a --staged mode and switch the lefthook.yml hook to it. In --staged mode the checker gates the git index: it exits 0 immediately when no staged path falls under the entities dir, and otherwise runs the existing checks against the staged copies of the entity files, so unstaged WIP prose (for example a mid-edit definition.md) can no longer block an unrelated commit. Bare invocations — CI, direct runs, and the fixture-driven tests — keep today’s working-tree behavior.

  1. Add a --staged boolean flag to parseArgs in .claude/skills/project-check/check_entities.ts (default off; ParsedArgs gains a staged field). Without the flag, behavior is unchanged.
  2. In main, when staged is set, resolve the git repo governing the entities dir via git rev-parse --show-toplevel run with -C at the resolved entities dir. If the dir is not inside a git repo, print an error naming the conflict and exit 2 (the existing script-failure exit class).
  3. Compute the entities dir’s repo-relative path and probe the index with git diff --cached --name-only limited to that path. Empty output means nothing relevant is staged: print a one-line skip note and exit 0 without reading any prose.
  4. Otherwise materialize the staged subtree into a mkdtempSync temp dir: feed git ls-files -z for the entities path into git checkout-index -z --stdin -f with --prefix pointing at the temp dir, then run the existing per-entity loop against the materialized dir. Rebase reported paths onto the real entities dir for readable citations and remove the temp dir in a finally block. This is the same no-stash, working-tree-untouched materialization pattern as checkStaged in apps/sdlc/lib/services/docs/generate.ts. Note the registered types keep their in-memory Zod schema (code imports resolve from the working tree regardless); only the prose and any fixture schema.json come from the index.
  5. Point the project-check-entities command in lefthook.yml at bun run .claude/skills/project-check/check_entities.ts --staged, with a comment carrying the index-vs-working-tree rationale (mirror the project-check-docs-drift comment).
  6. Extend .claude/skills/project-check/tests/check_entities.test.ts with staged-mode tests that build a throwaway git repo in a temp dir around a copied fixture: (a) unstaged drift with nothing staged under the entities dir exits 0 via the skip path; (b) clean content staged while a drifted edit sits unstaged in the working tree exits 0; (c) drifted content staged exits 1 with the usual citation; (d) --staged against a copied fixture outside any git repo exits 2.
LocationKindChange
.claude/skills/project-check/check_entities.tsmodifyAdd --staged flag: fast-skip when nothing is staged under the entities dir, else materialize the index subtree (git ls-files into git checkout-index) and run the existing checks against it
lefthook.ymlmodifyRun project-check-entities with --staged plus a rationale comment
.claude/skills/project-check/tests/check_entities.test.tsmodifyAdd staged-mode cases: skip-fast, staged-clean vs unstaged-drift, staged-drift fails, non-repo dir errors
  • AC-1: With --staged and no staged changes under the entities dir, the checker exits 0 with a skip note even when an entity *.md in the working tree carries drift that fails the bare working-tree run.
  • AC-2: With --staged and a drift-free staged copy of an entity file whose working-tree copy carries unstaged drift, the checker exits 0; staging the drifted content instead makes it exit 1 with the same file citation the working-tree mode prints.
  • AC-3: Without --staged the working-tree behavior is unchanged: the existing CASES table in .claude/skills/project-check/tests/check_entities.test.ts passes unmodified.
  • AC-4: The project-check-entities command in lefthook.yml invokes check_entities.ts with --staged.
  • AC-5: --staged against an entities dir outside any git repo exits 2 with a message naming the problem.
  • AC-6: running .claude/skills/project-check/tests/check_entities.test.ts under bun test passes, including new staged-mode tests covering AC-1, AC-2, and AC-5.
  • Converting the other working-tree project-check hooks wired in lefthook.yml (identifier-shape, pipe-tail, apps-imports, util-redeclarations, op-modules, skill-docs) to index gating — same failure class, separate follow-ups.
  • Repointing check_entities.ts at the sdlc CLI as a gate op (the T-WC1T repointing pattern) — the script stays a thin project-check adapter here.
  • Any change to the checks themselves (field references, status enums, body-only template, Operations-table shape) or to the semantics of the existing fixtures.
  • none — git and bun are already required by the pre-commit environment, and no other task blocks this.

Spawned by /sdlc:spawn-task-pr on 2026-07-18 UTC from T-2KK8-capability-kind-grains-and-locations in https://github.com/sksizer/dev.


← Back to Tasks