📝 Bug Description
The Pi npm publication workflow has two triggers (pi-v* tag push and workflow_dispatch) but runs plugin/pi/test/release-contract.mjs with the actual event ref only when github.event_name == 'push'. The unconditional later npm publish --provenance --access public step therefore has a manual route that never binds publication to a verified immutable release tag. The unconditional npm test includes unit checks of package/pin coherence with a synthesized matching tag; those checks are valuable but do not verify the manual event's ref. This is a workflow guard gap, not evidence that an invalid package has actually been published. Verified in main@c61f601ee9cc3a9cba6abbedd6bef2382318eada.
🔄 Steps to Reproduce
- Inspect
.github/workflows/publish-pi.yml: it declares both push.tags: ["pi-v*"] and workflow_dispatch.
- In that workflow, inspect the
Verify Pi release version step: if: github.event_name == 'push' gates node test/release-contract.mjs "$RELEASE_TAG".
- Inspect the later
Publish to npm with provenance step: it has no event guard and runs npm publish after an unconditional npm test.
- For a
workflow_dispatch event, evaluate that predicate: the release-contract step is skipped while the publish step remains eligible. No workflow was manually dispatched and no package was published to reproduce this control-flow gap.
✅ Expected Behavior
Every publication path must validate a resolved immutable release identity and the package version/installer pin before invoking npm publish; if a manual run has no valid matching tag, it fails closed. Keep the existing Pi test and OIDC/provenance gates. Add a deterministic negative fixture or workflow check proving that a manual invocation without a matching tag cannot reach publish, without publishing a package in tests.
❌ Actual Behavior
workflow_dispatch skips the release-contract step that receives github.ref_name because it is push-only, while npm test and npm publish remain unconditional. The tests exercise contract behavior with constructed tags and check publish-step ordering, but do not bind the manual event ref to the package or verify dispatch guard parity. Actual npm registry acceptance of a specific manual run was not tested and is not claimed.
Operating System
Windows
Engram Version
2.2.1 (installed CLI); workflow verified on main@c61f601
Agent / Client
Other
📋 Relevant Logs
# publish-pi.yml trigger: push pi-v* tags OR workflow_dispatch
# Verify Pi release version: if github.event_name == 'push'
# Publish to npm with provenance: no event condition
# Static workflow-control inspection only; no publish attempted.
💡 Additional Context
plugin/pi/test/release-contract.mjs already checks package version, release tag, installer pin, and CLI guidance when called. plugin/pi/test/package-contract.test.mjs invokes it with a constructed pi-v<package version> and checks tag pass-through, but not workflow_dispatch's actual event ref; publish-workflow.test.mjs checks unconditional npm test immediately before publish. Issue #1349 tracks a larger coordinated Engram/Pi release train; this report is the narrower, present-day manual-path preflight gap and should be linked to, not substituted for, that design. Closed #1048 added candidate-local Pi tests before publish; those tests do not validate release identity.
📝 Bug Description
The Pi npm publication workflow has two triggers (
pi-v*tag push andworkflow_dispatch) but runsplugin/pi/test/release-contract.mjswith the actual event ref only whengithub.event_name == 'push'. The unconditional laternpm publish --provenance --access publicstep therefore has a manual route that never binds publication to a verified immutable release tag. The unconditionalnpm testincludes unit checks of package/pin coherence with a synthesized matching tag; those checks are valuable but do not verify the manual event's ref. This is a workflow guard gap, not evidence that an invalid package has actually been published. Verified inmain@c61f601ee9cc3a9cba6abbedd6bef2382318eada.🔄 Steps to Reproduce
.github/workflows/publish-pi.yml: it declares bothpush.tags: ["pi-v*"]andworkflow_dispatch.Verify Pi release versionstep:if: github.event_name == 'push'gatesnode test/release-contract.mjs "$RELEASE_TAG".Publish to npm with provenancestep: it has no event guard and runsnpm publishafter an unconditionalnpm test.workflow_dispatchevent, evaluate that predicate: the release-contract step is skipped while the publish step remains eligible. No workflow was manually dispatched and no package was published to reproduce this control-flow gap.✅ Expected Behavior
Every publication path must validate a resolved immutable release identity and the package version/installer pin before invoking
npm publish; if a manual run has no valid matching tag, it fails closed. Keep the existing Pi test and OIDC/provenance gates. Add a deterministic negative fixture or workflow check proving that a manual invocation without a matching tag cannot reach publish, without publishing a package in tests.❌ Actual Behavior
workflow_dispatchskips the release-contract step that receivesgithub.ref_namebecause it is push-only, whilenpm testandnpm publishremain unconditional. The tests exercise contract behavior with constructed tags and check publish-step ordering, but do not bind the manual event ref to the package or verify dispatch guard parity. Actual npm registry acceptance of a specific manual run was not tested and is not claimed.Operating System
Windows
Engram Version
2.2.1 (installed CLI); workflow verified on
main@c61f601Agent / Client
Other
📋 Relevant Logs
💡 Additional Context
plugin/pi/test/release-contract.mjsalready checks package version, release tag, installer pin, and CLI guidance when called.plugin/pi/test/package-contract.test.mjsinvokes it with a constructedpi-v<package version>and checks tag pass-through, but notworkflow_dispatch's actual event ref;publish-workflow.test.mjschecks unconditionalnpm testimmediately before publish. Issue #1349 tracks a larger coordinated Engram/Pi release train; this report is the narrower, present-day manual-path preflight gap and should be linked to, not substituted for, that design. Closed #1048 added candidate-local Pi tests before publish; those tests do not validate release identity.