Skip to content

fix(core,content): stop generated SvelteKit routes dirtying the working tree - #2201

Open
willgriffin wants to merge 3 commits into
mainfrom
feat/issue-2198-sveltekit-route-drift
Open

fix(core,content): stop generated SvelteKit routes dirtying the working tree#2201
willgriffin wants to merge 3 commits into
mainfrom
feat/issue-2198-sveltekit-route-drift

Conversation

@willgriffin

@willgriffin willgriffin commented Aug 3, 2026

Copy link
Copy Markdown
Contributor
{"schema":"hv-agent-run:v1","runtime":"claude","session":"8a94e288-0358-403f-a3a6-8e81a80e6c5d","issue":"2198","head_sha":"274fccd02208b3ac4296ce17b26abcbeac2a4498","policy_revision":"1.0.0","status":"complete"}

Summary

Root cause

updateGeneratedRouteIgnoreBlock() recognized the legacy pair only when the line under # SMRT auto-generated routes (from Vite plugin) equalled ${routesDir}/**/+server.ts for the routesDir configured today. smrt-content adopted the plugin at src/routes/api and later moved to src/routes/api/v1, so its pair never matched and survived. The generator then appended its bounded block after the surviving !src/routes/api/v1/**/+server.ts negation, leaving rules that contradict each other.

Separately, the 96 generated routes under src/routes/api/v1 were tracked only because that stale negation exposed them. They were swept into #1863 alongside the handwritten dev-app pages; nothing described them as intentional output.

pnpm build does not reproduce — content/assets/images build in library mode (SMRT_PACKAGE_BUILD=1), which skips route generation. Generation runs from configResolved, so pnpm dev (or svelte-kit sync) is what dirties the tree.

Why untrack rather than commit the regenerated output

The committed routes are stale against today's generator, and the generator's output is not Biome-formatted:

tree biome ci --diagnostic-level=error on packages/content/src/routes/api/v1
committed 6 errors
regenerated 117 errors, 99 files reformatted

They are format diagnostics — emitted import lists, call expressions, and JSON.stringify-derived literals exceed lineWidth: 80 or use double quotes. Committing the regenerated output means running biome --write over 99 files, after which the committed text no longer matches generator output and the next dev run re-dirties it. Emitting byte-exact formatted code for arbitrary model and field names would mean reimplementing the formatter in the generator, and would regress the moment a name got longer.

Untracking matches packages/assets and packages/images (which track none), the generator's own stated intent, files: ["dist"] (the routes ship nowhere), and .claude/rules/testing.md"Test generators, not generated output." Regression coverage lands in packages/core against the generator.

Lint coverage improves

