Repository navigation
Description
Context
Attempts 7 and 8 of the live M1 dogfood reached npm publish and failed closed with windlass.verify.error.registry-linkage-mismatch / publication outcome is indeterminate.
- Attempt 7: https://github.com/windlasstech/vers-js/actions/runs/41427318706
- Attempt 8: https://github.com/windlasstech/vers-js/actions/runs/41488152839
- Diagnostics follow-up: [P06] Classify npm publish rejection details safely #142
Attempt 8 captured npm's immediate result as E422 / HTTP 422. The preserved Windlass bundle passed GitHub attestation verification, and the registry never exposed @windlass/[email protected] or its attestations. Comparing the rejected bundle with the registry-accepted npm-generated provenance for @windlass/[email protected] shows that the Windlass statement lacks npm's GitHub workflow compatibility projection:
buildDefinition.externalParameters.workflow.refbuildDefinition.externalParameters.workflow.repository- caller workflow
buildDefinition.externalParameters.workflow.path buildDefinition.internalParameters.github.event_namebuildDefinition.internalParameters.github.repository_idbuildDefinition.internalParameters.github.repository_owner_id
The signing certificate already carries corresponding public claims: source repository, source repository ref, caller build config URI, build trigger, repository ID, owner ID, and runner environment.
Proposal
Add a narrowly scoped npm compatibility projection to the signed SLSA v1 provenance statement so npm's registry-side certificate-to-predicate consistency checks can find the GitHub workflow fields they expect.
The projection should preserve the existing Windlass semantics:
- Keep the immutable Windlass
builder.idand build type. - Keep both SHA-256 and SHA-512 subject digests unless a later evidence-based decision rejects the extra digest.
- Keep the built source tag (
source.ref,source.revision) distinct from the invocation ref (source.invocation_ref,source.invocation_revision). - Populate npm-compatible workflow fields from verified public GitHub/OIDC claims, not arbitrary environment text.
- Treat the projection as npm registry compatibility metadata, not as a replacement for Windlass source or builder identity.
Expected direction
Candidate mapping:
workflow.ref<- certificateSourceRepositoryRef/ invocation refworkflow.repository<- certificateSourceRepositoryURIworkflow.path<- caller workflow path from certificateBuildConfigURIinternalParameters.github.event_name<- certificateBuildTriggerinternalParameters.github.repository_id<- certificateSourceRepositoryIdentifierinternalParameters.github.repository_owner_id<- certificateSourceRepositoryOwnerIdentifier
The final mapping, trust boundary, and provenance-contract wording require an ADR and specification update before implementation.
Non-goals
- Do not remove SHA-256 speculatively.
- Do not replace the Windlass SHA-pinned builder identity with npm's runner identity.
- Do not log raw registry responses; detailed rejection classification remains [P06] Classify npm publish rejection details safely #142.
- Do not retry publication before the projection contract is decided and implemented.
Acceptance criteria
- ADR and architecture specs define the projection and its trust semantics.
- TDD covers the six compatibility fields and their certificate/claim bindings.
- Existing immutable builder identity and source-tag versus invocation-ref semantics remain intact.
- Generated provenance passes existing offline/online verification and npm client custom-provenance checks.
- A later dogfood attempt can determine whether npm accepts the projection or reports a remaining rejection family.
Activity
docs(dogfood): Record npm M1 attempts 7 and 8 (#144)
feat(npmprofile): Relay filtered external-process stderr with rejecti…
docs(dogfood): Record npm M1 attempt 9 (#147)
yunseo-kim commentedon Nov 3, 2026
Attempt-9 evidence update (12026-11-03): the projection surface is now fully measured
Run 41666974638 captured the registry's complete rejection message via the ADR 0101 relay. The six workflow/GitHub fields in this issue's candidate mapping are confirmed verbatim, with exact expected values:
externalParameters.workflow.refrefs/heads/mainSourceRepositoryRef(invocation ref, not the built tag)externalParameters.workflow.repositoryhttps://github.com/windlasstech/vers-jsSourceRepositoryURIexternalParameters.workflow.path.github/workflows/publish.ymlBuildConfigURI(caller workflow path, not the reusable workflow's own path)internalParameters.github.event_nameworkflow_dispatchBuildTriggerinternalParameters.github.repository_id1261293345SourceRepositoryIdentifierinternalParameters.github.repository_owner_id257660296SourceRepositoryOwnerIdentifierTwo requirements surface that no prior evidence had shown:
buildDefinition.resolvedDependencies[0].urimust equalgit+<SourceRepositoryURI>@<SourceRepositoryRef>(attempt 9:git+https://github.com/windlasstech/vers-js@refs/heads/main) — the invocation source at the invocation ref, with no lockfile fragment.buildDefinition.resolvedDependencies[0].digest.gitCommitmust equal the certificateSourceRepositoryDigest, the invocation head commit (bd8b7ab1...), not the peeled tag commit.This interacts with the ADR 0079/0080 model: the provenance keeps the built tag in
externalParameters.source.*whileresolvedDependencies[0]now must present the invocation context. How that affects what the provenance claims about built content (and whether the built-source record moves to a laterresolvedDependenciesentry) belongs to the ADR/spec step before implementation.Two more observations:
<empty>in the registry message, confirming absence rather than mismatch.buildTypeandbuilder.idwere absent from the complaint despite our custom values — consistent with the post-12023-08 relaxation, but not conclusive until a conforming run reaches the registry.Full evidence record: PR #147 (
docs/dogfood/npm-m1.md, attempt-9 section). The recognizer-side template widening is tracked in #142.feat(npmprofile): Classify E422 publish rejections under a reduced re…
fix(attestation): Bind the Build Trigger certificate extension
fix(diagnostic): List valid diagnostic metadata members
fix(npm): Verify the signed record against the authenticated handoff
fix(npm): Reject source manifest drift before install and pack
fix(verification): Align level-four diagnostic precedence
fix(command): Classify npm publish input failures
fix(npm): Carry diagnostics through consumer statement validation
fix(provenance): Enforce positive decimal repository identifiers
fix(npm): Reject null build script dispositions
3 remaining items
refactor(npm): Unify build script declaration checks
fix(identity): Distinguish untrusted builder paths
fix(npm): Validate runner capture overrides
fix(command): Preserve consumer build config pins
fix(npm): Classify caller workflow parsing failures
test(npm): Cover producer evidence rejection paths
test(npm): Cover external interface rejection paths
test(fixture): Cover evidence decoding error paths
docs: Correct certificate-binding typo and complete ADR 0090 mapping
fix(npm): Enforce the canonical runner software URI spelling at capture
fix(npm): Report packed-tarball inspection failures under package-pac…
fix(npm): Reject whitespace-padded runner detail values
docs(changelog): Describe the npm contract as first-release content
feat(npm)!: Implement final npm provenance contract per ADR 0103-0112 (…
ci: Bump slsa-builder pin for npm provenance compatibility projection (…