Skip to content

test(15-5): AI schema regression test infrastructure (fixture loader + harness + seed fixtures)#114

Merged
Simplemart17 merged 4 commits into
mainfrom
feature/15-5-ai-schema-regression-tests
May 17, 2026
Merged

test(15-5): AI schema regression test infrastructure (fixture loader + harness + seed fixtures)#114
Simplemart17 merged 4 commits into
mainfrom
feature/15-5-ai-schema-regression-tests

Conversation

@Simplemart17

Copy link
Copy Markdown
Owner

Summary

  • NEW fixture loader + replay harness at src/lib/schemas/__tests__/fixture-replay.test.ts (8 cases) — walks __fixtures__/<schema>/*.json, replays each through corresponding Zod parser via it.each.
  • NEW src/lib/schemas/__fixtures__/ tree with 3 seed-schema dirs (writing-evaluation, dictation, mock-test-section) × 1 synthetic seed fixture each.
  • NEW operator runbook _bmad-output/planning-artifacts/runbooks/ai-fixture-capture.md documenting WHEN/HOW/WHERE to capture real fixtures.
  • INFRASTRUCTURE-ONLY scope — real fixture capture (10 per schema per spec target) is operator action requiring Sentry trace pulls or live OpenAI calls (cost-cap impact per Story 11-4).
  • 3 operator decisions resolved per Recommended (explicit FIXTURE_SCHEMA_MAP, _synthetic marker, 3 schemas vs all 36).

Story

  • Story ID: 15.5
  • Story Key: 15-5-ai-schema-regression-tests
  • Story File: _bmad-output/implementation-artifacts/15-5-ai-schema-regression-tests.md

Test plan

  • npx jest fixture-replay passes — +8 net cases.
  • Adding a new .json to any __fixtures__/<schema>/ dir produces a new passing test case without code changes.
  • Schema-rename safety: changing writingEvaluationSchema import name breaks TypeScript (good — catches drift).

🤖 Generated with Claude Code

3-agent adversarial review surfaced 12 actionable findings.

HIGH:
- BH-1: JSON.parse error path now wraps via loadFixture() so a
  malformed fixture's path appears in the error message, not just
  `SyntaxError at position N`.
- BH-2: NEW Case 6 enforces `_synthetic: true` marker on every
  committed fixture — privacy defense until a future operator-action
  `.real-fixtures.txt` manifest exists for real captures.
- BH-3 + EH-10: Runbook gains REQUIRED Privacy + GDPR section
  before Option A. User-derived French content (writing-evaluation
  errors[].original) may contain names/addresses/dates/contact info.
  Story 9-3 GDPR scrubber does NOT run on fixtures; Story 9-4 prompt
  wrapping does NOT protect captures; git history is irrevocable.
  Section mandates field-by-field sanitization + `_redacted: true`
  marker convention + prefer-synthetic-when-possible guidance.
- EH-9 (load-bearing): NEW Case 7 parallel strict-mode probe.
  The Zod schemas use default `.strip()` which silently drops
  unknown fields — defeating the regression-detection goal of
  the story. Probe runs `schema.strict().safeParse()` per fixture
  and reports extra-field drift as SOFT console.warn (does not
  fail the test — synthetic seeds pass by construction; real
  captures may flag drift for operator triage). Also corrected
  the runbook's misleading "Zod is strict() by default" claim.

MED:
- BH-4: Case 2 walks fs.readdirSync directly so EMPTY orphan dirs
  fail loud (not just dirs with files).
- BH-5: `FIXTURE_SCHEMA_MAP` uses `as const satisfies` for narrow
  value typing — value-typo at construction caught at compile time.
- BH-6: `dirent.isFile()` filter so a directory named `foo.json`
  doesn't crash readFileSync with EISDIR.
- BH-7 / EH-1: `stripMetadata` JSDoc documents the top-level-only
  contract — load-bearing for nested data passthrough.
- EH-6: case-insensitive `.json` match so `.JSON` / `.Json`
  files don't silently skip.

LOW:
- BH-12: Belt-and-suspenders FIXTURES_ROOT existence pin (Case 0).
- EH-2: Case 4 input gains nested `_note` for symmetric coverage
  alongside nested `_synthetic`.

Deferred (filed as follow-ups):
- 15-5-followup-real-fixture-manifest (operator-action allow-list)
- 15-5-followup-passage-ref-integrity (BH-8 — pre-existing schema gap)
- 15-5-followup-empty-fixture-boundary (EH-3 — empty-object edge)

Story file housekeeping: Status: review → done; populated File List +
Completion Notes per Acceptance Auditor APPROVE_WITH_NOTES.

+3 net test cases (8 → 11). All 4 quality gates green: type-check 0
/ lint 0 / prettier clean / jest 2170 tests pass.
@Simplemart17 Simplemart17 merged commit a43f630 into main May 17, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant