chore(#470): C10 — the flip: delete the Dev Platform from core (~49k LOC) - #804
Merged
Conversation
…(C10) Epic #470 C10 — the flip. The Dev Platform now lives in byte5ai/omadia-dev-platform and installs via Hub/ZIP. Removed: - middleware/src/devplatform/ (62 files) — stores, worker, backends, LLM proxy, pipeline/gates, GitHub App, triggers, routers, wireDevPlatform - middleware/test/devplatform/ (58 files) - middleware/scripts/dev-transcript.ts Adversarial eval (#498): the `brief_delimiter` Tier A probe ran the real `composeBrief` out of src/devplatform/. A probe against a module core no longer ships measures a library, not a deployed defense — so the probe, its five corpus scenarios (direct-injection.jsonl, indirect-injection.jsonl) and their baseline rows leave with it. This is a real coverage reduction and is recorded as such in test/adversarial/README.md, with both ways to close it. Deterministic corpus: 12 scenarios -> 7.
…y (C10) Removed: - middleware/packages/dev-runner-shim/ (23 files) — the in-container agent shim. Never built by `npm run build`, yet index.ts resolved its dist/ at runtime; the extraction removes that inconsistency with it. - middleware/sidecars/dev-runner/ (2), dev-runner-daemon/ (30, dockerode), dev-dind/ (2) - docker-compose.dev-platform.yaml All four now live in byte5ai/omadia-dev-platform, which owns their GHCR publishing, SBOM and signing pipeline.
index.ts (-238 lines): - 15 devplatform imports + the now-dead DeviceFlowStore / ConductorRoleStore - the GitHub-webhook block mounted before express.json - the whole assembly block: assembleDevPlatform / mountDevPlatform, the GitHub-App routers, the three chat orchestrator tools, worker start + SIGTERM/SIGINT hooks, the uiRouteCatalog nav registration, the retention cron and the no-graphPool warning Side effect worth calling out: the `/api/v1/dev-runner/llm/` carve-out sat BEFORE the conductor inbound webhook router, so an express.json ran ahead of that router's route-level express.raw() and short-circuited it (body-parser marks the request `_body`). Deleting the carve-out restores the order the surrounding comments already document: pluginRawBodyMount -> conductor raw router -> global express.json. config.ts (-305 lines): all 43 DEV_*/FLY_* dev-platform schema keys, the `devPlatform` namespace, `buildDevPlatformConfig`, `csvList`, and the CORE_DEV_PREFIXED_KEYS / DevPlatformEnvKey / isDevPlatformEnvKey machinery that existed only to hold those keys out of the top level — `Config` is now `ParsedConfig`. `devPlatformBootRefusals` goes too: the plugin owns both interlocks (pluginConfig.ts, verified), so they became activation refusals rather than vanishing. KEPT: devFlag() (two PUBLIC_MCP_* call sites), DEV_ENDPOINTS_ENABLED, DEV_ENDPOINTS_LOOPBACK_ONLY, FLY_APP_NAME. .env.example: the 15-key block replaced with a pointer to the plugin repo. middleware/package.json: the §5 note — express/pg/zod stay as plugin peerDependencies resolved through the host node_modules symlink.
… (C10) - web-ui/app/admin/dev-platform/ (29 files) — the operator SPA. It ships from the plugin now as a compiled Vite bundle served through the C8 static host at /p/<pluginId>/ui/. - web-ui/app/_components/devjobs/ (6 files) + app/_lib/useDevJobEvents.ts - app/admin/page.tsx — the grid card. The generic `requiresNavFrom` mechanism stays; this was its only user. - app/_lib/i18n-structural.test.ts — the GateInbox.tsx path entry. H3 resolved by omission, as the plugin's ACCEPTANCE-RUN records. chat/page.tsx no longer special-cases `tool.name === 'dev_job_start'`; a dev-job start from the installed plugin now falls through to the generic long-running-task card (`isTaskStartToolName` / TaskChatCard), which is the accepted degradation. i18n: 299 leaf keys per locale removed (adminDevPlatform.* 288, chat.devJob.* 9, admin.index.cards.devPlatform.* 2) = 598 across en+de, plus the four orphaned `i18n-identical-allowlist.json` entries the validator flagged. `npm run i18n:check` OK — 3560 keys; `i18n:literals` translate=0.
… (C10)
publish-images.yml:
- the `dev-runner` and `dev-runner-daemon` matrix entries
- the runner-image supply chain: cosign install, syft SPDX-JSON SBOM,
keyless sign + attest (all guarded on `matrix.name == 'dev-runner'`)
- `id-token: write`, which existed solely for that keyless signing
auto-release.yml / release.yml: the matching caller-side `id-token: write`
grants, which a reusable workflow cannot self-grant and nothing else needs.
The `if: false` npm-provenance job in release.yml keeps its own grant.
byte5ai/omadia-dev-platform owns runner GHCR publishing, SBOM and signing
now. Per implementation.md §2.4 the cosign certificate identity binds to
repo + workflow + ref, so the new signer needs the transition
`--certificate-identity-regexp` landed before it publishes — that is P4's
job in the plugin repo, not core's.
The missing-Dockerfile guard stays: it also computes the version `stamp`
every remaining image consumes; only its dev-runner-daemon justification
comment is gone.
scripts/wave-{implement,verify}.workflow.mjs: the generic wave prompts baked
in "terminal transitions go through finalizeDevJob" — a dev-platform domain
rule in a domain-agnostic prompt, naming a function core no longer ships.
Genericised to "the subsystem's single finalizer"; the rule survives, the
implementor name does not. Same for the `docs/dev-platform/` example path.
`tsc` caught these, which is exactly why C10 has to be one PR. Three LONG_RUNNING_* keys sat INTERLEAVED with the dev-platform keys in config.ts, between DEV_PLATFORM_RUNNER_BASE_URL and DEV_PLATFORM_CLI_BIN, and went out with the block: LONG_RUNNING_SUBAGENT_TOOLS — W2-2 / issue #543, the generic LONG_RUNNING_TASK_STALE_MS `<tool>_start/_status/_list` seam that any LONG_RUNNING_TASK_RETAIN_MS slow tool opts into. Not dev-platform. FLY_APP_NAME went too, despite CORE_DEV_PREFIXED_KEYS documenting in prose that it "describes the host, not the feature, so it must survive the extraction". All four are restored under an explicit heading that says why they are core, so the next person reading config.ts does not have to re-derive it. Also drops the `node:os` import, now unused (it backed the workspace-dir default), and rewords the three comments this PR itself introduced that named the extracted subsystem — C10 must not add coupling references while removing them. Verified: middleware build + typecheck + adversarial/golden tsconfigs green; 6925 pass / 0 fail; typecheck:test ratchet 406 -> 371 (baseline lowered); lint 0 problems. web-ui typecheck green, 717 pass / 0 fail, i18n 3560 keys OK.
…(C10)
Ratchet: **3,300 → 214**, updated consistently in all three places the README
says must agree — `decoupling-baseline.json`, the README "Baseline **214**"
line, and the `acceptance.md` guard row.
Nine of fourteen zones read CLEAN. Every survivor is scheduled, not stranded:
migrations 69 C11 — 0022-0030 stay; core still applies them
middleware/test 62 C13 — fixture strings + the legacy-key regression test
scripts 27 C13 — the ratchet's own pattern list (needs a
self-exclusion before the total can reach 0)
middleware/src 19 C12 — publicPaths (6); rest are comments
web-ui/app 19 C13 — nav test fixtures, two comments
packages 17 C13 — plugin-api CHANGELOG recording the removal
env-example 1 the plugin repo URL, which cannot be reworded
Also: `test-typecheck-baseline.json` 406 → 371, and `package-lock.json`
regenerated (npm left `packages/dev-runner-shim` as an `extraneous: true`
stanza rather than dropping it; `npm ci` verified clean afterwards).
Docs:
- README gains a "C10 — the flip" status section: what left, what was kept and
why, H3 resolved by omission, the adversarial coverage reduction, and the
express.json ordering bug the deletion fixed. The H3 "decision before code"
is struck through — it is answered now.
- plan.md §4.2 gains a C10 note (and flags that its `devJobStepEffect.ts` line
went stale in the other direction: C5 deleted that port as dead code). P4 row
marked shipped.
- docs/CHANGELOG.md: "Dev Platform moved to byte5ai/omadia-dev-platform
(install via Hub/ZIP)" — written for operators, so it leads with how to get
it back, states that no data is touched, and that in-flight jobs survive the
upgrade because C12 has not run yet.
…sha_on_release matrix ref (C10 review) Cross-family review of PR #804. Three defects, no scope change to the deletion itself. 1. `.github/workflows/publish-images.yml` referenced `matrix.sha_on_release`, which nothing defines any more: the only two matrix entries that set it (dev-runner, dev-runner-daemon) left with the Dev Platform. actionlint errors on this branch and is clean on main. Dropping the clause also restores the behaviour the adjacent comment already claims — sha tags on the edge build only. 2. `middleware/package.json` — the `_dependencyNote` added by this PR was indented four spaces and pushed `"dependencies"` to column 0. Valid JSON, but the rest of the file is 2-space and any tool that rewrites it would reflow the diff. Text unchanged. 3. Removing the LLM-proxy carve-out moved `createConductorWebhooksInboundRouter` from *after* the global `express.json` to *before* it. That is correct — and it silently fixes a live bug: on main the router HMAC'd `Buffer.alloc(0)`, because body-parser stamps `_body` and the route-level `express.raw()` then short-circuits, so every correctly-signed inbound webhook answered 401. Measured on a standalone probe: json-first yields rawLen 0, router-first yields the full 24 bytes. Nothing locked that order. `conductorWebhookInbound.test.ts` mounts the router alone on a bare app, which passes under BOTH orders — which is exactly how the broken ordering shipped green. Added two behavioural tests (same signed request, both mount orders: 202 vs 401) plus a source-order assertion in `778RouteMounts.wiring.test.ts` that fails loudly if either anchor goes missing rather than passing vacuously. All four assertions mutation-checked: reordering the mount fails the wiring test, renaming the anchor fails it for the anchor reason, and forcing/removing the parser in the harness kills exactly the intended behavioural tests. Also corrects the C10 file count in the spec README (213 -> 217 measured deletions). The adjacent "43 config keys" claim was verified correct and left alone. Ratchet stays at 214 — the one comment that would have raised it was reworded, not excused.
This was referenced Aug 20, 2026
Closed
… left with it (epic #470 C10)
…/omadia into feat/470-c10-delete-dev-platform
Weegy
added a commit
that referenced
this pull request
Aug 21, 2026
Weegy
added a commit
that referenced
this pull request
Aug 21, 2026
Weegy
added a commit
that referenced
this pull request
Aug 21, 2026
…racted subsystem (epic #470 C12) (#807) * chore(#470): delete the dev-platform backend tree, routers and tests (C10) Epic #470 C10 — the flip. The Dev Platform now lives in byte5ai/omadia-dev-platform and installs via Hub/ZIP. Removed: - middleware/src/devplatform/ (62 files) — stores, worker, backends, LLM proxy, pipeline/gates, GitHub App, triggers, routers, wireDevPlatform - middleware/test/devplatform/ (58 files) - middleware/scripts/dev-transcript.ts Adversarial eval (#498): the `brief_delimiter` Tier A probe ran the real `composeBrief` out of src/devplatform/. A probe against a module core no longer ships measures a library, not a deployed defense — so the probe, its five corpus scenarios (direct-injection.jsonl, indirect-injection.jsonl) and their baseline rows leave with it. This is a real coverage reduction and is recorded as such in test/adversarial/README.md, with both ways to close it. Deterministic corpus: 12 scenarios -> 7. * chore(#470): delete the dev-runner shim, sidecars and compose topology (C10) Removed: - middleware/packages/dev-runner-shim/ (23 files) — the in-container agent shim. Never built by `npm run build`, yet index.ts resolved its dist/ at runtime; the extraction removes that inconsistency with it. - middleware/sidecars/dev-runner/ (2), dev-runner-daemon/ (30, dockerode), dev-dind/ (2) - docker-compose.dev-platform.yaml All four now live in byte5ai/omadia-dev-platform, which owns their GHCR publishing, SBOM and signing pipeline. * chore(#470): unwire the dev platform from index.ts, config and env (C10) index.ts (-238 lines): - 15 devplatform imports + the now-dead DeviceFlowStore / ConductorRoleStore - the GitHub-webhook block mounted before express.json - the whole assembly block: assembleDevPlatform / mountDevPlatform, the GitHub-App routers, the three chat orchestrator tools, worker start + SIGTERM/SIGINT hooks, the uiRouteCatalog nav registration, the retention cron and the no-graphPool warning Side effect worth calling out: the `/api/v1/dev-runner/llm/` carve-out sat BEFORE the conductor inbound webhook router, so an express.json ran ahead of that router's route-level express.raw() and short-circuited it (body-parser marks the request `_body`). Deleting the carve-out restores the order the surrounding comments already document: pluginRawBodyMount -> conductor raw router -> global express.json. config.ts (-305 lines): all 43 DEV_*/FLY_* dev-platform schema keys, the `devPlatform` namespace, `buildDevPlatformConfig`, `csvList`, and the CORE_DEV_PREFIXED_KEYS / DevPlatformEnvKey / isDevPlatformEnvKey machinery that existed only to hold those keys out of the top level — `Config` is now `ParsedConfig`. `devPlatformBootRefusals` goes too: the plugin owns both interlocks (pluginConfig.ts, verified), so they became activation refusals rather than vanishing. KEPT: devFlag() (two PUBLIC_MCP_* call sites), DEV_ENDPOINTS_ENABLED, DEV_ENDPOINTS_LOOPBACK_ONLY, FLY_APP_NAME. .env.example: the 15-key block replaced with a pointer to the plugin repo. middleware/package.json: the §5 note — express/pg/zod stay as plugin peerDependencies resolved through the host node_modules symlink. * chore(#470): delete the dev-platform web-ui surface and its i18n keys (C10) - web-ui/app/admin/dev-platform/ (29 files) — the operator SPA. It ships from the plugin now as a compiled Vite bundle served through the C8 static host at /p/<pluginId>/ui/. - web-ui/app/_components/devjobs/ (6 files) + app/_lib/useDevJobEvents.ts - app/admin/page.tsx — the grid card. The generic `requiresNavFrom` mechanism stays; this was its only user. - app/_lib/i18n-structural.test.ts — the GateInbox.tsx path entry. H3 resolved by omission, as the plugin's ACCEPTANCE-RUN records. chat/page.tsx no longer special-cases `tool.name === 'dev_job_start'`; a dev-job start from the installed plugin now falls through to the generic long-running-task card (`isTaskStartToolName` / TaskChatCard), which is the accepted degradation. i18n: 299 leaf keys per locale removed (adminDevPlatform.* 288, chat.devJob.* 9, admin.index.cards.devPlatform.* 2) = 598 across en+de, plus the four orphaned `i18n-identical-allowlist.json` entries the validator flagged. `npm run i18n:check` OK — 3560 keys; `i18n:literals` translate=0. * chore(#470): drop the dev-runner CI matrix and supply chain from core (C10) publish-images.yml: - the `dev-runner` and `dev-runner-daemon` matrix entries - the runner-image supply chain: cosign install, syft SPDX-JSON SBOM, keyless sign + attest (all guarded on `matrix.name == 'dev-runner'`) - `id-token: write`, which existed solely for that keyless signing auto-release.yml / release.yml: the matching caller-side `id-token: write` grants, which a reusable workflow cannot self-grant and nothing else needs. The `if: false` npm-provenance job in release.yml keeps its own grant. byte5ai/omadia-dev-platform owns runner GHCR publishing, SBOM and signing now. Per implementation.md §2.4 the cosign certificate identity binds to repo + workflow + ref, so the new signer needs the transition `--certificate-identity-regexp` landed before it publishes — that is P4's job in the plugin repo, not core's. The missing-Dockerfile guard stays: it also computes the version `stamp` every remaining image consumes; only its dev-runner-daemon justification comment is gone. scripts/wave-{implement,verify}.workflow.mjs: the generic wave prompts baked in "terminal transitions go through finalizeDevJob" — a dev-platform domain rule in a domain-agnostic prompt, naming a function core no longer ships. Genericised to "the subsystem's single finalizer"; the rule survives, the implementor name does not. Same for the `docs/dev-platform/` example path. * fix(#470): restore four core config keys the C10 cut swallowed (C10) `tsc` caught these, which is exactly why C10 has to be one PR. Three LONG_RUNNING_* keys sat INTERLEAVED with the dev-platform keys in config.ts, between DEV_PLATFORM_RUNNER_BASE_URL and DEV_PLATFORM_CLI_BIN, and went out with the block: LONG_RUNNING_SUBAGENT_TOOLS — W2-2 / issue #543, the generic LONG_RUNNING_TASK_STALE_MS `<tool>_start/_status/_list` seam that any LONG_RUNNING_TASK_RETAIN_MS slow tool opts into. Not dev-platform. FLY_APP_NAME went too, despite CORE_DEV_PREFIXED_KEYS documenting in prose that it "describes the host, not the feature, so it must survive the extraction". All four are restored under an explicit heading that says why they are core, so the next person reading config.ts does not have to re-derive it. Also drops the `node:os` import, now unused (it backed the workspace-dir default), and rewords the three comments this PR itself introduced that named the extracted subsystem — C10 must not add coupling references while removing them. Verified: middleware build + typecheck + adversarial/golden tsconfigs green; 6925 pass / 0 fail; typecheck:test ratchet 406 -> 371 (baseline lowered); lint 0 problems. web-ui typecheck green, 717 pass / 0 fail, i18n 3560 keys OK. * docs(#470): record the C10 flip — ratchet baseline, specs, changelog (C10) Ratchet: **3,300 → 214**, updated consistently in all three places the README says must agree — `decoupling-baseline.json`, the README "Baseline **214**" line, and the `acceptance.md` guard row. Nine of fourteen zones read CLEAN. Every survivor is scheduled, not stranded: migrations 69 C11 — 0022-0030 stay; core still applies them middleware/test 62 C13 — fixture strings + the legacy-key regression test scripts 27 C13 — the ratchet's own pattern list (needs a self-exclusion before the total can reach 0) middleware/src 19 C12 — publicPaths (6); rest are comments web-ui/app 19 C13 — nav test fixtures, two comments packages 17 C13 — plugin-api CHANGELOG recording the removal env-example 1 the plugin repo URL, which cannot be reworded Also: `test-typecheck-baseline.json` 406 → 371, and `package-lock.json` regenerated (npm left `packages/dev-runner-shim` as an `extraneous: true` stanza rather than dropping it; `npm ci` verified clean afterwards). Docs: - README gains a "C10 — the flip" status section: what left, what was kept and why, H3 resolved by omission, the adversarial coverage reduction, and the express.json ordering bug the deletion fixed. The H3 "decision before code" is struck through — it is answered now. - plan.md §4.2 gains a C10 note (and flags that its `devJobStepEffect.ts` line went stale in the other direction: C5 deleted that port as dead code). P4 row marked shipped. - docs/CHANGELOG.md: "Dev Platform moved to byte5ai/omadia-dev-platform (install via Hub/ZIP)" — written for operators, so it leads with how to get it back, states that no data is touched, and that in-flight jobs survive the upgrade because C12 has not run yet. * fix(#470): lock the conductor raw-body mount order and drop the dead sha_on_release matrix ref (C10 review) Cross-family review of PR #804. Three defects, no scope change to the deletion itself. 1. `.github/workflows/publish-images.yml` referenced `matrix.sha_on_release`, which nothing defines any more: the only two matrix entries that set it (dev-runner, dev-runner-daemon) left with the Dev Platform. actionlint errors on this branch and is clean on main. Dropping the clause also restores the behaviour the adjacent comment already claims — sha tags on the edge build only. 2. `middleware/package.json` — the `_dependencyNote` added by this PR was indented four spaces and pushed `"dependencies"` to column 0. Valid JSON, but the rest of the file is 2-space and any tool that rewrites it would reflow the diff. Text unchanged. 3. Removing the LLM-proxy carve-out moved `createConductorWebhooksInboundRouter` from *after* the global `express.json` to *before* it. That is correct — and it silently fixes a live bug: on main the router HMAC'd `Buffer.alloc(0)`, because body-parser stamps `_body` and the route-level `express.raw()` then short-circuits, so every correctly-signed inbound webhook answered 401. Measured on a standalone probe: json-first yields rawLen 0, router-first yields the full 24 bytes. Nothing locked that order. `conductorWebhookInbound.test.ts` mounts the router alone on a bare app, which passes under BOTH orders — which is exactly how the broken ordering shipped green. Added two behavioural tests (same signed request, both mount orders: 202 vs 401) plus a source-order assertion in `778RouteMounts.wiring.test.ts` that fails loudly if either anchor goes missing rather than passing vacuously. All four assertions mutation-checked: reordering the mount fails the wiring test, renaming the anchor fails it for the anchor reason, and forcing/removing the parser in the harness kills exactly the intended behavioural tests. Also corrects the C10 file count in the spec README (213 -> 217 measured deletions). The adjacent "43 config keys" claim was verified correct and left alone. Ratchet stays at 214 — the one comment that would have raised it was reworded, not excused. * feat(#470): delete the two dev-platform publicPaths exemptions (C12) Core stops exempting two URLs on a plugin's behalf. Since C4 the plugin declares them itself in `permissions.public_paths`, and the operator consents per prefix (`platform/publicPathGrants.ts`). The deletion is the load-bearing half of that handover, not tidy-up. `validateDeclaredPublicPath` REJECTS a declaration that collides with a live static exemption — "is already a static core public path … remove the core exemption first, or drop the declaration" — because two mechanisms claiming one URL is ambiguous and only one of them terminates. So while these entries existed the plugin's own grant could never validate. With them gone it does. Guarded by a new suite that pins `STATIC_PUBLIC_PATHS` as a CLOSED set: every entry must be accounted for by a named, justified core-owned exemption. Restore either deleted entry and the suite goes red on the unaccounted entry, which is what makes this commit cleanly revertible. The suite deliberately does not name the two deleted paths. Hardcoding a plugin's wire paths into core is a reference to that plugin whichever direction it asserts in — the coupling this epic removes and the thing `scripts/check-core-decoupling.mjs` counts — and it goes stale-but-green the day the plugin renames them. The closed set is also the stronger guard: it catches any unauthenticated surface being added, not just these two. The 401 follows as a corollary, since `requireAuth` runs on the `/api` mount before routing, and is asserted end-to-end against the same chain `src/index.ts` assembles. Ratchet: 214 → 209. Nothing raised. * fix(#470): fail loud in CI when the closed-set 401 half cannot bind loopback (C12 review) The suite self-skips its five unauthenticated 401 assertions when a sandbox refuses a loopback listener. Locally that is right — the closed-set assertion still runs. In CI it is not: a runner that denied bind(127.0.0.1:0) would delete the entire 401 half of the guard while the job stayed green, which is the same permanently-green failure family the decoupling job's own timeout comment warns about. Keep the local skip, but throw with a named reason when CI is set. * chore(470): lower decoupling baseline 211 → 206 after C12
Weegy
added a commit
that referenced
this pull request
Aug 21, 2026
…d is pinned there (epic #470 C13) (#808) * chore(#470): delete the dev-platform backend tree, routers and tests (C10) Epic #470 C10 — the flip. The Dev Platform now lives in byte5ai/omadia-dev-platform and installs via Hub/ZIP. Removed: - middleware/src/devplatform/ (62 files) — stores, worker, backends, LLM proxy, pipeline/gates, GitHub App, triggers, routers, wireDevPlatform - middleware/test/devplatform/ (58 files) - middleware/scripts/dev-transcript.ts Adversarial eval (#498): the `brief_delimiter` Tier A probe ran the real `composeBrief` out of src/devplatform/. A probe against a module core no longer ships measures a library, not a deployed defense — so the probe, its five corpus scenarios (direct-injection.jsonl, indirect-injection.jsonl) and their baseline rows leave with it. This is a real coverage reduction and is recorded as such in test/adversarial/README.md, with both ways to close it. Deterministic corpus: 12 scenarios -> 7. * chore(#470): delete the dev-runner shim, sidecars and compose topology (C10) Removed: - middleware/packages/dev-runner-shim/ (23 files) — the in-container agent shim. Never built by `npm run build`, yet index.ts resolved its dist/ at runtime; the extraction removes that inconsistency with it. - middleware/sidecars/dev-runner/ (2), dev-runner-daemon/ (30, dockerode), dev-dind/ (2) - docker-compose.dev-platform.yaml All four now live in byte5ai/omadia-dev-platform, which owns their GHCR publishing, SBOM and signing pipeline. * chore(#470): unwire the dev platform from index.ts, config and env (C10) index.ts (-238 lines): - 15 devplatform imports + the now-dead DeviceFlowStore / ConductorRoleStore - the GitHub-webhook block mounted before express.json - the whole assembly block: assembleDevPlatform / mountDevPlatform, the GitHub-App routers, the three chat orchestrator tools, worker start + SIGTERM/SIGINT hooks, the uiRouteCatalog nav registration, the retention cron and the no-graphPool warning Side effect worth calling out: the `/api/v1/dev-runner/llm/` carve-out sat BEFORE the conductor inbound webhook router, so an express.json ran ahead of that router's route-level express.raw() and short-circuited it (body-parser marks the request `_body`). Deleting the carve-out restores the order the surrounding comments already document: pluginRawBodyMount -> conductor raw router -> global express.json. config.ts (-305 lines): all 43 DEV_*/FLY_* dev-platform schema keys, the `devPlatform` namespace, `buildDevPlatformConfig`, `csvList`, and the CORE_DEV_PREFIXED_KEYS / DevPlatformEnvKey / isDevPlatformEnvKey machinery that existed only to hold those keys out of the top level — `Config` is now `ParsedConfig`. `devPlatformBootRefusals` goes too: the plugin owns both interlocks (pluginConfig.ts, verified), so they became activation refusals rather than vanishing. KEPT: devFlag() (two PUBLIC_MCP_* call sites), DEV_ENDPOINTS_ENABLED, DEV_ENDPOINTS_LOOPBACK_ONLY, FLY_APP_NAME. .env.example: the 15-key block replaced with a pointer to the plugin repo. middleware/package.json: the §5 note — express/pg/zod stay as plugin peerDependencies resolved through the host node_modules symlink. * chore(#470): delete the dev-platform web-ui surface and its i18n keys (C10) - web-ui/app/admin/dev-platform/ (29 files) — the operator SPA. It ships from the plugin now as a compiled Vite bundle served through the C8 static host at /p/<pluginId>/ui/. - web-ui/app/_components/devjobs/ (6 files) + app/_lib/useDevJobEvents.ts - app/admin/page.tsx — the grid card. The generic `requiresNavFrom` mechanism stays; this was its only user. - app/_lib/i18n-structural.test.ts — the GateInbox.tsx path entry. H3 resolved by omission, as the plugin's ACCEPTANCE-RUN records. chat/page.tsx no longer special-cases `tool.name === 'dev_job_start'`; a dev-job start from the installed plugin now falls through to the generic long-running-task card (`isTaskStartToolName` / TaskChatCard), which is the accepted degradation. i18n: 299 leaf keys per locale removed (adminDevPlatform.* 288, chat.devJob.* 9, admin.index.cards.devPlatform.* 2) = 598 across en+de, plus the four orphaned `i18n-identical-allowlist.json` entries the validator flagged. `npm run i18n:check` OK — 3560 keys; `i18n:literals` translate=0. * chore(#470): drop the dev-runner CI matrix and supply chain from core (C10) publish-images.yml: - the `dev-runner` and `dev-runner-daemon` matrix entries - the runner-image supply chain: cosign install, syft SPDX-JSON SBOM, keyless sign + attest (all guarded on `matrix.name == 'dev-runner'`) - `id-token: write`, which existed solely for that keyless signing auto-release.yml / release.yml: the matching caller-side `id-token: write` grants, which a reusable workflow cannot self-grant and nothing else needs. The `if: false` npm-provenance job in release.yml keeps its own grant. byte5ai/omadia-dev-platform owns runner GHCR publishing, SBOM and signing now. Per implementation.md §2.4 the cosign certificate identity binds to repo + workflow + ref, so the new signer needs the transition `--certificate-identity-regexp` landed before it publishes — that is P4's job in the plugin repo, not core's. The missing-Dockerfile guard stays: it also computes the version `stamp` every remaining image consumes; only its dev-runner-daemon justification comment is gone. scripts/wave-{implement,verify}.workflow.mjs: the generic wave prompts baked in "terminal transitions go through finalizeDevJob" — a dev-platform domain rule in a domain-agnostic prompt, naming a function core no longer ships. Genericised to "the subsystem's single finalizer"; the rule survives, the implementor name does not. Same for the `docs/dev-platform/` example path. * fix(#470): restore four core config keys the C10 cut swallowed (C10) `tsc` caught these, which is exactly why C10 has to be one PR. Three LONG_RUNNING_* keys sat INTERLEAVED with the dev-platform keys in config.ts, between DEV_PLATFORM_RUNNER_BASE_URL and DEV_PLATFORM_CLI_BIN, and went out with the block: LONG_RUNNING_SUBAGENT_TOOLS — W2-2 / issue #543, the generic LONG_RUNNING_TASK_STALE_MS `<tool>_start/_status/_list` seam that any LONG_RUNNING_TASK_RETAIN_MS slow tool opts into. Not dev-platform. FLY_APP_NAME went too, despite CORE_DEV_PREFIXED_KEYS documenting in prose that it "describes the host, not the feature, so it must survive the extraction". All four are restored under an explicit heading that says why they are core, so the next person reading config.ts does not have to re-derive it. Also drops the `node:os` import, now unused (it backed the workspace-dir default), and rewords the three comments this PR itself introduced that named the extracted subsystem — C10 must not add coupling references while removing them. Verified: middleware build + typecheck + adversarial/golden tsconfigs green; 6925 pass / 0 fail; typecheck:test ratchet 406 -> 371 (baseline lowered); lint 0 problems. web-ui typecheck green, 717 pass / 0 fail, i18n 3560 keys OK. * docs(#470): record the C10 flip — ratchet baseline, specs, changelog (C10) Ratchet: **3,300 → 214**, updated consistently in all three places the README says must agree — `decoupling-baseline.json`, the README "Baseline **214**" line, and the `acceptance.md` guard row. Nine of fourteen zones read CLEAN. Every survivor is scheduled, not stranded: migrations 69 C11 — 0022-0030 stay; core still applies them middleware/test 62 C13 — fixture strings + the legacy-key regression test scripts 27 C13 — the ratchet's own pattern list (needs a self-exclusion before the total can reach 0) middleware/src 19 C12 — publicPaths (6); rest are comments web-ui/app 19 C13 — nav test fixtures, two comments packages 17 C13 — plugin-api CHANGELOG recording the removal env-example 1 the plugin repo URL, which cannot be reworded Also: `test-typecheck-baseline.json` 406 → 371, and `package-lock.json` regenerated (npm left `packages/dev-runner-shim` as an `extraneous: true` stanza rather than dropping it; `npm ci` verified clean afterwards). Docs: - README gains a "C10 — the flip" status section: what left, what was kept and why, H3 resolved by omission, the adversarial coverage reduction, and the express.json ordering bug the deletion fixed. The H3 "decision before code" is struck through — it is answered now. - plan.md §4.2 gains a C10 note (and flags that its `devJobStepEffect.ts` line went stale in the other direction: C5 deleted that port as dead code). P4 row marked shipped. - docs/CHANGELOG.md: "Dev Platform moved to byte5ai/omadia-dev-platform (install via Hub/ZIP)" — written for operators, so it leads with how to get it back, states that no data is touched, and that in-flight jobs survive the upgrade because C12 has not run yet. * fix(#470): lock the conductor raw-body mount order and drop the dead sha_on_release matrix ref (C10 review) Cross-family review of PR #804. Three defects, no scope change to the deletion itself. 1. `.github/workflows/publish-images.yml` referenced `matrix.sha_on_release`, which nothing defines any more: the only two matrix entries that set it (dev-runner, dev-runner-daemon) left with the Dev Platform. actionlint errors on this branch and is clean on main. Dropping the clause also restores the behaviour the adjacent comment already claims — sha tags on the edge build only. 2. `middleware/package.json` — the `_dependencyNote` added by this PR was indented four spaces and pushed `"dependencies"` to column 0. Valid JSON, but the rest of the file is 2-space and any tool that rewrites it would reflow the diff. Text unchanged. 3. Removing the LLM-proxy carve-out moved `createConductorWebhooksInboundRouter` from *after* the global `express.json` to *before* it. That is correct — and it silently fixes a live bug: on main the router HMAC'd `Buffer.alloc(0)`, because body-parser stamps `_body` and the route-level `express.raw()` then short-circuits, so every correctly-signed inbound webhook answered 401. Measured on a standalone probe: json-first yields rawLen 0, router-first yields the full 24 bytes. Nothing locked that order. `conductorWebhookInbound.test.ts` mounts the router alone on a bare app, which passes under BOTH orders — which is exactly how the broken ordering shipped green. Added two behavioural tests (same signed request, both mount orders: 202 vs 401) plus a source-order assertion in `778RouteMounts.wiring.test.ts` that fails loudly if either anchor goes missing rather than passing vacuously. All four assertions mutation-checked: reordering the mount fails the wiring test, renaming the anchor fails it for the anchor reason, and forcing/removing the parser in the harness kills exactly the intended behavioural tests. Also corrects the C10 file count in the spec README (213 -> 217 measured deletions). The adjacent "43 config keys" claim was verified correct and left alone. Ratchet stays at 214 — the one comment that would have raised it was reworded, not excused. * feat(#470): delete the two dev-platform publicPaths exemptions (C12) Core stops exempting two URLs on a plugin's behalf. Since C4 the plugin declares them itself in `permissions.public_paths`, and the operator consents per prefix (`platform/publicPathGrants.ts`). The deletion is the load-bearing half of that handover, not tidy-up. `validateDeclaredPublicPath` REJECTS a declaration that collides with a live static exemption — "is already a static core public path … remove the core exemption first, or drop the declaration" — because two mechanisms claiming one URL is ambiguous and only one of them terminates. So while these entries existed the plugin's own grant could never validate. With them gone it does. Guarded by a new suite that pins `STATIC_PUBLIC_PATHS` as a CLOSED set: every entry must be accounted for by a named, justified core-owned exemption. Restore either deleted entry and the suite goes red on the unaccounted entry, which is what makes this commit cleanly revertible. The suite deliberately does not name the two deleted paths. Hardcoding a plugin's wire paths into core is a reference to that plugin whichever direction it asserts in — the coupling this epic removes and the thing `scripts/check-core-decoupling.mjs` counts — and it goes stale-but-green the day the plugin renames them. The closed set is also the stronger guard: it catches any unauthenticated surface being added, not just these two. The 401 follows as a corollary, since `requireAuth` runs on the `/api` mount before routing, and is asserted end-to-end against the same chain `src/index.ts` assembles. Ratchet: 214 → 209. Nothing raised. * feat(#470): drive the core-decoupling count to 0 and pin it there (C13) Core carries no Dev Platform reference at all: no code path, config key, i18n key, fixture string or comment. All 14 zones CLEAN. 214 -> 0 (3,300 at the epic's start, 214 after C10, 209 after C12) WHAT MOVED - middleware/src (14 -> 0), packages (17 -> 0), env-example (1 -> 0): comments and one operator-facing pointer. Where the reference was to a dead path (`devplatform/routes/devWebhooks.ts`, `src/devplatform/ devJobTypes.ts`) it was a stale link and is now corrected; where it named the product it stays named, in prose. - middleware/test (62 -> 0): fixture route prefixes, nav ids and channel types now use a neutral example plugin. `manifestDevJobsLegacyKey.test.ts` becomes `manifestRetiredPermissionKey.test.ts` and asserts the guarantee that actually exists — unknown permission keys are ignored, not rejected — which is key-agnostic, so pinning one historical key made it stale-but- green the next time a different capability is retired. - web-ui/app (19 -> 0): the same, plus a genuinely stale comment claiming the nav entry appears "while DEV_PLATFORM_ENABLED" — an env var C10 deleted. - scripts (27 -> 0): the ratchet was counting its own `PATTERNS` array. It now excludes itself. A detector that counts its own definitions can never reach zero, and "zero except for the 27 that are the tool" is not a property anyone can check at a glance. WHAT IS ALLOWLISTED, AND WHY IT IS NOT A DODGE Two path-anchored entries, both historical record rather than coupling. A record you are allowed to edit is not a record. - `middleware/migrations/00{22..30}_*` — every deployment that ran them has those FILENAMES in its `schema_migrations` ledger, and C11's plugin-side migrator seeds from exactly those donor rows. Rename one and the handoff stops matching on the installations that need it most. - `packages/plugin-api/CHANGELOG.md` — a published entry for a released version, written to be found by a consumer grepping for a removed type. `services/githubAppJwt.ts` stays in core and needed no allowlist: it is generic GitHub App auth and carries no matching identifier. THE FLOOR IS NOW ABSOLUTE A ratchet parked at zero still reads its floor out of a JSON file, and a JSON file is editable. `EXTRACTION_COMPLETE` makes the script assert 0 outright and ignore the baseline for pass/fail; `--update` refuses to write a non-zero baseline. The CI job additionally greps the report for `TOTAL 0`, so a future refactor that made the script exit 0 while still counting hits still fails. Bringing the Dev Platform back into core is an architectural decision to argue in review, not a number to edit. Zero counts IDENTIFIERS, not the English name: a comment may still say "the dev platform plugin". Prose describes history; identifiers create coupling. * fix(#470): fail loud in CI when the closed-set 401 half cannot bind loopback (C12 review) The suite self-skips its five unauthenticated 401 assertions when a sandbox refuses a loopback listener. Locally that is right — the closed-set assertion still runs. In CI it is not: a runner that denied bind(127.0.0.1:0) would delete the entire 401 half of the guard while the job stayed green, which is the same permanently-green failure family the decoupling job's own timeout comment warns about. Keep the local skip, but throw with a named reason when CI is set. * fix(#470): anchor the detector self-exclusion and finish the neutral fixture rename (C13 review) Three defects from cross-family review of the zero floor. 1. The self-exclusion was a BASENAME wildcard. `!**/check-core-decoupling.mjs` hides every file of that name anywhere in a scanned zone, not just the one detector. A probe at middleware/src/__probe/check-core-decoupling.mjs carrying three real identifiers scanned as "Core is free of Dev Platform references", exit 0. Anchored to the exact repo-relative path; the scripts zone still reports CLEAN, and the probe now trips the check. 2. The mutation test is now committed, not just performed. It plants that probe and asserts the checker goes red. Its own tokens are assembled at runtime so the test file cannot trip the detector it tests. 3. The C13 identifier rename left the plugin's HUMAN-READABLE label behind in four fixtures. PATTERNS are identifier-shaped, so `Dev Platform` and `Dev-Plattform` are invisible to the zero floor while the sweep claimed no fixture string survives. Renamed with their assertions, preserving the en/de/de-AT/fr fallback each test exercises. Deliberate prose mentions are untouched. The regression test is wired into the decoupling job. scripts/ has no package.json and no job globs *.test.mjs, so it would otherwise have been a test nobody runs — green because nothing executed it. * chore(470): lower decoupling baseline 211 → 206 after C12 * chore(470): keep C13's neutral tombstone wording after merging C12/main * docs(470): drop the superseded in-flight ratchet row
This was referenced Aug 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
epic #470 C10 — the flip: delete the Dev Platform from core
The Dev Platform now lives in
byte5ai/omadia-dev-platformand installs via the Hub (or a ZIP upload). This is core's side of the move.One PR by necessity, not preference. A partial delete leaves
tscred —index.ts,config.ts, the routers and the SPA are one dependency knot. It is split into 7 commits by area so it stays reviewable and revertible.243 files changed · +149 / −59,356 · 217 files deleted.
Zone table — ratchet before → after
node scripts/check-core-decoupling.mjs --reportmiddleware/srcpublicPaths; rest are commentsmiddleware/testmiddleware/packagesplugin-apiCHANGELOG recording the removal → C13middleware/scriptsmiddleware/sidecarsmiddleware/migrations0022–0030stay, deliberatelymiddleware/package.jsonmiddleware/env-exampleweb-ui/appweb-ui/messagesweb-ui/configci-workflowsscriptscomposeNine of fourteen zones read CLEAN. The baseline is updated consistently in all three places the README requires:
decoupling-baseline.json, the README "Baseline 214" line, and theacceptance.mdguard row.Note on zero.
scripts/check-core-decoupling.mjscounts itself — 27 of the remaining 214 are its own literal pattern list. The total can never reach the0the README defines as done until the script excludes its own path. That is a one-line fix and belongs to C13; flagging it because "ratchet reads 0" is the stated definition of done and it is currently unreachable by construction.What was deleted
middleware/src/devplatform/middleware/test/devplatform/middleware/sidecars/(dev-runner,dev-runner-daemon,dev-dind)web-ui/app/admin/dev-platform/middleware/packages/dev-runner-shim/web-ui/app/_components/devjobs/middleware/src/{index,config}.tsdocker-compose.dev-platform.yamlPlus
scripts/dev-transcript.ts,web-ui/app/_lib/useDevJobEvents.ts, the/admingrid card, and all 43DEV_*config keys.i18n: 299 leaf keys per locale (
adminDevPlatform.*288,chat.devJob.*9,admin.index.cards.devPlatform.*2) = 598 across en+de, plus the 4 orphanedi18n-identical-allowlist.jsonentries the validator flagged.npm run i18n:check→ OK, 3560 keys.KEPT — and why
Every item below was traced to a live importer or an explicit spec instruction before being left in place.
auth/publicPaths.ts— both exemptions0022–0030services/githubAppJwt.tsexpress,pg,zodpeerDependencies, resolved through the hostnode_modulessymlink. Removing them breaks every installed plugin at runtime with no compile-time signal here. Comment added tomiddleware/package.jsonas §5 asked.DEV_ENDPOINTS_ENABLED,DEV_ENDPOINTS_LOOPBACK_ONLY/api/dev/*). Shares the prefix, nothing else.FLY_APP_NAMECORE_DEV_PREFIXED_KEYSalready said so in prose.devFlag()PUBLIC_MCP_*.uiRouteCatalog/requiresNavFromstampevery remaining image consumes. Only its dev-runner-daemon justification comment is gone.Traced importers from outside the delete set
Four, all resolved:
middleware/scripts/dev-transcript.ts→ deleted (in scope).middleware/src/config.ts→ theDevPlatformConfigtype import; namespace collapsed.middleware/src/index.ts→ the assembly; deleted.middleware/test/adversarial/adversarialModel.ts→composeBrief— see below. This is the one that needed a judgement call.The adversarial injection eval's
brief_delimiterTier A probe ran the realcomposeBriefout ofsrc/devplatform/. That is 5 of 12 deterministic scenarios, and the only deterministic coverage for thedirect_injectionandindirect_injectionvectors.It leaves with the code it measured. A probe against a module core no longer ships measures a library, not a deployed defense — and keeping a core copy would be dead code the eval then reports green against, which is worse than an honest gap. The plugin repo already owns
briefComposer.tsand its unit test.Removed together (the harness throws on a corpus row naming an unknown probe, so these cannot drift apart): the probe,
direct-injection.jsonl,indirect-injection.jsonl, the negative-control test, and 5baseline.jsonrows. Deterministic corpus 12 → 7.middleware/test/adversarial/README.mdnow states the gap and both ways to close it — they are not alternatives:composeBriefit owns;exfiltrationandtool_outputdeterministic coverage is unchanged, andsanitizeIssueBody(core's builder) keeps its probe.Two things that fell out of the deletion
A latent ordering bug, fixed. The
/api/v1/dev-runner/llm/express.jsoncarve-out sat before the conductor's inbound webhook router. That router uses a route-levelexpress.raw(), which body-parser short-circuits once an earlier parser has marked the request_body— so its HMAC path was reading a parsed object, not aBuffer. Deleting the carve-out restores the order the surrounding comments already promised:pluginRawBodyMount→ conductor raw router → globalexpress.json.Four core config keys had to be rescued (commit
af131b3d) —tsccaught them, which is precisely why this is one PR. ThreeLONG_RUNNING_*keys (the generic W2-2 / #543 task seam) sat interleaved with the dev-platform keys inside the same schema block, andFLY_APP_NAMEwent with them despite the prose saying it must survive. All four are restored under an explicit heading stating why they are core.H3 — resolved by omission
chat/page.tsxno longer hardcodestool.name === 'dev_job_start'. The plugin's start tool falls through to the generic long-running-task card (isTaskStartToolName/TaskChatCard). That is the accepted-degradation branch of D2, not the declarative card schema — a plugin wanting a richer card goes through the C8 UI contract. The README's "decisions before code" entry for H3 is struck through.Supply chain
dev-runneranddev-runner-daemonare no longer built, signed or published here;id-token: writeis dropped frompublish-images.ymland both callers (auto-release.yml,release.yml), since keyless cosign was its only user. Theif: falsenpm-provenance job keeps its own grant.--certificate-identity-regexplands — that is P4 in the plugin repo, and it must land before the first image publishes from there.Verification
middlewarebuildmiddlewaretypecheck (+ golden + adversarial tsconfigs)middlewarenpm testmiddlewaretypecheck:testratchetmiddlewarelintweb-uitypecheckweb-uinpm test(vitest)web-uilintweb-uii18n:checkweb-uii18n:literalstranslate = 0package-lock.jsonnpm civerified cleannpmleftpackages/dev-runner-shimin the lockfile as anextraneous: truestanza rather than dropping it; removed explicitly and re-verified withnpm ci.Follow-ups this PR does not do
dryRunfirst).publicPathsexemptions. Nothing else in that PR.plugin-apiCHANGELOG, and the ratchet's self-exclusion without which the count cannot reach 0.brief_delimiterprobe; land the cosign identity regexp before publishing.conductorRoles@1(role-principal gates are unapprovable without it — the largest functional gap),githubAppJwt@1,usageTelemetry@1.Do not merge before C11/C12 sequencing is agreed.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.Cross-family review (Forge)
Independent review by an OpenAI-family model (GPT-5.4, reasoning=high), run against the
code rather than the PR description. Focus for a deletion of this size is collateral:
what left that should have stayed, and what stayed that no longer resolves.
Verdict: MERGE — after three fixes, now in
2c6cc20cGate results (all run on the branch, not inferred)
middlewarebuild / typecheck / lintmiddlewaretypecheck:test371 known errors, no regressions (baseline 371)middlewarefull suiteweb-uilint / typecheck / buildweb-uivitesti18n-validate/i18n-literal-scan3560 keys, locales en, de· 0 orphaneddevPlatform/devJobkeyscheck-core-decoupling.mjsDev Platform references held at 214eval:adversarial7/7 defenses held (100%), 0 breachedactionlintdocker compose config.envcomplaints are pre-existing and unrelated)Collateral check — clean
Every deletion outside a
dev-platform-named path traces to the checklist:scripts/dev-transcript.ts(Zone 11),sidecars/dev-dind/**, thedevjobs/chat surfaceand
useDevJobEvents.ts(Zone 15), and the two adversarial corpus files (documented below).No survivor imports them — statically or dynamically. Checked beyond what
tsccan see:route tables, plugin manifests, CI YAML, Dockerfiles, all eight compose files,
render.yaml,fly/,package.jsonworkspaces/scripts,tsconfigreferences.workspacesis["packages/*"]andpackages/dev-runner-shimis gone, so nothing dangles.The kept-list verifies: migrations
0022–0030all present (C11), bothpublicPathsexemptions present (C12),
services/githubAppJwt.tspresent,express/pg/zodstill independencies. Config keys: 43 removed, allDEV_*, none interleaved —DEV_ENDPOINTS_ENABLEDand
DEV_ENDPOINTS_LOOPBACK_ONLYcorrectly survive despite the prefix, and the fourLONG_RUNNING_*/FLY_APP_NAMEkeys the author restored are the complete set. Ratchet isconsistent in all three places (JSON
214, READMEBaseline **214**, acceptanceBaseline **214**).The one thing that needed real work
The
express.jsoncarve-out removal changes behaviour, correctly, and nothing locked it.On
mainthe mount order waspluginRawBodyMount→ carve-out-wrappedexpress.json→conductorWebhooksInbound→ globalexpress.json. The conductor router therefore satbehind a JSON parser. Since body-parser stamps the request
_body, the router's ownroute-level
express.raw()short-circuits and the handler falls toBuffer.isBuffer(req.body) ? req.body : Buffer.alloc(0)— it HMACs an empty buffer.Measured with a standalone probe on both orders:
So on
mainevery correctly-signed inbound Conductor webhook answers 401. This PR fixesthat as a side effect — the answer to the review question is yes, the router now gets the
raw body.
But no test could see it.
conductorWebhookInbound.test.tsbuildsconst app = express(); app.use(createConductorWebhooksInboundRouter(...))— the router alone on a bare app, whichpasses under both orders. That is precisely how the broken ordering shipped green, and
reintroducing any parser above that mount would break production silently again.
Added, all mutation-checked:
orders (202
startedvs 401, nothing claimed or emitted);778RouteMounts.wiring.test.tspinning the realindex.tsorder, which fails loudly if either anchor is renamed rather than passing vacuously.
Mutation evidence: forcing the parser first fails 8 tests; removing it fails exactly the one
new test that depends on it; reordering
index.tsfails the wiring test on the orderingmessage; renaming the anchor fails it on the anchor message.
Also fixed
publish-images.ymlreferencedmatrix.sha_on_release, undefined since thedev-runneranddev-runner-daemonmatrix entries were deleted.actionlinterrors onthis branch and is clean on
main. Dropping the clause also restores what the adjacentcomment already claims (sha tags on the edge build only). The
steps.guardblock was leftalone — it still computes the
stampthatbuild-argsconsumes, so it is not dead.middleware/package.json— the new_dependencyNotewas indented four spaces andpushed
"dependencies"to column 0. Valid JSON, but off-style and any rewrite wouldreflow it. Text and ordering unchanged.
213 files deleted, measured217. Corrected. Theadjacent
43 config keysclaim was checked and is correct; left alone.The ratchet caught my own first patch (
214 → 215, one comment naming adev-runnerpath).Reworded rather than baseline-raised, per the spec's own rule.
Adversarial corpus reduction — honest, and the follow-up is real
12 → 7 is a genuine coverage loss, not an accounting trick:
brief_delimiterran the realcomposeBriefout ofsrc/devplatform/, so it went with the module.direct_injectionandindirect_injectionnow have no Tier A probe at all. The README says exactly that, inthose words, and names two non-alternative remedies (plugin repo ports the probe; core
re-establishes a Tier A delimiter probe against a surface it still ships). The gate still
functions on what remains —
7/7 held, baseline rows match the surviving scenario idsone-for-one, and the behavioral scenario is correctly treated as expected-absent without a
key. Accepting the reduction is reasonable; the follow-up should be tracked as an issue,
not left to the README.
Nits, not blockers
publish-images.yml's "skip a matrix image whose Dockerfile is not in the tree yet" guardnow protects three Dockerfiles that all exist. Harmless, and it does double duty computing
stamp, so it was left in place.actionlintis not wired into CI, so thesha_on_releaseerror would not have blocked themerge. Worth wiring up separately — it caught a real regression here for free.