Repository navigation
Open
Labels
dogfoodLive external proof against npmjs/GitHubLive external proof against npmjs/GitHubimplementationImplementation task from .omo/plans/implementation-plan.mdImplementation task from .omo/plans/implementation-plan.mdm1Part of the M1 npm-publish vertical slicePart of the M1 npm-publish vertical slice
Description
Scope
Execute real M1 publication from the selected caller repository and record actual evidence in docs/dogfood/npm-m1.md.
Contracts
Full npm profile; ADR confirmations 0073 and 0076
Dependencies
F00, P05
Blocking issues: [F00] #7; [P05] #29.
Executable QA
`gh run list --repo "$DOGFOOD_REPO" --workflow release.yml --branch "$DOGFOOD_TAG" --limit 1 --json databaseId,conclusion,headSha` must return one run with `conclusion:"success"`. `gh run watch "$RUN_ID" --repo "$DOGFOOD_REPO" --exit-status` must exit 0. `npm view "$DOGFOOD_PACKAGE@$DOGFOOD_VERSION" name version dist --json` must return exact name/version, nonempty `dist.integrity`, `dist.shasum`, and attestation metadata. `go run ./cmd/slsa-builder-internal verify-attestation --npm-package "$DOGFOOD_PACKAGE" --version "$DOGFOOD_VERSION" --expected-run-id "$RUN_ID" --mode online` must exit 0 with `result:"pass"` and matching `run_invocation`. A same-run retry must conclude success without a second publish; a new run for the same version must fail with `foreign-conflict`.
Metadata
Metadata
Assignees
Labels
dogfoodLive external proof against npmjs/GitHubLive external proof against npmjs/GitHubimplementationImplementation task from .omo/plans/implementation-plan.mdImplementation task from .omo/plans/implementation-plan.mdm1Part of the M1 npm-publish vertical slicePart of the M1 npm-publish vertical slice
Activity
ci(publish): Replace SLSA generator with token-based npm bootstrap (#34)
ci: publish releases via the slsa-builder npm SLSA3 reusable workflow (…
chore(release): v0.1.2 (#41)
yunseo-kim commentedon Aug 12, 2026
Dogfood attempt 1 (12026-08-12): FAIL-CLOSED as designed — no mutation occurred.
Run: https://github.com/windlasstech/vers-js/actions/runs/31622651874 (v0.1.2 tag push)
Outcome: verify job success → build job failed at 'Build and pack selected package' → provenance-sign skipped → publish skipped. Zero registry mutations.
Failure diagnostic:
windlass.verify.error.package-resolution-invalid— 'The package directory does not resolve to exactly one package inside the repository.'Root cause (spec gap identified): vers-js carries a settings-only
pnpm-workspace.yaml(policy settings: minimumReleaseAge, trustPolicy, etc.) with NOpackages:member. The current build-pack spec (js-ts-npm-build-pack.md, malformed-metadata clause) treats a pnpm-workspace.yaml whosepackagesmember is missing as malformed workspace metadata and fails closed. In the real pnpm ecosystem, settings-only pnpm-workspace.yaml is a valid and common shape (modern pnpm stores policy settings there); the N01 fixture corpus had no such fixture, so the gap passed CI.Assessment: slsa-builder-side spec/implementation gap, NOT a caller readiness issue (F00 verified GO separately). Resolution: new ADR to treat a missing
packagesmember as standalone-root-package mode (zero patterns, root package only) rather than malformed metadata, plus N02 fix, new fixture, and spec sync. Work starts immediately.Retry plan: after the ADR + fix merge, re-run via workflow_dispatch release_tag=v0.1.2 (idempotency: no version conflict occurred since nothing was published).
yunseo-kim commentedon Aug 12, 2026
ADR 0078 is drafted in #79: #79
It records the dogfood finding from https://github.com/windlasstech/vers-js/actions/runs/31622651874 and specifies that a settings-only
pnpm-workspace.yamlwith nopackagesmember selects the standalone root package rather than failing as malformed. The N02 implementation, accepted/rejected fixtures, andrelease_tag=v0.1.2dogfood retry remain the follow-up after this docs-only PR merges.docs(decisions): Treat settings-only pnpm workspace as root package (#79
ci: bump slsa-builder pin and wire source-ref for release retries (#42)
yunseo-kim commentedon Aug 13, 2026
P06 verification-procedure decision (12026-08-14): the executable-QA verification step is performed with the existing reference procedure from
docs/architecture/verification-policy-and-fixtures.md(§ Reference commands), not a new online mode of the internalverify-attestationsubcommand. The QA string in this issue maps to that procedure as follows:gh run list ... conclusion:"success"/gh run watch --exit-statusnpm view "$DOGFOOD_PACKAGE@$DOGFOOD_VERSION" name version dist --jsondist.integrity,dist.shasum, attestation metadata.verify-attestation --npm-package ... --expected-run-id "$RUN_ID" --mode online(not an implemented/spec'd command surface)verification-mode-invalidon ambiguity); (2) strict-parse policy/expectation/bundle with duplicate-member rejection; (3) acquire the online TUF trust root; (4)gh attestation verify "$ARTIFACT" --bundle "$BUNDLE" --repo windlasstech/vers-js --signer-workflow <SHA-pinned reusable workflow ref> --cert-oidc-issuer https://token.actions.githubusercontent.com --format jsonwith all constraints supplied; (5) JSON/X.509 post-processing for the claimsghcannot check (Fulcio numeric repository/owner IDs, runner environment, source ref/revision, Run Invocation URI).gh attestation verifysuccess is necessary but not sufficient. The--expected-run-idelement maps to comparing the signed Statement'srunDetails.metadata.invocationIdwithhttps://github.com/windlasstech/vers-js/actions/runs/<RUN_ID>.foreign-conflictAdditional evidence to record during the run (unchanged): ADR 0076 early npm OIDC exchange preflight outcome; ADR 0073 attestation propagation measurements (publish completion timestamp, every attestation observation timestamp, first authenticated visibility, total convergence time, request count) — one observation does not tighten the bound; issue #81 acceptance (Statement source revision = the tag's commit SHA while
run_invocation.refremainsrefs/heads/main); issue #22 acceptance (settings-only pnpm root-package resolution passes). All actual values are recorded indocs/dogfood/npm-m1.mdafter the run.docs(dogfood): Add npm M1 verification procedure (#87)
yunseo-kim commentedon Aug 14, 2026
Second dogfood attempt (12026-08-13/14): blocked pre-mutation, root cause identified, remediation in progress
Run: 31737001312 —
workflow_dispatchfrommain(7851c5f8) withrelease_tag=v0.1.2, reusable workflow pinned at496e40e5(includes the #86 source-ref fix). Conclusion:failure. No publication occurred —@windlass/[email protected]remains absent from the registry (E404) and the attestations endpoint returns 404.Timeline and failure point
518569c6→ peel68e6f001, caller pin verified)Build npm package✅ — settings-only pnpm resolution passed (closes #22), tag content built, handoffs producedSign npm provenance❌ — step "Sign and verify exact npm provenance Statement" failedPublish npm packagereported success but every mutation step was skipped (bundle handoff absent) — the no-mutation-on-failure property held end to endWhat failed and why
npm-profile-signran the ADR 0076 early npm OIDC exchange preflight. The registry returned HTTP 201 — the exchange succeeded (trusted-publisher configuration valid, publish token minted) — but the client's strict response decoder rejected the body, failing closed withwindlass.verify.error.npm-oidc-exchange-indeterminate("malformed response"). No token fallback was attempted; no mutation followed.This is the ADR 0076 confirmation event (standing item): the early-exchange preflight was empirically exercised on the live registry, classified an unreadable-by-contract surface, and blocked the mutation as designed.
Root cause (empirically pinned)
The expected response shape (
created/expiresas RFC 3339 strings) existed only in the P03 implementation and its fixtures — neither the spec nor ADR 0076 pins the exchange success body. A mutation-free probe (temporarypublish.ymlon a throwaway vers-js branch, run 31794210985, branch deleted afterwards) captured the real 201 body:{ "created": 1786705013, "expires": 1786705913, "token": "<375-char string>", "token_type": "oidc" }created/expiresare epoch-second numbers, not the RFC 3339 strings shown in the npm API docs example. Docs/implementation drift on npm's side; our strictness caught it.tokenand ignore everything else, which is why the drift is invisible to the ecosystem.Resolution path (approved)
js-ts-npm-provenance-publish.md(exchange response contract section), then a TDD decoder fix with the observed-shape fixture.docs/dogfood/npm-m1-verification-procedure.md) phases 1–6.Partial acceptances recorded in this attempt
refs/tags/v0.1.2/68e6f001to the signer with invocation context onmain; live Statement field observation rolls into the next run's acceptance here).docs: close satisfied ADR 0076 early-exchange standing item
22 remaining items
ci: Bump slsa-builder pin for Corepack distribution capture fix (#74)
yunseo-kim commentedon Nov 2, 2026
Seventh dogfood attempt (12026-11-02): fail-closed at publish read-back — Corepack keying fix verified live, the publish mutation never committed
Run: 41427318706 —
workflow_dispatchfrommain(02dfb25b) withrelease_tag=v0.1.3(tag objectede69038→ peelae5bb155, unchanged), callerpublish.ymlblobd566674f, reusable workflow pinned at643077b(carries the #140 Corepack plain-version keying fix). Conclusion:failure. No publication occurred — post-run the packument returnsversion not found: 0.1.3and the attestations endpoint returns HTTP 404.Timeline and failure point
ede69038→ peelae5bb155, caller blobd566674f, pin643077bverified at caller line 144; dispatch acceptedVerify release✅Build npm package✅ — Corepack acquisition capture passed with the integrity-suffixed descriptor[email protected]+sha512.f6988a32..., the exact point that killed attempt 6Sign npm provenance✅Publish npm package❌ — hardening, trusted-builder checkout, Node 24.22.1 provision/bind/assert, npm 11.21.1 provisioning, config diagnostics, and both digest-bound handoff downloads all ✅;Publish with trusted-publisher convergence❌ at 10:34:50 withwindlass.verify.error.registry-linkage-mismatch(npm.publish.readback, "publication outcome is indeterminate", exit 1)What passed that previously failed
The #140 plain-version keying fix passed live at exactly the attempt-6 failure point: the Installing-line and cache-path capture now keys on the plain version while
.corepacklocator.referenceretains the full descriptor, and the build proceeded through install/build/pack to hand off a verified tarball and a signed provenance bundle. The publish mutation stage was reached for the first time since attempt 4.What is confirmed about the failure
npm.publish.readback) confirms the entry revalidation and the npm invocation itself were reached and cleared.0.1.3never appeared in the packument during the entire budget and remains absent now; the attestation surface is likewise empty. The mutation never committed; zero registry mutation occurred, and the fail-closed property held again.ENEEDAUTH/E404/E401/E403map to definitive pre-commit rejections; any other npm failure class falls through to read-back polling, consistent with the observed budget exhaustion. Pinning the actual npm failure class is the next diagnostic step.yunseo-kim commentedon Nov 2, 2026
Attempt 7 follow-up: bundle verification passes; registry predicate rejection is the leading hypothesis, not a confirmed root cause
This follow-up records the additional evidence gathered after the previous attempt-7 report and narrows one statement in that report. It does not claim that the original failure has been reproduced or definitively classified.
1. The real artifacts pass npm 11.21.1 client-side provenance verification
The attempt-7 tarball and Sigstore bundle were downloaded and checked with the pinned npm 11.21.1 / Node.js 24.22.1 toolchain. The package binding was also checked independently: the expected subject is
pkg:npm/%40windlass/[email protected], and the subject's SHA-512 digest matches the tarball.Deliberately corrupted controls failed for the intended reasons:
This makes the following causes unlikely for attempt 7:
sigstore.verify()rejection;--provenance-filebundle npm/cli#9879 provenance-file problem, whose fix shipped in npm 11.20.0 and is present in 11.21.1.This is client-side evidence only. It does not prove that the registry accepted the same bundle or predicate.
2. The statement overlaps the registry's documented workflow/GitHub consistency checks
The attempt-7 statement has this shape:
externalParameters.workflow.refexternalParameters.workflow.repositoryexternalParameters.workflow.pathinternalParameters.githubThe certificate instead carries GitHub's invocation/source claims. In particular, the source identity is the dispatch invocation on
mainat02dfb25b, not the built tag. The built sourcev0.1.3/ae5bb155is recorded separately through the signed source binding, as designed in ADR 0080. Thereforerefs/tags/v0.1.3must not be substituted intoexternalParameters.workflow.refmerely to fill the missing field.slsa-framework/slsa-github-generator#2499 provides a directly relevant registry rejection case. npm returned
422 Unprocessable Entitywhile checking values including:externalParameters.workflow.refagainst the certificate source-repository ref;externalParameters.workflow.repositoryagainst the certificate source-repository URI;externalParameters.workflow.pathagainst the build-config URI;internalParameters.github.event_name, repository ID, and owner ID against GitHub claims.A later report in the same issue says the custom
buildType/builder.idrejection was relaxed while workflow andinternalParametersmismatch errors persisted.Current leading hypothesis: the registry rejected the Windlass SLSA v1 predicate because its workflow/GitHub fields were absent or inconsistent with the certificate, plausibly with
E422. This is more specific than attributing the failure to the custombuildTypealone, but it remains a hypothesis.3. What remains possible
The evidence does not currently distinguish among all of these execution paths:
buildTypealone4. Correction to the previous comment
The previous comment said “zero registry mutation occurred.” That was too strong.
The current packument and attestation 404 responses establish that no
0.1.3publication or attestation is visible now. They do not, by themselves, prove whether a registry PUT occurred, whether npm returnedE422, or how every historical request was processed. The accurate statement is: no publication is currently visible, and the original mutation outcome is indeterminate from the preserved evidence.5. Evidence still missing from the original run
The following cannot be reconstructed reliably from the retained workflow log:
E422;Further guessing will not resolve those unknowns. Before another publish retry, the trusted-core diagnostics need to preserve a bounded, secret-safe execution timeline: stage transitions, process start/exit, normalized npm error code, HTTP status and request phase, allowlisted predicate mismatch field paths, and bounded read-back observations. Raw environment values, JWTs, token-exchange responses, and unfiltered npm debug logs must remain excluded.
feat(npmprofile): Add redacted operational diagnostics (#141)
ci: Bump slsa-builder pin for redacted publish diagnostics (#75)
yunseo-kim commentedon Nov 2, 2026
Eighth dogfood attempt (12026-11-02): fail-closed at publish — npm E422 captured, registry predicate compatibility gap is the leading cause
Run: 41488152839 —
workflow_dispatchfrommain(3687faf7) withrelease_tag=v0.1.3(tag objectede69038→ peelae5bb155), callerpublish.ymlblob40fa08ef, reusable workflow pinned at03357579(carries PR #141 redacted operational diagnostics). Conclusion:failure. No publication became visible —@windlass/[email protected]remains absent from the packument (E404), and the attestation endpoint returns HTTP 404. No blind retry was attempted.Timeline and failure point
ede69038→ peelae5bb155, caller blob40fa08ef, pin03357579verifiedVerify release✅Build npm package✅ — pinned Node.js 24.22.1, toolchain pair assertion, Corepack acquisition capture, build, pack, and handoffs all passedSign npm provenance✅ — Windlass-signed SLSA v1 bundle produced and verifiedPublish npm package❌ — pinned npm 11.21.1 provisioning and both digest-bound handoffs passed;Publish with trusted-publisher convergencefailed after read-back exhaustionE422/ HTTP 422indeterminateWhat the new diagnostics established
PR #141's redacted operational diagnostics captured the evidence that attempt 7 lacked:
The preserved outcome report is
windlass.verify.error.registry-linkage-mismatch(npm.publish.readback, "publication outcome is indeterminate", exit 1). The registry state therefore remains fail-closed and non-committed from the caller's perspective.Artifact and signer verification
The preserved attempt-8 tarball and Windlass-signed bundle passed online GitHub attestation verification with the exact tarball, caller repository, issuer, SLSA v1 predicate type, and SHA-pinned signer workflow. This separates npm registry admission from bundle signature validity.
e3de6be684ff97651c09361d67680fddf1d5fe96d673433a88e2ddd3c03fa898e89f2c710fb587775984e7365bd80018df6d9b00a78b35c0838fe088b09f08747c6b04e568df06f48820b60c4ffb98d4897dc91a75ad741eabe89919dc5955af38cde7fbfb4e036991a8905b2af386fd298d604b6e8284d1cc2206d14d93da02The signing certificate carries the public GitHub claims npm can compare against: source repository
https://github.com/windlasstech/vers-js, source refrefs/heads/main, build confighttps://github.com/windlasstech/vers-js/.github/workflows/publish.yml@refs/heads/main, triggerworkflow_dispatch, repository ID1261293345, owner ID257660296, and runner environmentgithub-hosted.Root-cause assessment
The exact registry response body was not retained, so the final server-side message remains unobserved. The evidence nevertheless narrows the leading cause to npm's certificate-to-predicate consistency validation for GitHub workflow fields.
Comparing the registry-accepted npm-generated provenance for
@windlass/[email protected]with the rejected attempt-8 Windlass bundle shows:buildDefinition.externalParameters.workflow.refrefs/heads/mainbuildDefinition.externalParameters.workflow.repositoryhttps://github.com/windlasstech/vers-jsbuildDefinition.externalParameters.workflow.path.github/workflows/publish.yml.github/workflows/js-ts-npm-package-slsa3.ymlbuildDefinition.internalParameters.github{}sha512sha256andsha512buildDefinition.buildTyperunDetails.builder.idhttps://github.com/actions/runner/github-hostedThe leading hypothesis is that npm rejected the custom SLSA v1 predicate because the workflow/GitHub projection was absent or inconsistent with the certificate. Upstream evidence in slsa-github-generator#2499 records npm returning E422 for this field family. The extra SHA-256 digest and the custom Windlass
buildType/ SHA-pinnedbuilder.idremain lower-confidence hypotheses; npm 11.21.1's client-side custom-provenance path checksdigest.sha512and does not reject additional digest algorithms, and the upstream thread records that npm relaxed its earlier builder/build-type checks.Follow-up issues and next step
The next implementation step should be the normal observe → ADR → spec → TDD path for #143, while preserving the immutable Windlass builder identity and the signed distinction between the built source tag and the dispatch invocation ref. No further publish retry should occur until that contract change is decided and implemented, or until #142 provides a more precise rejection classification.
docs(dogfood): Record npm M1 attempts 7 and 8 (#144)
feat(npmprofile): Relay filtered external-process stderr with rejecti…
fix(npmprofile): Shorten the post-publish read-back budget to five mi…
ci: Bump slsa-builder pin for stderr relay and read-back budget (#76)
yunseo-kim commentedon Nov 3, 2026
Ninth dogfood attempt (12026-11-03): fail-closed at publish — full registry rejection captured via the ADR 0101 stderr relay
Run: 41666974638 —
workflow_dispatchfrommain(bd8b7ab1) withrelease_tag=v0.1.3(tag objectede69038→ peelae5bb155), callerpublish.ymlblob21f17485, reusable workflow pinned ate7c55d91(carries PR #145 stderr relay + family enrichment and PR #146 five-minute read-back budget). Conclusion:failure. No publication became visible — post-runnpm view @windlass/[email protected]returns E404 and the attestations endpoint returns 404; because the publish call was attempted, the outcome remainsindeterminateby design.What happened
Verify release, build, and provenance-sign jobs passed; the publish job passed pinned runtime/npm provisioning and both handoff verifications, then failed at
Publish with trusted-publisher convergencewithE422after 2.83 seconds. Post-mutation read-back made 21 observations over the new five-minute budget and returnedindeterminate(windlass.verify.error.registry-linkage-mismatch). Run wall time was ~9m46s, down from attempt 8's 20m25s.The registry's complete rejection message (first first-party capture)
The PR #145 relay carried npm CLI's rendered stderr into the log with no instrumentation change, satisfying ADR 0101's confirmation criterion:
Findings
<empty>confirms the fields are absent from our statement rather than mismatched.resolvedDependencies[0]must track the invocation source — URIgit+<SourceRepositoryURI>@<SourceRepositoryRef>with no lockfile fragment, anddigest.gitCommitequal to the invocation head commit (bd8b7ab1, not the peeled tag commit).workflow.pathmust equal the certificate BuildConfigURI's caller workflow path (.github/workflows/publish.yml), not the reusable workflow's own path;workflow.refis the invocation ref (refs/heads/main), not the built tag.buildTypeandbuilder.idare absent from the complaint — suggestive of the post-12023-08 relaxation, but not proof of acceptance until a conforming run reaches the registry.code=E422 http_status=422but nofamily=: the live form's tworesolvedDependencies[0]lines are outside its binding table, so the complete-template matcher declined. The relay carried the full text regardless — the ADR 0101 enrichment fallback working as designed. Template widening is tracked in [P06] Classify npm publish rejection details safely #142.registry-linkage-mismatch,indeterminate, exit 1.e3de6be6…, SHA-512e89f2c71…) are byte-identical to attempt 8's, reconfirming deterministic build reproduction.Next
resolvedDependencies[0]lines.resolvedDependencies[0]invocation-source requirement.docs/dogfood/npm-m1.md, attempt-9 section).docs(dogfood): Record npm M1 attempt 9 (#147)
feat(npmprofile): Classify E422 publish rejections under a reduced re…
feat(npm)!: Implement final npm provenance contract per ADR 0103-0112 (…
ci: Bump slsa-builder pin for npm provenance compatibility projection (…