Gate the Directus extension bundle on format, lint and build (Phase 0) - #245
Gate the Directus extension bundle on format, lint and build (Phase 0)#245Jan0707 wants to merge 4 commits into
Conversation
Two corrections to the formatter's own configuration, kept ahead of the reformat so that sweep runs under the final config rather than a config that changes underneath it. `jsxBracketSameLine` was removed in Prettier 3 and printed a deprecation warning on every single run. Verified it changed no output: exactly the same 64 files are unformatted before and after removing it, so the reformat in the next commit is byte-identical either way. The config now matches nuxt-app/.prettierrc exactly, minus the Tailwind plugin. Added a .prettierignore for `.claude`. `prettier . --write` walks the whole tree and was rewriting contributors' untracked `.claude/settings.local.json` — local tool state that nobody reviews. Verified with a control: a deliberately unformatted file inside `.claude/` is not flagged, while the same content outside it is. `node_modules` and `dist` are deliberately not repeated in that file; Prettier reads `.gitignore` as well, and the bundle's already covers both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMa3aosYRWyivC4DtPbSNr
Mechanical `prettier . --write` output, no hand edits. Formatting was
never enforced in this tree, so 64 files had drifted from the committed
.prettierrc — one of them the untracked local settings file excluded in
the previous commit, leaving 63 here. The gate that stops this
recurring arrives in the next commit.
Beyond whitespace, this strips semicolons (`semi: false`) and sorts
imports via @ianvs/prettier-plugin-sort-imports, so the diff is not
whitespace-only.
Import reordering is the one way a sweep like this can genuinely break
something, so it was checked first: the only load-time side-effect
imports are `import 'dotenv/config'` in the two Algolia CLI scripts, the
plugin keeps them in the same slot (after third-party, before local),
and the only process.env reads in those files sit below the entire
import block.
Verified against the artefact Directus actually loads, since lint, test
and build are all blind to a formatter by construction and 17 of 26
entries have no tests at all. Built dist/ before and after:
- app.js: all 43 string literals identical; after normalising every
identifier, exactly one structural difference, which is the unused
watch parameters removed in the next commit.
- api.js: same 18 module specifiers in the same order; 10,554 of
10,583 literals byte-identical. The 29 that differ are `entities`
lazy-init blobs varying only in Rollup's internal module variable
names, a knock-on of modules being emitted in a different order.
Static analysis could not push api.js past "almost certainly
equivalent", so the decisive check was to import both builds in Node and
compare the registry handed to Directus: deep-equal, 23 hooks in the
same order, 2 endpoints, 0 operations. That compares the registry's
shape, not the behaviour of the 23 handlers, which cannot be invoked
without a Directus host.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMa3aosYRWyivC4DtPbSNr
The bundle's CI job ran npm ci, ESLint and Jest. It did not check
formatting, did not typecheck, and never confirmed the artefact Directus
loads still compiles. This adds the two gates that need no source
changes, plus the config corrections behind them. Phase 0 of
docs/directus-extension-tooling-plan.md; no dependency versions change.
CI (bundle job now: format, lint, test, build)
- `prettier:check`, not `prettier` — the latter writes, so it would
pass by mutating.
- A build step. `directus-extension build` has its own Rollup/esbuild
pipeline that neither ESLint nor Jest exercises, so a change that
broke the loadable bundle could merge green.
- `node-version-file:` reading a new .nvmrc instead of a hardcoded 22,
so CI, the Docker image and local development have one file to
change rather than three. Local dev had already drifted to 24.19.0.
- `shared-code/**` added to the paths filter. The bundle imports from
there, so those changes were skipping the gate entirely.
Corrected the comment claiming this tree is frozen by the Directus
licence block. It is not: every @directus/* package the bundle uses is
MIT, and only the `directus` server package is blocked. The real reason
to stay on 22 is Dockerfile.directus — Node 24 brings npm 11, which
gates the install scripts this tree's native dependencies need.
Hermetic image build
Both `npm install` calls in Dockerfile.directus become `npm ci`, so the
image can no longer resolve versions the lockfile does not record.
Verified both lockfiles are in sync first (`npm ci --dry-run` exits 0 in
directus-cms and in the bundle), since otherwise this would land a
broken image build.
ESLint now covers the one Vue SFC
`files: ['**/*.ts']` had left presentation-publishable.vue as the only
unlinted source file. Adding eslint-plugin-vue surfaced 2 errors and 20
warnings, the warnings all being formatting rules fighting Prettier —
because eslint-config-prettier was already a dependency but the config
had never applied it. Harmless while the config was TypeScript-only and
enabled no stylistic rules; load-bearing the moment such a plugin
arrives. Applying it last takes the 20 to 0.
That left two genuine errors, unused watch callback parameters, removed
here. The gate was confirmed to actually fire by injecting an unused
variable and watching ESLint fail.
Also removed a dead ignore entry for `**/podcast-transcription/**`; the
directory is `podcast-transcript`, so it had never matched anything.
Docs
TESTING.md claimed exactly one function was tested; there are 15 files
and 205 assertions across 9 of 26 entries. AGENTS.md said formatting was
enforced in nuxt-app only, and pointed at nuxt-app's Prettier config as
if it were the only one.
Verified: prettier:check, lint, test (205/205) and build all green;
npm audit unchanged at 19, which Phase 3 addresses.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMa3aosYRWyivC4DtPbSNr
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR implements “Phase 0” gating for the Directus extension bundle so CI verifies code style and that the bundle still compiles, aligning the bundle with the repo’s existing “format-first” approach and reducing the risk of shipping an unbuildable Directus artefact.
Changes:
- Updates CI for the extension bundle to run prettier:check → lint → test → build, and ensures the gate triggers when
shared-code/**changes. - Adds/updates tooling config for the bundle: introduces
.nvmrc-based Node selection, adds Vue SFC linting viaeslint-plugin-vue, and wireseslint-config-prettierto prevent Prettier/ESLint rule conflicts. - Makes Docker builds more deterministic by switching Directus image installs from
npm installtonpm ci, and documents the intended Node version coupling.
Reviewed changes
Copilot reviewed 73 out of 75 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| Dockerfile.directus | Switches to npm ci for hermetic installs; documents Node version alignment with CI. |
| .github/workflows/run_tests.yml | Adds formatting + build gates for the bundle, uses .nvmrc, and includes shared-code/** in path filters. |
| AGENTS.md | Documents the bundle’s full CI gate order alongside existing repo guidance. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/TESTING.md | Updates testing docs to reflect the full CI gate and Jest/CJS constraints. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/.nvmrc | Introduces a single Node-version source for CI/local alignment (bundle). |
| directus-cms/extensions/directus-extension-programmierbar-bundle/.prettierrc | Aligns Prettier config (removes deprecated option). |
| directus-cms/extensions/directus-extension-programmierbar-bundle/.prettierignore | Prevents Prettier from touching local .claude state in the bundle tree. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/eslint.config.js | Adds Vue SFC linting + applies eslint-config-prettier last to avoid formatter conflicts. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/jest.config.ts | Formatting-only adjustments to Jest configuration file. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/package.json | Adds prettier:check, adds eslint-plugin-vue, declares Node engines baseline. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/package-lock.json | Locks new eslint-plugin-vue (and transitive) additions. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/publishable/presentation-publishable.vue | Removes unused watcher parameters to satisfy Vue linting. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/ticket-wallet/index.ts | Import ordering/formatting consistency. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/ticket-profile-completion/index.ts | Formatting/import ordering; no behavior change intended. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/ticket-order-processing/index.ts | Formatting/import ordering; no behavior change intended. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/speaker-token/index.ts | Formatting/import ordering; no behavior change intended. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/speaker-portal-notifications/index.ts | Formatting and minor layout changes for readability/lint. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/social-media-publish/index.ts | Formatting and line-wrapping for lint/Prettier compliance. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/screenshot/index.ts | Formatting of action registration for lint/Prettier compliance. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/process-guard/index.ts | Formatting of Slack notification call chain. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/post-to-discord/index.ts | Import ordering/formatting consistency. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/content-approval/index.ts | Formatting of action handler registration and expressions. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/conference/index.ts | Formatting cleanup for endpoint implementation. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/deploy-website/index.ts | Formatting cleanup for hook registration and log statements. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/create-profile/index.ts | Formatting cleanup and minor type formatting for lint/Prettier compliance. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/set-slug/index.ts | Formatting cleanup; removes stray semicolons/blank lines. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/set-slug/util/getPayloadWithSlug.ts | Formatting cleanup; ensures consistent object commas/ASI safety. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/podcast-transcript/index.ts | Formatting cleanup and consistent hook/schedule registration layout. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/podcast-transcript/generateTranscriptItem.ts | Formatting cleanup; minor object literal layout adjustments. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/podcast-transcript/processTranscriptItem.ts | Formatting cleanup; minor object literal layout adjustments. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/member-matching/index.ts | Import ordering/formatting consistency. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/member-matching/matchMembers.ts | Formatting cleanup, especially for long regex literals and log lines. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/asset-generation/index.ts | Formatting cleanup for action registrations and async error handling blocks. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/asset-generation/generateAssets.ts | Formatting cleanup and line wrapping for long calls. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/email-service.ts | Formatting cleanup for exported function signature. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/gemini.ts | Formatting cleanup for inline predicate. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/invoice-generator.ts | Formatting cleanup and line wrapping for PDFKit chains and signatures. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/isPublishable.ts | Formatting cleanup; introduces explicit ASI-safe semicolons for IIFE logging. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/wallet-pass-generator.ts | Formatting cleanup for exported function signature. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/index.ts | Formatting cleanup and improved structure for helper functions/actions. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/util/pagination.ts | Formatting cleanup for async generators and helper functions. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/util/sanitizer.ts | Formatting cleanup and consistent object literal style. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/ItemHandler.ts | Formatting cleanup for interface/abstract base class. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/index.ts | Formatting cleanup and quote/style normalization. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/MeetupHandler.ts | Formatting cleanup; improves readability of payload-fitting logic. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/PodcastHandler.ts | Formatting cleanup for returned payload array structure. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/PickOfTheDayHandler.ts | Formatting cleanup for returned payload array structure. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/SpeakerHandler.ts | Formatting cleanup for indexFields and payload structure. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/handlers/TranscriptHandler.ts | Formatting cleanup; simplifies some expressions and normalizes style. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/cli/rebuild-index.ts | Formatting cleanup for CLI structure and logging. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/algolia-index/cli/repair-index.ts | Formatting cleanup for CLI structure and logging. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/index.ts | Formatting cleanup; wraps long warning log and normalizes semicolons. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/handlers/buzzsprout.ts | Formatting cleanup; normalizes axios config and error logging style. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/handlers/handlePodcastAction.ts | Formatting cleanup; normalizes condition grouping and error string quotes. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/handlers/handlePickOfTheDayAction.ts | Formatting cleanup; normalizes imports. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/handlers/handleTagAction.ts | Formatting cleanup; normalizes imports. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/buzzsprout/handlers/podcastData.ts | Formatting cleanup; normalizes initialization style. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/cascade-publish/index.ts | Formatting cleanup for Slack message assembly. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/create-news/index.ts | Formatting cleanup for long log/error statements. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/fetch-open-graph/tests/index.test.ts | Formatting cleanup and import ordering for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/fetch-open-graph/util/tests/openGraph.test.ts | Formatting cleanup for long test call expressions. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/fetch-open-graph/util/tests/urlSafety.test.ts | Formatting cleanup for long expect chains. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/cascade-publish/tests/index.test.ts | Formatting cleanup and import ordering for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/create-news/tests/index.test.ts | Formatting cleanup and import ordering for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/create-news/util/tests/newsTarget.test.ts | Formatting cleanup for test data layout. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/member-matching/tests/matchMembers.test.ts | Formatting cleanup for imports. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/schedule-publication/tests/index.test.ts | Import ordering/formatting consistency for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/set-slug/tests/README.md | Formatting cleanup for markdown list indentation. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/set-slug/tests/getPayloadWithSlug.test.ts | Formatting cleanup and import ordering for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/tests/isPublishable.test.ts | Formatting cleanup and import ordering for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/tests/podcasts_fields.json | JSON formatting normalization. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/tests/safeHook.test.ts | Import ordering/formatting consistency for Jest tests. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/tests/settings.test.ts | Formatting cleanup for long expect chains. |
| directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/tests/test-wallet-passes.ts | Formatting cleanup for fetch calls and type assertions. |
Files not reviewed (1)
- directus-cms/extensions/directus-extension-programmierbar-bundle/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The plan put coverage in Phase 5, after the dependency upgrades. That was wrong. It inherited the nuxt-app plan's shape, where coverage sat in a follow-up backlog, without re-deriving it for a tree whose upgrades and untested modules overlap almost exactly. Measured: of the 16 files the planned bumps touch, 3 have a test covering them. algolia-index — largest module in the bundle, zero tests — is hit by four separate bumps: the sanitize-html critical, algoliasearch, @directus/sdk and meow. The old Phase 3 contained the tell. It required a manual step, "verify the Algolia indexer specifically … a broken indexer fails silently". A hand-verification instruction inside an upgrade phase is an admission that the automated check should have come first. That step is gone, replaced by Phase 3 item 1. New order: 3 contract tests, 4 security and minors, 5 E2E harness, 6 majors, 7 Renovate. Tier 3 also moves ahead of the majors, for the same reason rather than for symmetry: the E2E harness is the only thing that can validate @directus/extensions-sdk 18 against the frozen 11.17.4 host, which is Phase 6's riskiest item and one the plan already says not to merge unverified. Phase 3 is now ordered by which upgrade each entry protects rather than by LOC. That mostly agrees with the old table, but promotes shared/postSlackMessage to second — 26 lines, and the failure-notification path @slack/web-api 7→8 breaks — and pulls shared/invoice-generator in for pdfkit. Neither appeared before, because shared/ is not a bundle "entry". The sanitize-html critical is carved out and does not wait: holding a security patch behind a multi-PR test phase is the wrong trade. It ships with unit tests for sanitizer.ts in the same PR, including an assertion for the advisory's actual vector — content inside <xmp> must not pass through unescaped — so it is a regression test, not just coverage. ADR 0003 records the three tiers, the audit of all 11 outbound integrations (6 already redirectable by env, 5 hardcoded), and the two harness rules: a fail-closed stub, and no secrets in the environment at all rather than fake-but-plausible values, so a missed redirect dies on a connection error instead of reaching the production Slack. It also records the cost that matters most: Tier 2 fakes the shape of the Directus hook context, so a Directus upgrade that changes that shape lets the fakes drift and pass while production breaks. That is what Tier 3 is insurance against, and why it cannot be dropped despite being small. ADR 0003 complements ADR 0001 rather than superseding it — the util/-extraction guidance there is Tier 1 and still stands. Only ADR 0001's module-format half gets superseded, so Phase 2's Vitest ADR is renumbered to 0004. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMa3aosYRWyivC4DtPbSNr
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 74 out of 76 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- directus-cms/extensions/directus-extension-programmierbar-bundle/package-lock.json: Generated file
Suppressed comments (4)
directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/isPublishable.ts:52
- This log statement is wrapped in an unnecessary IIFE. A direct optional call is clearer and avoids ASI hazards.
directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/isPublishable.ts:73 - These logger statements are wrapped in unnecessary IIFEs. Direct optional calls are clearer and avoid the leading-semicolon workaround.
directus-cms/extensions/directus-extension-programmierbar-bundle/src/create-profile/index.ts:55 - This comment is unreachable (it sits after a
returninside thetryblock). It looks like it was meant to describe thecatchbelow; removing it avoids confusion.
directus-cms/extensions/directus-extension-programmierbar-bundle/src/shared/isPublishable.ts:41 - The logger calls are wrapped in an unnecessary IIFE and require a leading semicolon to avoid ASI. This reduces readability and is easy to simplify without changing behavior.
This issue also appears in the following locations of the same file:
- line 50
- line 67
Phase 0 of
docs/directus-extension-tooling-plan.md, added in this PR. No dependency versions change — the only dependency touched is a new one,eslint-plugin-vue, on the same footing asnuxt-app's Phase 0 addingvue-tscand@playwright/test.The bundle's CI job ran
npm ci, ESLint and Jest. It did not check formatting, did not typecheck, and never confirmed the artefact Directus loads still compiles. This adds the two gates that need no source changes, plus the config corrections behind them.Review guide — read commit 3, skip commit 2
prettier . --writeoutput. Nothing to review.All three are green in isolation, verified by checking each out and running the gates that exist at that commit.
What this gates
CI's bundle job becomes format → lint → test → build:
prettier:check, notprettier— the latter writes, so it would pass by mutating.directus-extension buildhas its own Rollup/esbuild pipeline that neither ESLint nor Jest exercises, so a change that broke the loadable bundle could merge green.node-version-file:reading a new.nvmrcinstead of a hardcoded22, so CI, the Docker image and local development have one file to change rather than three. Local dev had already drifted to 24.19.0.shared-code/**added to thepathsfilter — the bundle imports from there, so those changes were skipping the gate entirely.Hermetic image build: both
npm installcalls inDockerfile.directusbecomenpm ci, so the image can no longer resolve versions the lockfile does not record. Verified both lockfiles are in sync first (npm ci --dry-runexits 0 indirectus-cmsand in the bundle), since otherwise this would land a broken image build.Three things worth a second look
This tree is not blocked by the Directus licence question. A CI comment claimed it was. Every
@directus/*package the bundle uses is MIT (extensions-sdk,errors,sdk— checked at both current and latest); only thedirectusserver package is non-OSI, and it lives indirectus-cms/package.json. The real reason to stay on Node 22 isDockerfile.directus: Node 24 brings npm 11, which gates the install scriptssharp,sqlite3,isolated-vmandoracledball need. That is now a recorded blocker for the deferred Node 24 move, not a vague caution.eslint-config-prettierwas a dependency that had never been applied. Addingeslint-plugin-vueto cover the one unlinted source file produced 2 errors and 20 warnings — the warnings all formatting rules fighting Prettier (vue/html-indentwants 2-space,.prettierrcsays 4). Applyingeslint-config-prettierlast takes the 20 to 0. It was harmless while the config was TypeScript-only and enabled no stylistic rules, and load-bearing the moment such a plugin arrives. The 2 real errors were unusedwatchparameters, removed here; the gate was confirmed to fire by injecting an unused variable.host: "^10.10.0"is correct and deliberately unchanged. The plan initially flagged it as stale against the 11.17.4 server. It is not:extensions-sdkexportsLAST_BREAKING_RELEASE = '10.10.0'anddirectus-extension createscaffoldshostas exactly that. The field declares the extensions API contract version, not the server version, and Directus never checks it against the running server (z.string(), no semver comparison). Changing it to^11.0.0would narrow compatibility for nothing.Verifying the 63-file reformat
Lint, test and build are blind to a formatter by construction, and 17 of 26 entries have no tests, so the sweep was verified against the artefact Directus actually loads — build before, build after.
Import reordering is the one way this can genuinely break something, so it was checked first: the only load-time side-effect imports are
import 'dotenv/config'in the two Algolia CLI scripts, the plugin keeps them in the same slot, and the onlyprocess.envreads in those files sit below the whole import block.app.js— all 43 string literals identical; identifier-normalised, exactly one structural difference, the unusedwatchparameters removed in commit 3.api.js— same 18 module specifiers in the same order; 10,554 of 10,583 literals byte-identical. The 29 that differ areentitieslazy-init blobs varying only in Rollup's internal module variable names, a knock-on of a different module emission order.Static analysis could not push
api.jspast "almost certainly equivalent", so the decisive check was importing both builds in Node and comparing the registry handed to Directus: deep-equal — 23 hooks in the same order, 2 endpoints, 0 operations.Blind spot, stated plainly: that compares the registry's shape, not the behaviour of the 23 handlers, which cannot be invoked without a Directus host.
Verification
prettier:check,lint,test(205/205) andbuildall green.npm auditunchanged at 19 (1 critical, 7 high) — deliberately untouched here. The critical (sanitize-html) is a patch bump and ships as its own carve-out; the rest is Phase 4.Added after the first review pass: the plan's phase order was wrong
Commit 4 is docs-only and changes no code in this PR, but it changes what comes next, so it is worth reading.
The plan put test coverage in Phase 5, after the dependency upgrades — inherited from the
nuxt-appplan's shape without re-deriving it. Measured: of the 16 files the planned bumps touch, 3 have a test covering them, andalgolia-index(largest module, zero tests) is hit by four separate bumps. The old Phase 3 even required a manual step, "verify the Algolia indexer specifically … a broken indexer fails silently" — a hand-verification instruction inside an upgrade phase is an admission the automated check should have come first.New order: 3 contract tests → 4 security and minors → 5 E2E harness → 6 majors → 7 Renovate. Tier 3 also moved ahead of the majors, because it is the only thing that can validate
@directus/extensions-sdk18 against the frozen 11.17.4 host._ADRs/0003-three-tier-extension-testing.mdrecords the three tiers, an audit of all 11 outbound integrations (6 already redirectable via env, 5 hardcoded), and the harness rules — fail-closed stub, and no secrets in the environment at all rather than fake-but-plausible ones, so a missed redirect dies on a connection error instead of reaching the production Slack.Not in this PR
Typechecking.
tsccannot currently run on this tree at all — it dies onTS5110before checking anything, hiding 124 real errors across 32 files. That is Phase 1.🤖 Generated with Claude Code
https://claude.ai/code/session_01BMa3aosYRWyivC4DtPbSNr