Skip to content

refactor(conductor): delete the dead dev-job step coupling (epic #470 C5) - #554

Merged
Weegy merged 8 commits into
mainfrom
epic/470-c5-conductor-devjob
Aug 12, 2026
Merged

refactor(conductor): delete the dead dev-job step coupling (epic #470 C5)#554
Weegy merged 8 commits into
mainfrom
epic/470-c5-conductor-devjob

Conversation

@Weegy

@Weegy Weegy commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

First change in epic #470 that moves the decoupling count down: 3,303 → 3,167 (−136). Every prior change either added or held it flat.

What this is not

The Conductor is a live feature — 31 files, ~6,200 LOC backend, 23 UI files, 7 own migrations, its own spec. Workflow runs, steps, human approval gates, templates, the Designer canvas: all untouched. All 204 conductor tests pass.

What was dead

One step type inside it. dev.job was meant to let a workflow launch a Dev Platform job, park the run, and resume on its terminal outcome. It was built and never wired:

  • conductor/index.ts constructs the executor with no devJob dep, so the dispatch branch was permanently false — and git log -S 'devJob' on that file returns nothing, so it was never wired in any commit
  • the launch half had no implementation at all: createConductorJob, setAwaitId, getAwaitId existed only as interface members, and dev_jobs.conductor_await_id was never selected or written
  • DevJobOutcomeEmitter.emit() had no caller in src/
  • nothing scheduled the reconciliation sweep
  • no bundled template referenced dev.job, and listActions never included it — so the step could not even pass validation

That last point matters: nobody could have authored this step.

Removed

File
conductor/devJobStepEffect.ts deleted, 122 LOC
devplatform/devJobConductorBridge.ts deleted, 113 LOC
test/conductorDevJobStep.test.ts deleted, 358 LOC
conductor/runExecutor.ts 31 → 0 refs
conductor/awaitStore.ts 6 → 0 refs
conductor/routes.ts 1 → 0 refs

Kept: migration 0024's conductor_await_id column. Migrations are forward-only here and dropping a column is the one irreversible act in this change — it is marked orphaned instead.

The subtle part

awaitStore's human-inbox query excluded channel_type = 'dev_job'. That predicate is now gone rather than genericised: after the delete, openHumanAwait is the sole writer of conductor_awaits, so a filter whose complement no code path can populate asserts an invariant in the wrong place. A channel allow/denylist would also fail open for a future await kind that forgot to register itself.

On a database that somehow held such a row, it becomes visible in the operator inbox and a resolve attempt returns 403 (the holder gate compares against dev_job:<id>, which no user id can equal). With the resolver deleted, such a run is permanently parked — the await row is the only evidence of it, so surfacing it is the better outcome than hiding it.

A review catch worth naming

The compensating test added for the removed predicate could not detect that predicate being restored. Its fixtures were teams/telegram/web, so re-adding AND channel_type <> 'dev_job' left it green — it did not cover the exact regression it existed for. Fixed with a dev_job fixture row and mutation-checked: with the predicate restored 1 fail, without it 3 pass.

Also

Propagates the decision into the specs, which still described the step as a capability the extraction must carry and H2 as a registry to build. H2 needed no mechanism after all — the coupling was dead, so deleting it was the whole fix. That removes one of the three "hard couplings" from the plan.

Test plan

  • Middleware suite: 5,197 pass. Two intermittent failures are unrelated and load-sensitive (a 401 session test and one that measures wallclock speedup of parallel dispatch); neither reproduces consistently and neither is in conductor
  • All 204 conductor tests pass, including runs, awaits, quorum/timeout, reminders and the resume worker
  • Typecheck clean; ratchet 3,303 → 3,167, no zone rose
  • Every removed symbol grepped across middleware/, web-ui/, scripts/, .github/ — the exported DevJobPortUnavailableError included — with no consumer outside the delete list

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Weegy added 8 commits July 30, 2026 12:58
…C5)

The Conductor's `dev.job` step was built in W3 and never wired: `conductor/index.ts`
constructs `ConductorRunExecutor` without a `devJob` dep, so the dispatch branch was
permanently false; the launch half had no implementation at all (`createConductorJob`,
`setAwaitId`, `getAwaitId` existed only as interface members); `DevJobOutcomeEmitter.emit()`
had no caller in `src/`; nothing scheduled the reconciliation sweep; and no bundled template
referenced `dev.job` (which `listActions` rejects anyway). Per
`specs/470-dev-platform-plugin/dormant-capabilities.md` §1 the verdict is DELETE, not
genericise — nothing needs the generic version.

Deleted whole: `conductor/devJobStepEffect.ts`, `devplatform/devJobConductorBridge.ts`,
`test/conductorDevJobStep.test.ts`. Stripped from `runExecutor.ts`: both imports, the
`devJob` field + ctor dep, `DevJobPortUnavailableError`, the dispatch branch,
`resolveDevJobAwait`, `reconcileTerminalDevJobAwaits`, `openDevJobAwait`.

`awaitStore.listWaiting` drops its `AND channel_type <> 'dev_job'` predicate outright
rather than keeping a generic filter. `openHumanAwait` is now the single caller of
`create`, so every await has a human holder by construction and the excluded set is
provably empty; a filter whose complement no member can enter asserts an invariant in
the wrong place, and a future non-human await kind would have to remember to add itself
to survive it. New `test/conductorAwaitStore.test.ts` guards the channel-agnostic
contract (mutation-checked: re-adding a channel predicate fails it).

`dev_jobs.conductor_await_id` (migration 0024) is KEPT — migrations are forward-only here
and a DROP is the one irreversible act. It is marked as an orphaned column so the schema
is not misread as evidence of a live feature.

Core-decoupling ratchet: 3303 -> 3164 (-139), no zone rose.
First change in this epic that moves the decoupling count DOWN.
3,303 → 3,167 (−136): middleware/src −96, middleware/test −43.

WHAT THIS IS NOT: the Conductor is a live feature — 31 files, ~6,200 LOC
backend, 23 UI files, 7 migrations, its own spec. Runs, steps, human
approval gates, templates, the Designer canvas are all untouched. All
204 conductor tests pass.

WHAT WAS DEAD: one step type inside it. `dev.job` was meant to let a
workflow launch a Dev Platform job, park the run, and resume on its
terminal outcome. It was built and never wired:
  - conductor/index.ts constructs the executor with NO devJob dep, so
    the dispatch branch was permanently false — and `git log -S` shows
    it was never wired in ANY commit
  - the launch half had no implementation at all: createConductorJob,
    setAwaitId and getAwaitId existed only as interface members, and
    dev_jobs.conductor_await_id was never selected or written
  - DevJobOutcomeEmitter.emit() had no caller in src/
  - nothing scheduled the reconciliation sweep
  - no bundled template referenced dev.job, and listActions never
    included it, so the step could not pass validation

Removed: conductor/devJobStepEffect.ts (122 LOC), devplatform/
devJobConductorBridge.ts (113), test/conductorDevJobStep.test.ts (358),
and every dev-job reference in runExecutor.ts (31 → 0), awaitStore.ts
(6 → 0) and routes.ts (1 → 0).

KEPT: migration 0024's conductor_await_id column. Migrations are
forward-only here and dropping a column is the one irreversible act in
this change; it is marked orphaned instead.

THE SUBTLE PART: awaitStore's human-inbox query excluded
`channel_type = 'dev_job'`. That predicate is now gone rather than
genericised, because after the delete `openHumanAwait` is the sole
writer of conductor_awaits — a filter whose complement no code path can
populate asserts an invariant in the wrong place, and a channel
allow/denylist would fail open for a future await kind that forgot to
register itself.

The compensating test needed a fix the review caught: its fixtures were
teams/telegram/web, so restoring `<> 'dev_job'` would have left it
green — it could not detect the exact regression it exists for. Added a
dev_job fixture row and mutation-checked it: with the predicate restored
1 fail, without it 3 pass.

Also propagates the decision into the specs, which still described the
step as a capability the extraction must carry and H2 as a registry to
build. H2 needed no mechanism after all — the coupling was dead, so
deleting it was the whole fix.
The channel-api work added 3 dev-platform references (test/packages).
Fourth legitimate raise: main ADDED dev-platform code; core did not
re-acquire a dependency.
…-devjob

# Conflicts:
#	specs/470-dev-platform-plugin/decoupling-baseline.json
…-devjob

# Conflicts:
#	specs/470-dev-platform-plugin/decoupling-baseline.json
@Weegy
Weegy merged commit ab046e8 into main Aug 12, 2026
10 checks passed
Weegy added a commit that referenced this pull request Aug 12, 2026
#554 (C5) landed, so the shared counter moved under this branch. Both
PRs reduce disjoint sets of references: C2a still removes exactly 86,
the same delta it removed against the pre-#554 main.

Also fixes doc drift the merge exposed: README and acceptance.md both
still quoted 3,167 on main while the committed baseline was already
3,312 — the number is in three places and #554 updated only the JSON.
All three now read 3,226.
Weegy added a commit that referenced this pull request Aug 12, 2026
…#470 C2a) (#555)

* refactor(plugin-api): delete the unreachable ctx.devJobs plugin surface

`ctx.devJobs` was a published plugin accessor that nothing ever provided.
It resolved its host service lazily per call, so every invocation threw
"dev-platform host service unavailable". No manifest in this repo, in the
private byte5 plugin set, or in any sibling repo ever declared
`permissions.devJobs`, so no consumer has ever existed.

Per specs/470-dev-platform-plugin/dormant-capabilities.md section 2 this is
a pure deletion of an unreachable surface. No permission gate is added to
ctx.services: `provide("devJobs", ...)` exists nowhere, so both `ctx.devJobs`
and `ctx.services.get("devJobs")` already yield nothing. Removing the
accessor opens no hole. No package manifest, serviceRegistry or builder
codegen is touched, and no installed plugin changes behaviour.

Deleted:
- plugin-api: the `devJobs?` field on PluginContext and the six types
  DevJobKind, DevJobStatus, DevJobDescriptor, DevJobCreateRequest,
  DevJobEventRecord, DevJobsAccessor
- host pluginContext: the permissions gate, the context spread,
  DevJobsHostService and createPluginDevJobsAccessor
- manifestLoader: the `permissions.devJobs` parse and its two summary fields
- admin-v1: the dev_jobs / dev_jobs_repos_hint DTO fields
- devRepoPluginGrantStore.ts and pluginDevJobsAccessor.test.ts (whole files)

The descriptor/event view types survive core-locally in
src/devplatform/devJobTypes.ts, which travels with the dev-platform tree when
it moves. DevJobKind/DevJobStatus are re-exported from src/devplatform/types.ts
rather than redefined.

devJobsHostService survives for the chat surface but sheds everything that
existed only for the plugin path: listGrantedRepoIds and the grants dep,
the plugin-shaped createJob (and with it repoStore, resolveJobPlacement and
mintRunnerToken), and cancelJob's requestedByPluginId creator check with its
finalize dep. chatDevJobService only ever called getJob, listJobs and
listJobEvents; it passed inert stubs for the rest.

Migrations 0024 (dev_repo_plugin_grants) and 0025 (source='plugin') are kept
- the migration set is forward-only - and are annotated as knowingly orphaned
so a future reader does not read the schema as evidence of a live feature.

Back-compat: a stale manifest still declaring `permissions.devJobs` installs
and activates unchanged. Unknown permission keys are ignored today; that was
implicit, and test/manifestDevJobsLegacyKey.test.ts now asserts it explicitly
so a future strict-validation change cannot break stale manifests silently.

* chore(470): lower the ratchet baseline to 3,220 after the ctx.devJobs deletion

* chore(470): resync C2a with main (#552, #553)

* fix(470): type-sound C2a legacy-manifest test + resync baseline

The test/ typecheck ratchet (#573, landed after this branch was last
touched) flags 4 errors in test/manifestDevJobsLegacyKey.test.ts:

- `createPluginContext` gained two required options (notificationRouter,
  uiRouteCatalog). They are only dereferenced inside lazy accessors, so
  the test passed at runtime while being type-unsound. Stubbed both.
- Three `as Record<string, unknown>` casts were unsound (TS2352). Two are
  unnecessary — `mcp` is a declared field on PluginPermissionsSummary.
  The key-absence checks now use `Object.hasOwn`, which asserts the key
  is absent rather than merely undefined.

Ratchet baseline unchanged at 406: no new debt. Decoupling baseline
resynced against current main, 3,448 -> 3,362.

* chore(470): resync C2a baseline onto main after #554 — 3,312 -> 3,226

#554 (C5) landed, so the shared counter moved under this branch. Both
PRs reduce disjoint sets of references: C2a still removes exactly 86,
the same delta it removed against the pre-#554 main.

Also fixes doc drift the merge exposed: README and acceptance.md both
still quoted 3,167 on main while the committed baseline was already
3,312 — the number is in three places and #554 updated only the JSON.
All three now read 3,226.
Weegy added a commit that referenced this pull request Aug 12, 2026
…,288

C5 (#554) and C2a (#555) both landed, taking main's baseline to 3,226.
C3's own delta is unchanged: +62 (middleware/src +29, middleware/test
+33), so the hand-raised baseline is 3,288.

That +62 has now measured identically against three successive mains —
before #554, after #554, after #555. It is C3's own concentration
effect, not drift picked up from whatever else landed, and the README
now says so instead of quoting a single merge-time measurement.

Also fixes the baseline number in acceptance.md, which still read 3,365.
The number lives in three places — the JSON, the README, and the
acceptance ratchet row — and nothing checks that they agree.
Weegy added a commit that referenced this pull request Aug 12, 2026
… C3) (#557)

* refactor(dev-platform): one-way layering + namespaced config (C3)

* chore(470): hand-raise the ratchet baseline to 3,365 for C3

FIRST TIME the baseline rises for OUR OWN change rather than for
something main added. That deserves the explanation to be in the diff,
which is why the script refuses `--update` here and forces a hand-edit.

Why it rose (+59): the refactor collapses 41 flat DEV_*/FLY_* config
keys into one `config.devPlatform` object. The zod schema is unchanged —
proven byte-identical, 562 lines, empty diff — so every key name still
appears there, and the new mapping layer that builds the namespace names
each one a SECOND time. Net: config.ts +48, the new devplatform/config.ts
+36, against index.ts −33 and wireDevPlatform.ts −8. The eight moved
routers are a wash: same zone, different path.

Why it is worth taking: index.ts drops from ~20 threaded config values
to one argument, and the layering arrow now points one way (zero
src/devplatform → src/routes edges, zero src/routes → devplatform).
Both make the P4 file-move mechanical instead of a rewrite. And every
one of those +59 references is deleted at extraction — the schema keys,
the mapping, and the type file all go with the tree.

The honest tension: the ratchet counts identifiers, and this refactor
concentrates identifiers rather than removing them. Letting that block a
correct refactor would be the measurement driving the work. Documented
in README so the next raise is judged against the same two criteria and
nothing wider.

* chore(470): re-justify the C3 baseline raise against current main — 3,510

Merging main moved the numbers: the hand-raised 3,365 was measured against
a main that has since grown. Re-measured, C3's own delta is +62 over
main's 3,448 (middleware/src +29, middleware/test +33), not the +59
recorded when the branch was last touched.

The src half is unchanged in kind: 41 flat DEV_*/FLY_* keys collapse into
one `config.devPlatform` object, and the mapping layer names each key a
second time.

The test half was never written down. It is the same effect — a shared
`devPlatformConfig.harness.ts` plus the moved routers' new
`src/devplatform/routes/…` import paths. Checked rather than asserted:
all 212 added matching lines in the test zone are inside
`middleware/test/devplatform/`, none outside. No core test acquired a
dev-platform dependency, which is the only thing a rise is allowed to
mean here.

README updated to 3,510 with the test-zone reasoning, so the next raise
is judged against the same two criteria.

* chore(470): re-justify the C3 baseline raise onto main after #555 — 3,288

C5 (#554) and C2a (#555) both landed, taking main's baseline to 3,226.
C3's own delta is unchanged: +62 (middleware/src +29, middleware/test
+33), so the hand-raised baseline is 3,288.

That +62 has now measured identically against three successive mains —
before #554, after #554, after #555. It is C3's own concentration
effect, not drift picked up from whatever else landed, and the README
now says so instead of quoting a single merge-time measurement.

Also fixes the baseline number in acceptance.md, which still read 3,365.
The number lives in three places — the JSON, the README, and the
acceptance ratchet row — and nothing checks that they agree.
@Weegy
Weegy deleted the epic/470-c5-conductor-devjob branch August 14, 2026 06:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant