# Check Fixture Repo

> Validate the examples/test-repo fixture submodule and drive the built CLI against it end to end -- resolution schemes, groups, hooks, guidance precedence, executables, skill dependencies and `repo lint`, MCP presets and parameters, and the verify/repair round-trip -- in a throwaway state directory that cannot touch the developer's real configuration.

- Skill: `lorem-dev/check-fixture-repo` (Agent Skill)
- Install (CLI): `npx skillmds@latest add lorem-dev/check-fixture-repo`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lorem-dev/check-fixture-repo/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: lorem-dev (https://skillmd.com/u/lorem-dev)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lorem-dev/check-fixture-repo

---


# check-fixture-repo

The unit tests cover the domain core against an in-memory filesystem. This skill
covers the layer they cannot: the real `skillkeeper` binary, against a real Git
working tree, writing real files. It is the only check that would catch a
regression living in the wiring between the CLI, the adapters, and the
filesystem -- for example an adapter resolving the wrong destination root, or a
skill that resolves in `MemFs` but not on disk.

The fixture is `examples/test-repo`, a submodule tracking
[skillkeeper-test-repo](https://github.com/lorem-dev/skillkeeper-test-repo). It
is built so that every documented resolution path has exactly one example, and
its own README explains what each fixture drives.

## Isolation

The suite never touches the developer's machine state. `e2e/cli/src/cli.ts` is the
only way a spec can invoke the CLI, and it always sets **both**:

- `XDG_CONFIG_HOME`, which relocates `state.json` and `config.yaml`
  (`crates/skillkeeper-cli/src/wiring.rs`) -- otherwise a run would mutate the
  real tracked repositories and install records.
- `HOME`, which relocates the agents' **global** roots. Codex MCP installs always
  write to `~/.codex/config.toml` regardless of the project, and global-scope
  skill installs write under `~`.

Both matter, for different reasons, and forgetting either would silently corrupt
the machine -- which is why the harness owns them rather than each spec.

## Steps

### 1. Run the suite

```bash
pnpm test:e2e:cli
```

That is the whole check. The script behind it
(`scripts/e2e-prepare.mjs`) initializes the fixture submodule, force-pulls it to
the tip of its branch, and builds `target/debug/skillkeeper`; Jest then runs the
specs in `e2e/cli/tests/`.

Set `SKILLKEEPER_E2E_PIN_FIXTURE=1` to run against the fixture commit this
repository pins instead of pulling. CI does that for reproducibility; locally
pulling is the better default, because it surfaces fixture drift early.

### 2. Read the failures with the right lens

Each spec file answers a different question, and which one fails tells you where
to look:

| spec | covers | a failure means |
|---|---|---|
| `e2e/cli/tests/fixture.spec.ts` | the submodule is checked out, ASCII-only, and still has the manifests and file modes the rest of the suite assumes | the **fixture** drifted |
| `e2e/cli/tests/skills.spec.ts` | resolution schemes, `.skid.yml` identity, nested body paths, selective `+x`, guidance precedence, hook merge and consent, the delimited-text region, and both silent-failure modes of the resolver | the **product** changed |
| `e2e/cli/tests/mcp.spec.ts` | preset discovery including the group-scoped file, parameter substitution, both ledger files, the `.gitignore` guard for the secrets file, rules rendering, instance-name allocation, the Codex stdio-only skip, and removal | the **product** changed |
| `e2e/cli/tests/mcp-oauth.spec.ts` | the oauth preset's exact per-agent native shape, the copilot skip, that no agent's config ever carries a client secret, and `repo lint` on the deliberately invalid oauth preset | the **product** changed |
| `e2e/cli/tests/mcp-parameters.spec.ts` | link rendering, description truncation, option-value validation on install, and every mcp lint warning a description or parameter can trigger | the **product** changed |
| `e2e/cli/tests/repair.spec.ts` | `verify` -> `repair` -> `verify`, directory pruning, the bounds that keep repair inside the repaired skill, and uninstall reversing hooks and guidance | the **product** changed |
| `e2e/cli/tests/requires.spec.ts` | skill dependencies: every `repo lint` code the `requires` group triggers, the single-document `--json` form, both target-misuse exits, the transitive install closure, and the uninstall breakage report | the **product** changed |

If `fixture.spec.ts` fails, fix or re-pin the fixture. If it passes and another
spec fails, the CLI's behaviour moved and the fixture is telling you so.

Two of the fixture's twenty-six `SKILL.md` files must **not** resolve:
`deep-nesting/l2/l3/l4/too-deep-skill` (four group levels, one past the limit)
and `requires/invalid-strict` (a malformed strict `skillkeeper.requires`, which
fails the whole manifest). A count assertion that suddenly passes with 25
resolvable skills is a regression, not a fixed test.

`repo lint --path examples/test-repo` exits **1** by design: the fixture ships
`SK001`, `SK002`, `SK003`, `SK004`, and `SK005` errors alongside `SK010`,
`SK011`, `SK012`, and `SK013` warnings, one skill per code. The fixture's own
README documents which skill triggers which. Never "fix" the fixture to make
that exit 0.

### 3. Confirm nothing escaped the sandbox

```bash
git status --porcelain                        # expect clean
git -C examples/test-repo status --porcelain  # expect clean
```

Every spec runs the CLI with throwaway `HOME` and `XDG_CONFIG_HOME`
(`e2e/cli/src/cli.ts`), so a dirty tree here means a test wrote somewhere it should
not have -- a blocker, and a harness defect rather than a product one.

Note that a force-pull may legitimately leave the submodule pointer moved; that
shows as `modified: examples/test-repo (new commits)` in the superproject and is
not a failure. Restore it with `git submodule update -- examples/test-repo`, or
commit the bump on its own.

## Report

Jest already reports per-test results, so summarize rather than restate:

```
pnpm test:e2e:cli:        PASS / FAIL (N passed, N failed of 67)
failing spec(s):          <file> -> <test name>
attributed to:            fixture drift / product change / harness defect
working tree clean after: yes / no
```

For each failure give the test name, the assertion Jest printed, and which of the
three causes it is. Fixture drift and a product change need opposite fixes, and
the fixture's own README states the intended behaviour for every case the suite
covers.

