From ef107a5e14cf3ff25c6fbc6526a558c994fa8139 Mon Sep 17 00:00:00 2001 From: SteveBot <1153461+unbraind@users.noreply.github.com> Date: Fri, 14 Aug 2026 08:05:16 +0200 Subject: [PATCH] Scope preflight override to owned commands registerPreflight used the bare-function form, which the pm runtime treats as a global override. Two globally-scoped overrides genuinely contend: pm health reports extension_preflight_override_collision for every installed pair, so all six fleet packages installed together produce 15 warnings and ok: false. Rewrite the registration to the scoped object form, declaring exactly the mutating github/gh-issues command paths the override guards. The runtime matches a command against the commands array by exact normalized path, so the array must list the full command paths (github sync, github export, github import, gh-issues import, github project import, github project sync) rather than a bare top-level name: a bare 'github' never matches 'github sync' and would silently disable the early warning. The authoritative credential gate stays in the command handlers; this only narrows the override's scope so it no longer contends with other packages' overrides. Verified: with two of these packages installed, pm health reported ok: false with one collision warning; after scoping both, ok: true with zero warnings and the guarded command still runs. --- .agents/pm/history/pm-github-yhhz.jsonl | 6 ++++ .agents/pm/issues/pm-github-yhhz.toon | 19 +++++++++++ CHANGELOG.md | 6 ++++ index.ts | 45 ++++++++++++++++--------- test/smoke.test.ts | 33 ++++++++++++++++++ 5 files changed, 94 insertions(+), 15 deletions(-) create mode 100644 .agents/pm/history/pm-github-yhhz.jsonl create mode 100644 .agents/pm/issues/pm-github-yhhz.toon diff --git a/.agents/pm/history/pm-github-yhhz.jsonl b/.agents/pm/history/pm-github-yhhz.jsonl new file mode 100644 index 0000000..05e9ab8 --- /dev/null +++ b/.agents/pm/history/pm-github-yhhz.jsonl @@ -0,0 +1,6 @@ +{"ts":"2026-08-14T05:58:22.786Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"create","patch":[{"op":"add","path":"/metadata/id","value":"pm-github-yhhz"},{"op":"add","path":"/metadata/title","value":"Scope preflight override to pm-github's owned commands"},{"op":"add","path":"/metadata/description","value":"registerPreflight used the bare-function (global) form, which collides pairwise with every other installed package's preflight override (pm health reports extension_preflight_override_collision). Rewrite it to the scoped object form declaring exactly the mutating github/gh-issues command paths the override guards, so it no longer contends with other packages. The runtime matches a command against the commands array by exact normalized path, so the array lists the full command paths (github sync, github export, github import, gh-issues import, github project import, github project sync) — not a bare top-level name, which would never match and silently disable the early warning. The authoritative credential gate stays in the command handlers; this only narrows the override's scope."},{"op":"add","path":"/metadata/type","value":"Issue"},{"op":"add","path":"/metadata/status","value":"open"},{"op":"add","path":"/metadata/priority","value":2},{"op":"add","path":"/metadata/tags","value":[]},{"op":"add","path":"/metadata/created_at","value":"2026-08-14T05:58:22.786Z"},{"op":"add","path":"/metadata/updated_at","value":"2026-08-14T05:58:22.786Z"},{"op":"add","path":"/metadata/author","value":"pi-agent"}],"before_hash":"3cc22dff72be7b14824654a7a64ea62b04799939b2fee54c1b5f52ca60bf6df0","after_hash":"735c0b5b215bb02c81b81865a6bbee96f50cf9c648a5704415da417f05bba0af","message":""} +{"ts":"2026-08-14T05:58:31.685Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"update","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-08-14T05:58:31.685Z"},{"op":"replace","path":"/metadata/status","value":"in_progress"}],"before_hash":"735c0b5b215bb02c81b81865a6bbee96f50cf9c648a5704415da417f05bba0af","after_hash":"c7ce8ad4c59908200381f1c7cedf100be9d563e1ab3852bf922139006b4abc7b"} +{"ts":"2026-08-14T05:58:32.683Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-08-14T05:58:32.683Z"},{"op":"add","path":"/metadata/files","value":[{"path":"index.ts","scope":"project"}]}],"before_hash":"c7ce8ad4c59908200381f1c7cedf100be9d563e1ab3852bf922139006b4abc7b","after_hash":"3162edc6bd1a4cad452ed58c19dd83f9e7b364a234919626400c3e9f49ffe2fc"} +{"ts":"2026-08-14T05:58:33.826Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"files_add","patch":[{"op":"add","path":"/metadata/files/1","value":{"path":"test/smoke.test.ts","scope":"project"}},{"op":"replace","path":"/metadata/updated_at","value":"2026-08-14T05:58:33.826Z"}],"before_hash":"3162edc6bd1a4cad452ed58c19dd83f9e7b364a234919626400c3e9f49ffe2fc","after_hash":"4823285e0cd9ce89220d228228453a9908211b37e7dc0515182159dfd2526a92"} +{"ts":"2026-08-14T06:01:10.116Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"tests_add","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-08-14T06:01:10.116Z"},{"op":"add","path":"/metadata/tests","value":[{"command":"npm test","path":"test/smoke.test.ts","scope":"project","note":"Asserts registerPreflight is a scoped object whose commands array equals pm-github's owned mutating command paths (github sync, github export, github import, gh-issues import, github project import, github project sync) and run is a function. Test title: preflight override is scoped to pm-github's owned command paths"}]}],"before_hash":"4823285e0cd9ce89220d228228453a9908211b37e7dc0515182159dfd2526a92","after_hash":"a6a09803b8c2205d954cf15c3bde20e17da3a0d9fb2401c3ed9cc9b3613dcccf"} +{"ts":"2026-08-14T06:03:30.132Z","author":"pi-agent","author_source":"asserted","agent_harness":"pi","agent_model":"glm-5.2","agent_model_source":"environment","agent_instance":"56b7d1a35febb62ac00aa45f","agent_provenance":{"model":{"value":"glm-5.2","source":"environment"},"effort":null,"role":null,"topic":null},"op":"close","patch":[{"op":"replace","path":"/metadata/updated_at","value":"2026-08-14T06:03:30.132Z"},{"op":"replace","path":"/metadata/status","value":"closed"},{"op":"add","path":"/metadata/closed_at","value":"2026-08-14T06:03:30.121Z"},{"op":"add","path":"/metadata/completed_at","value":"2026-08-14T06:03:30.121Z"},{"op":"add","path":"/metadata/close_reason","value":"completed"}],"before_hash":"a6a09803b8c2205d954cf15c3bde20e17da3a0d9fb2401c3ed9cc9b3613dcccf","after_hash":"0434a8c16a01573390ab85d68f7bd1e1a7259095dd7bdfd6f841e9398fe7ba6c"} diff --git a/.agents/pm/issues/pm-github-yhhz.toon b/.agents/pm/issues/pm-github-yhhz.toon new file mode 100644 index 0000000..f28b732 --- /dev/null +++ b/.agents/pm/issues/pm-github-yhhz.toon @@ -0,0 +1,19 @@ +id: pm-github-yhhz +title: Scope preflight override to pm-github's owned commands +description: "registerPreflight used the bare-function (global) form, which collides pairwise with every other installed package's preflight override (pm health reports extension_preflight_override_collision). Rewrite it to the scoped object form declaring exactly the mutating github/gh-issues command paths the override guards, so it no longer contends with other packages. The runtime matches a command against the commands array by exact normalized path, so the array lists the full command paths (github sync, github export, github import, gh-issues import, github project import, github project sync) — not a bare top-level name, which would never match and silently disable the early warning. The authoritative credential gate stays in the command handlers; this only narrows the override's scope." +type: Issue +status: closed +priority: 2 +tags: [] +created_at: "2026-08-14T05:58:22.786Z" +updated_at: "2026-08-14T06:03:30.132Z" +closed_at: "2026-08-14T06:03:30.121Z" +completed_at: "2026-08-14T06:03:30.121Z" +author: pi-agent +files[2]{path,scope}: + index.ts,project + test/smoke.test.ts,project +tests[1]{command,path,scope,note}: + npm test,test/smoke.test.ts,project,"Asserts registerPreflight is a scoped object whose commands array equals pm-github's owned mutating command paths (github sync, github export, github import, gh-issues import, github project import, github project sync) and run is a function. Test title: preflight override is scoped to pm-github's owned command paths" +close_reason: completed +body: "" diff --git a/CHANGELOG.md b/CHANGELOG.md index 1542ba2..d8472c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixed + +- Scope preflight override to pm-github's owned commands ([pm-github-yhhz](https://github.com/unbraind/pm-github/blob/main/.agents/pm/issues/pm-github-yhhz.toon)) + ## 2026.8.10 - 2026-08-10 ### Fixed diff --git a/index.ts b/index.ts index f3b0ab4..9b81e0a 100644 --- a/index.ts +++ b/index.ts @@ -4685,23 +4685,38 @@ export default defineExtension({ ]); // ----------------------------------------------------------------------- - // preflight — safe, local guard for mutating github commands. - // Runs before pm core commands; it does NOT make network calls (that would - // be a surprise side effect on every command) and cannot hard-block (the - // runtime swallows preflight throws). It only surfaces a clear, early - // warning when a github mutation is requested without a resolvable token; - // the authoritative validation + non-zero exit lives in the handlers. + // preflight — safe, local guard for mutating github commands. Scoped to + // the command paths pm-github owns (the mutating github/gh-issues paths + // isMutatingGithubCommand recognizes) so it cannot contend with another + // package's preflight override; an unscoped (global) override collides + // pairwise with every other installed package's override (pm health reports + // extension_preflight_override_collision). It runs before those commands; + // it does NOT make network calls (that would be a surprise side effect on + // every command) and cannot hard-block (the runtime swallows preflight + // throws). It only surfaces a clear, early warning when a github mutation + // is requested without a resolvable token; the authoritative validation + + // non-zero exit lives in the handlers. // ----------------------------------------------------------------------- - api.registerPreflight((ctx: PreflightOverrideContext) => { - if (isMutatingGithubCommand(ctx.command, ctx.options || {})) { - if (!resolveGitHubToken()) { - console.error( - "[pm-github preflight] this github command mutates remote state but no GitHub " + - "token is resolvable (GITHUB_TOKEN/GH_TOKEN or `gh auth login`). It will fail.", - ); + api.registerPreflight({ + commands: [ + "github sync", + "github export", + "github import", + "gh-issues import", + "github project import", + "github project sync", + ], + run: (ctx: PreflightOverrideContext) => { + if (isMutatingGithubCommand(ctx.command, ctx.options || {})) { + if (!resolveGitHubToken()) { + console.error( + "[pm-github preflight] this github command mutates remote state but no GitHub " + + "token is resolvable (GITHUB_TOKEN/GH_TOKEN or `gh auth login`). It will fail.", + ); + } } - } - return {}; + return {}; + }, }); // ----------------------------------------------------------------------- diff --git a/test/smoke.test.ts b/test/smoke.test.ts index 693323a..6b9d1d5 100644 --- a/test/smoke.test.ts +++ b/test/smoke.test.ts @@ -139,6 +139,39 @@ test("extension registers at least one capability", async () => { ); }); +test("preflight override is scoped to pm-github's owned command paths", async () => { + // The override MUST register as a scoped object (commands + run), not a bare + // function: a global (unscoped) override collides pairwise with every other + // installed package's preflight override (pm health reports + // extension_preflight_override_collision). The runtime matches a command + // against `commands` by exact normalized path, so the array must list the + // full command paths pm-github mutates — not a bare top-level `github` (which + // never matches `github sync` and would silently disable the early warning). + const ext = await harnessPromise; + const override = ext.assertPreflightOverride(); + assert.ok( + Array.isArray(override.commands) && override.commands.length > 0, + "preflight override must register as a scoped object with a non-empty commands array", + ); + assert.equal( + typeof override.run, + "function", + "scoped preflight override must expose a run function", + ); + assert.deepEqual( + override.commands, + [ + "github sync", + "github export", + "github import", + "gh-issues import", + "github project import", + "github project sync", + ], + "preflight override must be scoped to exactly pm-github's owned mutating command paths", + ); +}); + test("parseNextLink extracts the rel=\"next\" page URL", () => { const header = '; rel="next", ' + '; rel="last"';