# Diagnose and fix the content full-suite hang

## Objective

Continue only Task 17 from implementation checkpoint `c5fdc7953241b8aa134a0ef274c6c45edca4b916`. Identify the exact `@platform-modules/content` test, test-file interaction, or leaked process/resource that kept `pnpm --filter @platform-modules/content test` alive past 600 seconds; fix only that verified cause; review the locale-environment change already committed in both embedded-Postgres harnesses; run bounded Node 22 evidence; and commit the scoped repair. Do not begin Tasks 2–16 or 18 and never modify `../mod-cms` or any sibling worktree.

The planner copy `specs/8aae5cd5_content-test-hang.md` is expected orchestration state after the baseline commit, not an implementation change or evidence that the hang is fixed.

## Sources of truth and invariants

Read before editing:

1. `docs/specs/2026-08-08-content-types-complete-design.md`.
2. `docs/specs/2026-08-08-fields-complete-design.md` only where the mirrored harness/transaction contract is relevant.
3. `docs/specs/2026-08-08-mod-cms-content-model-completion-design.md`, especially truthful adapter atomicity.
4. `docs/plans/2026-08-08-mod-cms-content-model-completion.md`, Task 17 only.
5. `docs/standards/coding-standard.md` §2 and §4.
6. `specs/ab54f1f4_task-17-finalization_v2.md` for the prior timeout and warning requirements; this narrower plan supersedes its broad rerun/review work.

Keep these contracts fixed:

- D1 continues to expose ordinary queries plus one genuine Cloudflare binding atomic batch. It has no callback transaction or transaction identity.
- PostgreSQL continues to use a genuine callback transaction with callback-scoped opaque identity, real rollback, inactive-capability rejection, and independent-transaction rejection.
- Content/fields Task 17 remains a flat-corpus read/conformance floor. Do not add registry enforcement, migrations, later stores, coordinators, or host integration.
- Do not weaken, skip, mock, or replace the real Miniflare D1 and embedded PostgreSQL fixtures to make tests terminate.
- Do not raise global test/hook timeouts, disable leak detection, force Vitest to exit, suppress output, or add arbitrary sleeps/retries.
- Do not touch `apps/mod-cms`, `../mod-cms`, `packages/fields` behavior, generated `dist`, or unrelated packages. A mirrored correction to `packages/fields/src/pg-harness.ts` is allowed only if review proves the already-checkpointed locale change itself is wrong; do not rerun the fields full suite.
- Every Node/pnpm command uses Node 22 by starting the shell with `export PATH="$HOME/.nvm/versions/node/v22.23.1/bin:$PATH"`. Confirm `node --version` once. Run commands serially and judge their exit status, including timeout status, rather than matching words in output.
- Use bounded ordinary diagnostics only. Do not launch resource-exhaustion probes or broad parallel runners on this shared machine.

## 1. Freeze the baseline and inventory likely resource owners

1. Verify `git rev-parse HEAD` equals `c5fdc7953241b8aa134a0ef274c6c45edca4b916`; inspect `git status --short --branch`. The only expected addition is this planner artifact. Do not reset, amend, squash, or rewrite the checkpoint history.
2. Record `git show --stat c5fdc795`, `git diff c5fdc795^..c5fdc795 -- packages/content/src/pg-harness.ts packages/fields/src/pg-harness.ts`, and `git diff --check`.
3. Use `vitest list` (not a test run) to freeze the content file/test inventory. Inspect lifecycle ownership in:
   - `packages/content/vitest.config.ts` and `tooling/vitest-config/index.js`;
   - `packages/content/src/pg-harness.ts` and every content test calling `startPg()`;
   - `packages/content/src/conformance.test.ts` for Miniflare disposal;
   - every content test calling `createPgliteClient`, especially the repeated `beforeEach` creation in `store.test.ts`, `migrate.test.ts`, `settings.test.ts`, taxonomy tests, and revisions tests;
   - `packages/db/src/postgres/pglite.ts` only to understand whether the returned handle exposes an owned close/dispose path. Do not change the DB seam without direct evidence that this is necessary.
4. Build a short table of each resource owner (PGlite, Miniflare, `pg.Pool`, embedded `postgres` child, temporary data directory), where it is created, and where/if it is deterministically released. This is the hypothesis list, not yet permission to edit.

## 2. Locate the hang with bounded discriminating runs

Do not immediately rerun the unchanged full package suite. Keep complete stdout/stderr and elapsed time for each run. Add `--reporter=verbose --reporter=hanging-process` to diagnostic Vitest runs so the last completed test and active handles are visible.

1. First prove the known targeted store file still terminates, with a strict bounded wall clock:

   ```bash
   pnpm --filter @platform-modules/content exec vitest run --config vitest.config.ts \
     --reporter=verbose --reporter=hanging-process src/store.test.ts
   ```

   Do not retry it unchanged if it times out. Record the final named test reached, test count, elapsed time, exit/timeout status, and reported handles.
2. Partition by resource class rather than spending another 600 seconds:
   - run the files with no PGlite/Miniflare/embedded-PG ownership as one bounded group;
   - run PGlite-owning files in two explicit halves;
   - run each embedded-PG/Miniflare file separately: `revisions.test.ts`, `conformance.test.ts`, `search.test.ts`, `store.concurrency.test.ts`, `taxonomy.migrate.test.ts`, and `taxonomy.move.test.ts`.
   Use roughly 90–120 seconds for lightweight groups and at most 180 seconds for a real-DB file. Commands are serial; a timeout is a red diagnostic result, not grounds to rerun unchanged.
3. If every file passes alone, test interaction/leak accumulation with explicit two-file and then cumulative subsets. Start with adjacent resource owners and the high-churn PGlite files, then combine the PGlite group with one embedded-PG file. Bisect the smallest ordered subset that reproduces delayed exit. Do not run all files until a candidate fix exists.
4. If a file itself hangs, use Vitest `-t` filters to bisect its describe/test blocks. For `search.test.ts`, isolate each real-PG describe because that file starts five separate servers. For a PGlite-heavy file, compare one test with a bounded multi-test subset to distinguish a behavioral deadlock from accumulated unclosed handles.
5. Correlate the hanging-process reporter with repository ownership. For an embedded child, verify whether `stop()` closes both pools, terminates and awaits the child’s actual exit, and removes the data directory only after exit. For PGlite, verify whether each created client reaches its real close method. For Miniflare, verify `dispose()` executes on every success/failure branch.
6. Stop diagnosis only when there is a minimal reproducer and a precise cause statement such as “N unclosed PGlite clients from these hooks keep this worker alive” or “the embedded Postgres stop path sends SIGKILL but never awaits child exit.” A merely slow full suite, a guessed leak, or one successful retry is not sufficient.

## 3. Review the checkpointed locale change

Review both one-line changes in `packages/content/src/pg-harness.ts` and `packages/fields/src/pg-harness.ts` before deciding to retain them.

1. Run `locale -a` directly under the same explicit `LC_ALL=C LANG=C` environment and record exit status and stderr. Then run the smallest content real-PG fixture (normally content conformance) and the fields conformance fixture once, serially, with full unfiltered output.
2. Verify locale precedence and intent across `bestLocale()`, `localeEnv`, `initdbCached` cache-key inputs, explicit `--lc-messages`, and the spawned server. The fix must select a valid deterministic locale and eliminate repository-controlled locale warnings; it must not merely hide stderr. Note that `LC_ALL` overrides category-specific environment variables, so retain the current code only if the explicit initdb argument and runtime environment remain internally truthful.
3. If the checkpointed environment addition is correct and warning-free, retain it unchanged and document that decision. If it is wrong, correct only the harness locale setup in both mirrored files and rerun the two conformance files. No production behavior/spec change is implied by a test-harness-only correction.

## 4. Make the smallest verified fix with the requested model

For each verified defect, use a fixer running **`gpt-5.6-terra` at `high` thinking**. Give it the minimal reproducer, active-handle/process evidence, fixed contracts above, exact allowed files, and RED/GREEN command. Do not ask another model to author fixes.

1. Add or adjust the smallest deterministic regression that fails because the resource is not released or shutdown is incomplete. Prefer testing the lifecycle helper directly or asserting the minimal multi-file reproducer terminates; do not pin arbitrary timing when deterministic ownership can be asserted.
2. Run the reproducer for RED once, apply the minimum robust fix, and rerun it for GREEN.
3. Likely corrections, only when supported by evidence, are:
   - content test fixtures retain the actual PGlite handle and close it in `afterEach`/`afterAll`, including setup-failure cleanup, rather than creating anonymous clients that cannot be released;
   - the content embedded-PG harness makes `stop()` idempotent, closes both pools, terminates the child, awaits its exit with a bounded escalation path, clears startup timers/listeners, and removes the data directory after process exit;
   - content conformance disposes Miniflare and PostgreSQL on every partial-start/failure path.
   These are hypotheses, not a mandate to change all three.
4. Do not expose a new public production API merely to facilitate tests if the existing concrete PGlite handle already owns a close method. If the only robust correction truly changes a public DB/content contract, stop implementation, amend the authoritative boundary spec and Task 17 plan first, then resume. Test-harness lifecycle fixes do not require a spec amendment.
5. Preserve all genuine database semantics. No fake transaction, sequential D1 `BEGIN`/`COMMIT`, callback shim, transaction cast workaround, mocked rollback, or skipped real fixture is acceptable.

## 5. Focused verification on the repaired tree

Run serially under Node 22, reusing successful receipts from the identical tree instead of repeating them:

1. The exact minimal reproducer from §2, with verbose and hanging-process reporters.
2. Every content test file directly touched by the fix.
3. Content Task 17 conformance:

   ```bash
   pnpm --filter @platform-modules/content exec vitest run --config vitest.config.ts src/conformance.test.ts
   ```

   It must visibly execute genuine D1 binding-batch rollback and genuine PostgreSQL callback rollback/identity and exit without owned warnings.
4. Fields conformance only if needed to validate/repair the mirrored locale harness:

   ```bash
   pnpm --filter @platform-modules/fields exec vitest run --config vitest.config.ts src/conformance.test.ts
   ```

5. Only after the minimal reproducer is green, run the formerly hanging full content command exactly once with a 10-minute outer wall bound:

   ```bash
   pnpm --filter @platform-modules/content test
   ```

   Require normal process exit, all discovered files/tests passed, no skipped mandatory fixture, and no project-controlled warning. A timeout, forced exit, or completed tests followed by a stuck process is red.
6. Run `pnpm --filter @platform-modules/content typecheck` and `pnpm --filter @platform-modules/content build`. If `packages/db` was unavoidably changed, also run its directly affected test, typecheck, and build. If the fields harness changed, run fields typecheck in addition to its conformance file. Inspect successful output for warnings.
7. Run `git diff --check`.

## 6. Scope audit, commit, and precise receipt

1. Inspect `git diff --name-only c5fdc795`, `git diff --stat c5fdc795`, the complete uncommitted diff, and `git status --short`. Remove generated `dist`, temporary PostgreSQL state, Miniflare state, caches, logs, and scratch output. Reject changes for Tasks 2–16/18, product code unrelated to the proven cause, `apps/mod-cms`, or any sibling checkout.
2. Confirm the final tree still has truthful D1 atomic batch semantics and genuine PostgreSQL callback transaction semantics, and that any locale edit is mirrored only where required.
3. Commit the scoped fix plus this planner artifact with normal hooks and an imperative subject such as `Fix content test resource cleanup`. Do not bypass hooks. Verify the final commit SHA and clean `git status --porcelain`.
4. Return a criterion-by-criterion receipt containing:
   - starting SHA and final commit SHA;
   - exact hanging test/subset or leaked resource and the minimal reproducer;
   - before/after elapsed time, exit status, passed test/file counts, last completed test, and hanging-process evidence;
   - exact files changed and why each was necessary;
   - locale direct reproduction, decision to retain/correct, and warning status for content and fields conformance;
   - D1 real binding-batch rollback evidence and PostgreSQL real callback rollback/identity evidence from conformance;
   - the one post-fix full content-suite result, content typecheck/build results, any conditional DB/fields checks, and `git diff --check`;
   - confirmation that no Tasks 2–16/18 or forbidden checkout was touched and final porcelain output is empty.

## Definition of done

Task 17’s hang repair is complete only when a bounded minimal run identifies the exact test/resource cause; the fix was authored with `gpt-5.6-terra/high`; the regression and directly affected tests pass on Node 22; the content full suite exits normally once on the repaired tree within 10 minutes; locale handling is explicitly reviewed and warning-free or corrected in both mirrored harnesses; D1 remains a real one-shot atomic batch without transaction identity; PostgreSQL remains a real callback transaction with identity and rollback; typecheck/build and diff checks are clean; the scoped work is committed; and the receipt reports exact evidence rather than a successful retry.