The 5 handwritten handlers under src/routes/api/v1 (contents/[id]/chat/**, images/**) stay tracked and gain coverage: the bounded block lists only generator-owned paths, where the blanket negation exposed everything indiscriminately.

contents-api.test.ts imports two generated routes. The test script runs svelte-kit sync first, which regenerates the tree, so all 302 content tests pass with none committed — verified by hiding all 96 files and re-running.

Known limitation (#2199, pre-existing)

svelte-check, via pnpm typecheck, runs a second generation pass at the plugin's default routesDir. It deletes the v1 tree and emits a duplicate surface under src/routes/api/. That is pre-existing on main, masked there by the broad glob this PR removes, and unrelated to this fix — a .gitignore cannot influence which routesDir the plugin uses. The documented guard in packages/content/.gitignore keeps route files out of git status under both block shapes; only the .gitignore itself still churns. Filed as #2199 with the investigation so far; the guard is to be deleted with that fix.

Follow-up: Coverage Gate (2nd commit)

scripts/check-coverage.mjs runs pnpm exec vitest run --coverage directly, bypassing the package's test script and its svelte-kit sync. With the routes untracked, contents-api.test.ts could not resolve its imports, so the gate reported content (T2): no coverage produced.

The gate already runs pnpm run --if-present generate:test first for exactly this case (core builds its test manifest there). Content's now points at svelte-kit sync, marked cache: false in packages/content/turbo.json because the root task's outputs describe core's manifest — a turbo cache hit would restore nothing and silently skip the sync.

Gate now reports content (T2) 72.07% (floor 70%) and core (T1) 86.21% (floor 80%), verified from a true fresh-checkout state with every generated route deleted.

Validation

  • pnpm test — core 3179 passed / 45 skipped (270 files); content 302 passed / 6 skipped (45 files)
  • content tests re-run with all 96 generated routes removed from disk — same result
  • npx @biomejs/biome@2.5.2 ci --diagnostic-level=error . — exit 0 (the CI Lint job command)
  • turbo run typecheck — core + content exit 0; assets + images exit 0 and leave a clean tree (their routesDir is already the default, so they need no guard)
  • pnpm smrt dev:knowledge-check — 0 errors, 0 warnings; pnpm check:agents-chain — 64 chains under limit
  • node scripts/check-coverage.mjs — no tiered packages affected
  • idempotency: a second dev-server regeneration rewrites nothing (Updated .gitignore is not logged) and leaves zero untracked noise

Closes #2198
Refs #2199


Note: an orphaned Agent Policy run (workflow id 315669746, since deleted) sat queued on this head and could not be rerun, blocking the required context. It was force-cancelled and this edit re-triggers Agent Policy via pull_request: edited.

Lifecycle re-trigger: the org-required Agent Policy run and this repo's own share the concurrency group agent-policy-diagnostic-2201, so overlapping push/edit events wedged the required run in queued. Cleared and re-fired once.

Copilot AI review requested due to automatic review settings August 3, 2026 15:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses persistent working-tree churn caused by SMRT’s SvelteKit route generator by (1) making legacy .gitignore migration robust to routesDir changes and (2) ensuring generated route output is treated as build/dev-time output rather than tracked source, aligning content with assets/images.

Changes:

  • Core: migrate legacy generated-route .gitignore entries by shape (including negations) and continue stripping legacy entries even when a bounded managed block already exists.
  • Packages: replace legacy broad ignore rules in assets/images with bounded, explicit managed blocks for generator-owned +server.ts paths.
  • Content: remove previously committed generated route handlers and document the intended “generator output is not tracked” workflow in AGENTS.md.

Reviewed changes

Copilot reviewed 103 out of 103 changed files in this pull request and generated no comments.

Show a summary per file
File Description
packages/images/.gitignore Replaces legacy broad ignore rule with bounded managed block for generated routes.
packages/core/src/vite-plugin/sveltekit-generator.ts Updates .gitignore managed-block logic to migrate legacy entries by shape and strip stale negations reliably.
packages/core/README.md Documents updated migration behavior and clarifies that generated routes should not be committed.
packages/content/src/routes/api/v1/contentversions/restoreIntoContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/listForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/getVersion/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/getNextVersionNumber/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/getLatestPublishedForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/getLatestForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/createSnapshot/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentversions/[id]/transparency/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/transparency/preview/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/transparency/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/review-profiles/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/review-profiles/[profileKey]/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/governance/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/fact-audit/repair/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/fact-audit/evidence/status/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/fact-audit/evidence/repair/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/fact-audit/claims/recheck/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contents/[id]/fact-audit/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreviews/listForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreviews/getLatestForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreviews/createFromResult/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/unlink/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/setLinks/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/getForTarget/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/getForSource/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/detach/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/byRight/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/byLeft/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentreferences/attach/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentgovernanceprofiles/getByKey/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentgovernancepolicies/getByKey/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentgovernanceassignments/resolveForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentfeedsources/findByStatus/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentfeedsources/findActive/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcorrections/listForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcorrections/issue/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcorrections/getPublishedForContent/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributors/getByProfileId/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributors/getByEmail/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributors/findOrCreateByEmail/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributiontypes/getByKey/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/submit/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/ingest-email/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/withdraw/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/revisions/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/request-changes/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/reject/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/promote/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributions/[id]/approve/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/src/routes/api/v1/contentcontributionattachments/listForRevision/+server.ts Removes tracked generated route file (now treated as generated output).
packages/content/AGENTS.md Documents the “do not track generated routes” policy and the current typecheck spillover behavior/guard.
packages/assets/.gitignore Replaces legacy broad ignore rule with bounded managed block for generated routes.

The legacy `.gitignore` migration matched the header only when the line
beneath it equalled a pattern derived from the *current* `routesDir`.
`@happyvertical/smrt-content` adopted the plugin at `src/routes/api` and
later moved to `src/routes/api/v1`, so its pair never matched and survived
every regeneration; the bounded block was then appended after the surviving
`!src/routes/api/v1/**/+server.ts` negation, leaving contradictory rules.

Migrate by shape instead: take the recognized header plus the contiguous run
of recursive `+server.ts` wildcards beneath it, negations included, stopping
at the first line that is neither (a comment always ends the run). Migration
now also runs once the bounded block exists, because a project generated by
the first #2185 release kept its unmatched pair, and that stale negation
silently re-includes whatever the block stops listing. An application-owned
broad rule elsewhere in the file is still preserved.

Stop tracking the 96 generated route files in `packages/content`. They were
swept into #1863 alongside the handwritten dev-app pages and the negation
that exposed them; nothing described them as intentional output. They are not
Biome-formatted — regenerating produces 117 `format` errors across 99 files
against 6 today — so tracking them means a Lint job gated on output no one
edits, and emitting byte-exact formatted code for arbitrary model and field
names is not something the generator can promise. `packages/assets` and
`packages/images` already track none, `files: ["dist"]` ships none, and
`.claude/rules/testing.md` says to test generators, not generated output.

`contents-api.test.ts` imports two of them; the `test` script runs
`svelte-kit sync` first, which regenerates the tree, so all 302 content tests
pass with none committed. The 5 handwritten handlers under `src/routes/api/v1`
stay tracked and gain lint coverage: the bounded block lists only
generator-owned paths, where the blanket negation covered everything.

Regenerate the bounded block for all three packages. Regeneration is now
idempotent — a second dev run rewrites nothing.

`packages/content/.gitignore` keeps a documented spillover guard: `svelte-check`
runs a second generation pass at the default `routesDir`, emitting a duplicate
API surface under `src/routes/api/` and deleting the v1 tree. That is
pre-existing and tracked separately in #2199; the guard keeps route files out
of `git status` under both shapes so this change does not unmask it.

Closes #2198
Refs #2199
@willgriffin
willgriffin force-pushed the feat/issue-2198-sveltekit-route-drift branch from 7b5f49f to 24221a0 Compare August 3, 2026 17:03
`scripts/check-coverage.mjs` runs `pnpm exec vitest run --coverage`
directly, bypassing the package's `test` script and therefore its
`svelte-kit sync` step. With the generated routes no longer committed,
`contents-api.test.ts` cannot resolve `./routes/api/v1/contents/+server`,
the suite fails to load, no coverage summary is written, and the gate
reports `content (T2): no coverage produced` instead of a percentage.

The gate already runs `pnpm run --if-present generate:test` first for
exactly this reason — core uses it to build its test manifest. Point
content's at `svelte-kit sync`, which regenerates the whole route tree.

Mark the task `cache: false` for this package: the root `generate:test`
declares core's manifest paths as its `outputs`, so a turbo cache hit
would restore nothing for content and silently skip the sync.

Coverage gate now reports content (T2) 72.07% against a 70% floor.

Refs #2198
@willgriffin
willgriffin force-pushed the feat/issue-2198-sveltekit-route-drift branch from 24221a0 to 6a3f3d9 Compare August 3, 2026 17:30
@have-claim-reconciler have-claim-reconciler Bot added the status: blocked Reviewable but blocked from merge label Aug 5, 2026
@have-claim-reconciler

Copy link
Copy Markdown

Implementation lease expired. PR is ready for review but blocked pending handoff.

@willgriffin willgriffin removed the status: blocked Reviewable but blocked from merge label Aug 5, 2026
@willgriffin
willgriffin enabled auto-merge August 6, 2026 02:35
@willgriffin
willgriffin disabled auto-merge August 6, 2026 02:35
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.

fix(core,content): SvelteKit route generation dirties the working tree on every dev run

2 participants