Repository navigation
fix(npmprofile): Support pnpm settings-only root packages - #80
Conversation
Treat an omitted packages member as a terminal root-only package boundary while preserving fail-closed subdirectory selection. Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
Signed-off-by: Yunseo Kim <git@yunseo.kim>
| @@ -0,0 +1,3 @@ | |||
| packages: packages/* | |||
| minimumReleaseAge: 1440 | |||
There was a problem hiding this comment.
This pnpm workspace configuration does not set a minimum release age. Newly published packages can be malicious or unstable. Add minimumReleaseAge: 10080 (minutes) to wait at least seven days before installing newly published package versions. Added in: v10.16.0 Reference: https://pnpm.io/settings#minimumreleaseage
🌟 Removed in commit 8d4245e 🌟
| @@ -0,0 +1,6 @@ | |||
| minimumReleaseAge: 1440 | |||
There was a problem hiding this comment.
This pnpm workspace configuration does not set a minimum release age. Newly published packages can be malicious or unstable. Add minimumReleaseAge: 10080 (minutes) to wait at least seven days before installing newly published package versions. Added in: v10.16.0 Reference: https://pnpm.io/settings#minimumreleaseage
✨ Removed in commit 8d4245e ✨
| @@ -0,0 +1,6 @@ | |||
| minimumReleaseAge: 1440 | |||
There was a problem hiding this comment.
This pnpm workspace configuration does not set a minimum release age. Newly published packages can be malicious or unstable. Add minimumReleaseAge: 10080 (minutes) to wait at least seven days before installing newly published package versions. Added in: v10.16.0 Reference: https://pnpm.io/settings#minimumreleaseage
🥳 Removed in commit 8d4245e 🥳
| @@ -0,0 +1,3 @@ | |||
| packages: packages/* | |||
There was a problem hiding this comment.
Missing or incorrect blockExoticSubdeps. Set blockExoticSubdeps: true to transitive dependencies from being installed from untrusted sources. Added in: v10.26.0 Reference: https://pnpm.io/settings#blockexoticsubdeps
🎈 Removed in commit 8d4245e 🎈
Signed-off-by: Yunseo Kim <git@yunseo.kim>
…elease retries (#86) ## Summary Implements the optional, tags-only `source-ref` input for the npm producer workflow (issue #81, ADRs 0079/0080). A caller can now retry a failed release with a fixed pipeline: dispatch from a ref that carries the fixed workflow (e.g. `main`) while the built, attested, and published content remains the signed release tag. - What changed? - New optional `workflow_call` input `source-ref` (full `refs/tags/<name>` only) on `js-ts-npm-package-slsa3.yml`. - Pre-checkout resolution: the new internal `npm-profile-source-ref` subcommand validates, resolves, and peels the tag, and the package checkout is pinned to the resolved commit SHA (fail-closed on missing/unresolvable/non-commit refs; no tag mutation ever). - The signed SLSA v1 predicate records the built source identity and, when `source-ref` is supplied, additionally records the invocation ref/SHA as signed invocation context (`source.input_ref`, `source.invocation_ref`, `source.invocation_revision`, all-present-or-absent, explicit `null` rejected). - ADR 0080 verification binding: explicit policy binds the signed built-source fields; certificate source claims bind the signed invocation record, with exact single-identity fallback when the input is omitted. - Signing consumes the built release identity through the digest-verified handoff (build job outputs), while the certificate context remains bound to the invocation `GITHUB_REF`/`GITHUB_SHA`. - Release concurrency groups canonicalized to the full-ref form `${{ inputs.source-ref || github.ref }}` across build/sign/publish and enforced by workflowcheck; the architecture specs were updated accordingly (a tag name cannot be derived from a full ref in GitHub expressions). - Exact-spelling rule: omitted means exactly the empty string; whitespace-only or padded spellings are rejected with `windlass.verify.error.source-ref-invalid` because the raw input participates in job-level concurrency group computation and cannot be normalized there. - Why is this needed? - Today the build job always checks out the caller's invocation ref, so a `workflow_dispatch` retry either cannot run with a fixed pipeline or would weaken the provenance claim to an unsigned movable branch head (real trigger: vers-js v0.1.2 run failure fixed in #80). - How to test: `go test ./...`, `go test -race ./...`, `go run ./cmd/slsa-builder-internal fixture-check --index testdata/fixtures/index.json --surface npm --phase provenance-inputs`, `go run ./cmd/slsa-builder-internal workflow-check --workflow .github/workflows/js-ts-npm-package-slsa3.yml --profile npm-only`, `actionlint .github/workflows/js-ts-npm-package-slsa3.yml`. ## Related Issues - Related #81 (kept open until the post-merge live dogfood retry confirms the path end-to-end) - Related #80 ## Change Type - [x] Feature ## Changelog - Category: Added - User-facing note: Adds the optional tags-only `source-ref` input to the npm producer workflow for fixed-pipeline release retries, with built-source provenance, signed invocation context, and ADR 0080 verification binding. Changelog update: - [x] `CHANGELOG.md` `[Unreleased]` updated ## Checklist ### General - [x] PR title follows Conventional Commits format: `type(scope): Summary` - [x] This PR does not expose backend/internal implementation details in a public repo. - [x] No secrets, tokens, keys, or private endpoints are included. - [x] Changes stay within this repository's intended scope. ### CI/Workflow Changes (if applicable) - [x] All `uses:` references are pinned to full 40-character commit SHAs (with `# vX.Y.Z` comment) — no new or changed action references - [x] `step-security/harden-runner` is included as the first step in every job — unchanged - [x] Job-level `permissions` are used instead of top-level `permissions` — unchanged ### Protocol / Compatibility Impact - [ ] No protocol/spec impact - [x] Protocol/spec updated - [x] Conformance tests updated - [ ] Breaking change is versioned and migration notes are included Compatibility impact: additive and backward compatible. The input is optional; when omitted, build metadata, predicate bytes, and verification outcomes are byte-compatible with the pre-existing contract (golden-tested). No accepted ADR body was edited; architecture specs were updated in this PR (concurrency-key canonical form and the exact-spelling rule). ## Testing - [x] Unit tests added/updated - [x] Integration or conformance tests added/updated - [x] Tests pass - [x] Lint and format pass - [x] Manual verification performed Test evidence: - Every behavioral commit was developed test-first: new failing tests were captured in a detached worktree from `main` before each implementation landed, and each commit is independently green. - `go test ./...` and `go test -race ./...` pass (all packages); `golangci-lint run ./...` reports 0 issues; `actionlint` is clean. - Fixture conformance: 2 accepted + 9 rejected fixtures registered under the `provenance-inputs` phase (`fixture-check` passes; rejected fixtures carry their canonical primary diagnostics). - Workflow static conformance: `workflow-check` passes for `--job build`, `--job provenance-sign`, `--job publish`, and `--profile npm-only`, including the nine-input contract, the canonical concurrency groups, the resolver-before-checkout ordering, and the SHA-pinned package checkout. - Manual CLI verification covered: lightweight/annotated tag resolution against local git remotes, missing/non-commit tag rejection, whitespace-only/padded rejection, exact-empty passthrough without remote access, exit-code split (policy failure 1 vs invocation failure 2), and dispatcher-level sign mismatch classification. - A five-lane independent review (goal/constraints, hands-on QA, code quality, security, context mining) iterated to an unconditional pass; its findings (sign-job identity wiring, input normalization, dispatcher error classification, concurrency-spelling gap) are fixed in dedicated commits on this branch. ## Documentation - [ ] README updated (not needed: both READMEs already document `source-ref` and the 1-required + 8-optional input contract) - [x] Spec/docs updated - [x] Changelog decision completed above ## Rollout / Risk - Risk level: Medium (touches the release build/sign/publish path; every new behavior is gated behind the optional input and defaults to byte-compatible existing behavior) - Rollback plan: revert this branch; callers that never set `source-ref` are unaffected. The live vers-js dogfood retry (issue #81 acceptance) runs only after merge and is recorded separately. ## Reviewer Checklist - [x] Scope is clear and minimal - [x] Security and boundary checks passed - [x] Tests and docs are sufficient - [x] Compatibility impact is correctly handled --------- Signed-off-by: Yunseo Kim <git@yunseo.kim>
## Summary - Re-pins the slsa-builder reusable workflow in `.github/workflows/publish.yml` from `4b3edf3` to `f7a164409a13906effabcab4141f6a0a6ede7374` (`# main, pre-release`). - Why: the first v0.1.2 dogfood run ([run 31622651874](https://github.com/windlasstech/vers-js/actions/runs/31622651874)) failed in slsa-builder's npm-profile build with `package-resolution-invalid` (`npm_profile.selection`): this repository's settings-only `pnpm-workspace.yaml` (no `packages` field) was not treated as a standalone root package. slsa-builder fixed the selection logic in windlasstech/slsa-builder#80. - How to test: retry the v0.1.2 publication after merge; npm has no `0.1.2` (registry `latest` is still `0.1.1`), so the publish convergence machine starts from the absent state. - Second commit wires slsa-builder #86's new tags-only `source-ref` input (pin now `496e40e5ea983cd6933de0dec6b2658c6ccb4db6`): `workflow_dispatch` passes `refs/tags/<release_tag>` so the build resolves, peels, and checks out the signed tag commit regardless of the dispatch ref, and the signed predicate binds to the built tag identity while recording the dispatch context as signed invocation members. Tag pushes pass the exact empty string (byte-compatible with the previous contract). The dispatch guard relaxes from "must originate at the tag ref" to a `vX.Y.Z` shape check; tag existence/version agreement fail closed downstream (caller checkout, `source-ref-invalid`, tag/version/CHANGELOG agreement). - Third commit updates `docs/release.md`: the dispatch IMPORTANT block now documents the source-ref mechanics (dispatch from any ref; build and provenance bind to the signed tag commit; dispatch context recorded as signed invocation members), the publish job input list covers `source-ref` empty-on-push behavior, the provenance model notes the invocation-member claims, and failure recovery gains the pipeline-defect rerun path (fix on main, dispatch with `release_tag`, no tag recreation). ## Related Issues - Related windlasstech/slsa-builder#30 (P06 live M1 dogfood — this unblocks the retry) - Related windlasstech/slsa-builder#80 (the consumed fix) ## Change Type - [ ] Bug fix - [ ] Feature - [ ] Refactor - [ ] Documentation - [x] Test/CI - [ ] Breaking change - [ ] Other: <!-- describe --> ## Changelog - Category: None - User-facing note: CI-only builder pin bump; the 0.1.2 changelog already records the pipeline change, and no package content changes. Changelog update: - [ ] `CHANGELOG.md` `[Unreleased]` updated - [x] Not needed because this change is not user-facing ## Checklist ### General - [x] PR title follows [Conventional Commits](https://www.conventionalcommits.org/) format: `type(scope): Summary` - [x] This PR does not expose backend/internal implementation details in a public repo. - [x] No secrets, tokens, keys, or private endpoints are included. - [x] Changes stay within this repository's intended scope. ### CI/Workflow Changes (if applicable) - [x] All `uses:` references are pinned to full 40-character commit SHAs (with `# vX.Y.Z` comment) — the slsa-builder reference carries the documented `# main, pre-release` comment convention until slsa-builder cuts releases. - [x] `step-security/harden-runner` is included as the first step in every job — unchanged; this PR touches only the pin line. - [x] Job-level `permissions` are used instead of top-level `permissions` — unchanged (top-level `permissions: {}` with job-level elevation). ### Protocol / Compatibility Impact - [x] No protocol/spec impact ## Testing - [ ] Unit tests added/updated - [ ] Integration or conformance tests added/updated - [ ] Tests pass - [x] Lint and format pass - [ ] Type check passes - [x] Manual verification performed Describe test evidence: one-line pin change; `actionlint` exit 0 on `publish.yml`; `oxfmt` clean. The consumed fix was verified against the failure signature: the failed run's build job emitted `{"category":"package-resolution-invalid","check":"npm_profile.selection",...}`, which is exactly the case windlasstech/slsa-builder#80 handles (settings-only `pnpm-workspace.yaml` as terminal standalone-root package). End-to-end validation happens on the v0.1.2 retry run. ## Documentation - [ ] README updated - [ ] Spec/docs updated - [x] Changelog decision completed above ## Rollout / Risk - Risk level: Low - Rollback plan: revert this commit to restore the previous pin; the retry is gated on this merge either way. ## Reviewer Checklist - [x] Scope is clear and minimal - [x] Security and boundary checks passed - [x] Tests and docs are sufficient - [x] Compatibility impact is correctly handled --------- Signed-off-by: Yunseo Kim <git@yunseo.kim>
Summary
pnpm-workspace.yamlobject withoutpackagesas a terminal standalone-root package boundary with zero workspace patterns.packagesvalues and unsupported patterns fail-closed.vers-jsdogfood run 31622651874 exposed that valid modern pnpm policy settings were incorrectly classified as malformed workspace metadata.Related Issues
Issue #22 remains open for the orchestrator's post-merge dogfood retry.
Change Type
Changelog
pnpm-workspace.yamlfiles now resolve successfully without weakening malformed-workspace rejection.Changelog update:
CHANGELOG.md[Unreleased]updatedChecklist
General
type(scope): SummaryCI/Workflow Changes (if applicable)
Not applicable; no workflow files changed.
uses:references are pinned to full 40-character commit SHAs (with# vX.Y.Zcomment)step-security/harden-runneris included as the first step in every jobpermissionsare used instead of top-levelpermissionsProtocol / Compatibility Impact
If impacted, describe compatibility impact:
ADR 0078 and both architecture specs were already synchronized by #79. This implementation admits only the documented root-only pnpm shape; existing npm/Yarn workspaces and pnpm arrays remain unchanged.
Testing
Describe test evidence:
go test ./internal/npmprofile -run 'TestWorkspaceDiscovery|TestLockfileRules' -count=1failed becausepnpm settings-only rootemittedwindlass.verify.error.package-resolution-invalid.go test ./internal/npmprofile -count=1passed.go run ./cmd/slsa-builder-internal fixture-check --index testdata/fixtures/index.json --surface npm --phase build-packreturned pass/exit 0.go test -race ./...,go vet ./...,golangci-lint run ./...(0 issues), andgolangci-lint fmt --diff(clean) passed.Fixture matrix:
pnpm-settings-only-root-validpackagespnpm-workspace-packages-wrong-typepackageswindlass.verify.error.package-resolution-invalidpnpm-settings-only-subdirectorywindlass.verify.error.package-resolution-invalidSecurity review: no new path or pattern input is accepted. The relaxed branch is reachable only after strict YAML object parsing and only when
packagesis absent; it accepts exact candidate-root equality and rejects subdirectories immediately, so farther ancestor discovery and standalone subpackage fallback cannot weaken one-package or ambiguity checks.Documentation
Specs were already updated by #79; no ADR/spec files changed here.
Rollout / Risk
Reviewer Checklist