Repository navigation
Description
Summary
ScopedPreflightOverrideDefinition lets an extension declare which commands its preflight override owns, and the host honours it at dispatch. But pm health's extension_preflight_override_collision check never consults that declaration: it reports a collision between any two extensions that register a preflight at all, even when their declared commands sets are disjoint.
The result is that the collision is unresolvable from extension code. There is no registration an author can write that clears the warning short of not registering a preflight.
This looks like the preflight analogue of #897 (renderer collision check ignoring host-enforced ownership), which was fixed for renderers.
Evidence (pm 2026.8.10)
Installing 15 published pm extensions into one project gives pm health → ok: false, 24 unique warnings. 21 of them are a complete pairwise clique over the seven extensions that register a preflight (C(7,2) = 21):
extension_preflight_override_collision:project:pm-github:project:pm-gantt-chart
extension_preflight_override_collision:project:pm-jira:project:pm-gantt-chart
extension_preflight_override_collision:project:pm-jira:project:pm-github
extension_preflight_override_collision:project:pm-linear:project:pm-gantt-chart
...
extension_preflight_override_collision:project:pm-todos:project:pm-slack-standup
A clique over "extensions that registered a preflight" is the signature of a check that is not looking at ownership at all.
The fix that should have worked, and does not
pm-todos registered an unscoped override whose first statement was a runtime scope guard:
api.registerPreflight((ctx: PreflightOverrideContext) => {
const passthrough = { /* ... */ };
if (ctx?.command !== "todos import") return passthrough; // runtime-only scoping
/* ... */
});I converted it to a static declaration, which is exactly what the SDK type exists for:
api.registerPreflight({
commands: ["todos import"],
run: (ctx: PreflightOverrideContext) => { /* ... */ },
});Verified after the change:
tsc --noEmitclean — the field name matchesScopedPreflightOverrideDefinition, so this is not a silently-ignored misnamed field.- The built artifact carries the declaration:
grep -c 'commands: \["todos import"\]' dist/index.js→1, and the same string is present in the installed copy under.agents/pm/extensions/pm-todos/dist/index.js. - The extension still activates and its commands still register (
pm todos --helpworks), andpm health --fullreports noextension_*failed|error|invalidwarning. - The six
pm-todospreflight collision warnings are byte-identical before and after. Unique warning count stays at 24.
So a correctly-scoped preflight collides with six extensions it can never run alongside.
Expected
extension_preflight_override_collision should be raised only when two registrations can actually contend — i.e. their effective command sets intersect. Concretely:
- two unscoped registrations collide (both are global);
- an unscoped registration collides with every scoped one;
- two scoped registrations collide only where
commandsoverlap.
Under that rule the fleet case above collapses from 21 warnings to 0 once each extension declares ownership, and the warning regains its meaning: it would then flag a real double-registration on the same command rather than the mere presence of a second extension.
Why this matters
pm health is the gate the fleet runs in CI with --strict-exit. A warning that cannot be cleared by correct code trains authors to ignore the check, and it is currently the sole reason a project that installs the published extension set reports ok: false. It also removes the incentive to declare ownership at all — which is the thing that makes preflight dispatch correct in the first place, since an unscoped override otherwise runs on every command.
Related
- Renderer collision check ignores the ownership the host itself enforces, so correctly scoped overrides can never reach pm health ok — and an unscoped override that destroys all CLI output reports the identical warning #897 — same blind spot for renderer overrides, fixed there.
- Preflight overrides have no ownership declaration, so any workspace with two preflight extensions can never reach pm health ok — the defect just fixed for renderers in #893 #907 — added the ownership declaration; the health check appears not to have been updated alongside it.
Activity
coderabbitai commentedon Aug 10, 2026
🔗 Related PRs
#150 - Extension SDK platform maturity: typed flags, self-identity, lifecycle teardown, field-type validation & override testing helpers [merged]
#326 - feat(extensions): least-privilege capability reconciliation (pm-fk84) [merged]
#479 - feat(package): isolate project doctor diagnostics [merged]
#657 - SDK extension authoring, trust boundaries, and scoped rendering [merged]
#884 - SDK extension inventories, scoped activation, and stable agent context [merged]
📝 Issue Planner
Check the box below or use the
@coderabbitai plancommand to generate an implementation plan and prompts that you can use with your favorite coding assistant.🧪 Issue enrichment is currently in open beta.
You can configure auto-planning by selecting labels in the issue_enrichment configuration.
To disable automatic issue enrichment, add the following to your
.coderabbit.yaml:💬 Have feedback or questions? Drop into our discord!
Separate dispatch scoping from collision detection in the preflight c…
Declare static command ownership for the todos import preflight overr…
unbraind commentedon Aug 10, 2026
Tracked canonically as pm-zryb9d under the extension ownership lineage. It owns health/runtime parity for scoped preflight command intersections and fleet negative controls.
SDK assurance: measurements, assertions, lifecycle gates, and durable…