The manifest was more stable than the branch

R7K1 stopped caching by branch and built an artifact identity from source, lockfile, recipe, base image, and declared configuration.

R7K1's build cache began with a branch name. If feature/search had built before, the next request looked for an image tagged feature-search.

That reused stale work after the branch moved and missed reuse when two branches pointed at identical source. Replacing the branch with a commit hash fixed only part of the problem. The same commit could build differently after a lockfile, base image, recipe, or build-time configuration changed.

The breakthrough was to identify the declared build, not the human workflow that requested it.

R7K1 created a normalized manifest, hashed it, and used that digest as the artifact request identity. The branch became provenance. The manifest became the answer to “which inputs are supposed to determine this output?”

The first manifest was a list of suspicions

I did not begin with a perfect reproducibility model. I wrote down every input that had changed output during a failed comparison:

source commit
dependency lockfile bytes
build recipe version
Dockerfile and build-context rules
base image digest
compiler and package-manager versions
declared non-secret build configuration
fixture client schema where bundled code depended on it

Some values were already inside the source tree. Recording them separately made their role visible and allowed a recipe change to invalidate reuse without editing application code.

The manifest explicitly excluded runtime data, preview hostname, environment credentials, and branch label. Those changed environment behavior without changing the application artifact.

This boundary was reviewed whenever a build unexpectedly differed. The manifest was an evolving model of causality, not a magic list assembled once.

Source identity needed tree content

A commit hash was a stable Git object identity and included history relationships beyond the checked-out tree. R7K1 used the resolved commit for provenance and a normalized source-tree digest for build input where appropriate.

Generated local files, ignored secrets, and untracked developer state were absent from controlled builds. Submodules or vendored inputs had to contribute their own resolved identities. The build context inventory showed exactly which files entered.

This prevented a worker's leftover file from affecting output without appearing in the manifest. Clean workspaces and explicit context were as important as hashing.

I did not let the build calculate identity after arbitrary scripts had mutated the source directory. Manifest resolution happened before untrusted build execution in an isolated workspace.

The branch event supplied a commit. The trusted controller resolved the controlled content that candidate meant.

The package manifest allowed version ranges. Two clean installs could resolve different dependency trees while application source remained fixed.

R7K1 required the repository's lockfile for cacheable candidate builds and included its content digest. The build used the package manager's locked installation mode available to the project rather than updating resolutions.

If a project lacked a trustworthy lockfile, R7K1 marked the build non-reusable or included a captured resolved-dependency receipt after one build. It did not pretend the package manifest alone determined output.

The package manager version also mattered. Different versions could interpret metadata or install layout differently. The recipe selected a version and recorded it.

This did not guarantee upstream package availability or integrity forever. It made dependency resolution an explicit input instead of host-local luck.

Cache safety improved because dependency identity stopped hiding behind “same commit.”

A base-image tag was another moving name

The Dockerfile said FROM node:... under a convenient tag. The tag could later refer to different image content. Two workers building the same source and lockfile might start from different operating-system packages.

The trusted resolver translated the declared base reference into an immutable registry identity before calculating the manifest. The manifest stored both readable reference and resolved digest.

declared base: project runtime tag
resolved base digest: sha256:...

Build policy could choose when to refresh a moving base tag. Refresh produced a different manifest and artifact even if application source stayed unchanged. Security rebuilds became visible input changes rather than mysterious cache misses.

If the registry could not resolve or later supply the exact content, the build failed. It did not substitute whatever the tag meant at retry time under the old manifest.

The branch lesson repeated at another layer: labels help people; digests identify content.

R7K1's build worker changed over time. New compiler flags, asset commands, environment defaults, or file-copy behavior could alter output without a repository change.

The build recipe received its own version or digest derived from trusted controller code and relevant configuration. A deploy of R7K1 that changed only queue behavior did not need to invalidate artifacts; a change to build semantics did.

I avoided one global platform version that destroyed the whole cache after every release. The recipe identity covered the portion capable of affecting output.

The manifest showed the recipe in diagnostics, making “built before the asset fix” queryable. A known-bad recipe could be denied for new environments while retaining old artifact evidence for investigation.

Build infrastructure was part of the compiler. Treating it as an input made platform upgrades accountable.

Configuration required an allow-list

Hashing the entire environment would include ephemeral values, worker paths, timestamps, and secrets. It would prevent reuse and risk leaking sensitive data into manifests.

Each project declared build-time configuration that was permitted to influence public output:

asset base path
feature profile intended for compilation
public API sandbox origin
locale set
build mode

The controller supplied and normalized those values. Undeclared environment access in the build was restricted where possible and detected through clean workers and tests.

Secret values were never embedded as cache keys or artifacts. If a build genuinely required a private dependency credential, that credential authorized retrieval but did not become source input; the retrieved dependency identity did.

The allow-list made configuration causality reviewable. “Environment variable changed something” became a contract violation or a manifest change.

Normalization came before hashing

Serializing a map with unstable property order or host-specific path separators could produce different hashes for equivalent inputs.

R7K1 defined a canonical manifest representation:

  • stable field ordering,
  • explicit schema version,
  • normalized text encoding,
  • paths relative to declared roots,
  • line-ending policy for values whose bytes were semantic,
  • arrays sorted only where order had no meaning,
  • missing values distinct from empty values.

The canonical bytes were inspectable and stored beside the digest. Hashing an opaque in-memory object would make disagreements difficult to diagnose.

Order was preserved for inputs where it mattered, such as compiler arguments. Normalization did not erase semantics merely to improve cache hits.

The digest was trustworthy only to the degree that every producer agreed on the same canonical meaning.

Manifest schema changes were explicit

Adding a field to the manifest could change its digest even if output did not. Omitting a newly relevant field could make reuse unsafe.

The manifest carried a schema version and recipe. R7K1 could read historical versions for evidence but did not compare digests across schemas as if they had identical meaning.

A migration that added purely descriptive provenance could preserve a separate build-key projection. A field affecting causality entered the build key and intentionally invalidated reuse.

I split the document conceptually:

identity inputs — canonical fields determining artifact request
provenance — branch, review, requester, timestamps
receipt — builder, result, artifact digest, verification

Only the first group formed the request digest. Provenance could have many values for one artifact. Receipt existed after work completed.

This stopped a new request timestamp from creating a new build and stopped descriptive metadata from pretending to be causal input.

Concurrent requests joined one attempt

Once the manifest digest existed, builds from different branches could request the same identity at nearly the same time.

R7K1 created one build-attempt lease per digest. The first worker became builder. Others subscribed to the durable attempt and reused its result. If the builder lease expired, a new worker reconciled registry and receipt state before restarting.

The attempt could end:

verified artifact available
failed with retryable infrastructure reason
failed with deterministic build reason
cancelled before execution because no request still needed it
outcome unknown, requiring artifact observation

A deterministic source failure was not cached as a successful artifact, but its log could answer identical requests until input changed or an explicit retry policy applied.

Deduplication reduced work without making one branch own the shared build.

Artifact identity and manifest identity differed

The manifest digest identified requested inputs. The resulting image or archive had its own content digest.

Ideally, one manifest produced the same artifact bits every time. In practice, timestamps, file ordering, generated IDs, and tool behavior introduced nondeterminism.

R7K1 stored both:

manifest digest M
artifact digest A
build receipt R

Reuse for M selected the one verified artifact A recorded by the winning receipt. A second build of M producing B was not silently treated as equivalent. It triggered a reproducibility diagnostic and policy decision.

This allowed safe caching before perfect reproducible builds. The system promised that one completed request identity mapped to a specific verified artifact, not that the universe could recreate identical bytes anywhere.

The distinction turned nondeterminism into observable evidence instead of a philosophical blocker.

Comparing artifacts from identical manifests exposed timestamps in generated banners, nondeterministic asset traversal, random temporary names embedded in source maps, and archive metadata.

I removed or normalized those inputs when they did not serve the product. Stable file ordering and explicit build time improved artifact comparison. Generated runtime IDs moved to environment provisioning.

Some tooling still produced differences that did not affect runtime behavior. R7K1 recorded them and avoided claiming complete bit reproducibility. Verification compared the actual stored artifact selected for reuse.

The exercise improved builds independently of caching. Stable output made diffs meaningful, reduced needless asset fingerprint changes, and made rollback evidence clearer.

Reproducibility became a direction with measured exceptions, not a badge awarded because a hash existed.

Verification happened before publication to the cache

A worker could upload a partial or corrupt image under a tag and then write a success record. R7K1 inverted authority.

The artifact uploaded under immutable content identity. Verification checked manifest attachment, expected platform, required files, startup metadata, and vulnerability or policy gates available to the project. Only then did the controller commit a verified build receipt for manifest M.

Consumers looked up the receipt, not a registry tag alone. A present artifact without a verified receipt remained quarantined.

The receipt included builder identity, timing, artifact digest, verification version, and logs. Re-verification under newer policy could add another attestation without rewriting the original bytes.

The cache stored evidence-backed artifacts, not everything that happened to exist in object storage.

Branch code was untrusted relative to R7K1. It could attempt to write misleading labels or upload an artifact under another manifest identity.

The build environment did not receive broad registry credentials. A trusted post-build component collected output, calculated artifact identity, attached the controller's manifest, and uploaded with narrow authority.

Build logs and application metadata supplied by the candidate were not allowed to override controller receipts. Readiness later compared embedded build identity with expected receipt, but the protected manifest label came from the trusted pipeline.

Storage enforced immutability for content-addressed objects. A same-digest write had to match existing bytes. A manifest mapping changed only through the build controller's transactional receipt.

Content addressing does not establish who is allowed to assert content relationships. Trust and hashing solved different questions.

Garbage collection followed references

Artifacts outlived branches and served several review generations. Deleting one branch could not delete a shared image still used elsewhere.

R7K1 retained artifacts referenced by active or rollback environments, pinned review evidence, verified receipts within policy, or ongoing builds. Unreferenced artifacts entered a grace period before removal.

The collector started from durable receipts and environment records, not registry tag names. It treated the artifact digest as the object and branch aliases as disposable indexes.

A deleted artifact left a tombstone in the build receipt if policy allowed, preserving why a later environment could no longer be recreated. Critical manifests and small provenance records outlived large image layers.

Reference-aware cleanup was another benefit of separating artifact identity from workflow names. The system could remove bytes without rewriting history and preserve bytes without keeping dead branch labels.

The manifest improved incident queries

When a base image issue appeared, R7K1 could find artifacts whose manifests resolved that digest. When a build recipe produced broken source maps, it could identify every artifact built under that recipe version.

Queries followed declared inputs rather than guessing from build dates. Active environments linked to artifacts, so affected previews could be removed or rebuilt deliberately.

The project did not have a sophisticated supply-chain platform. A normalized searchable manifest supplied enough provenance for targeted action.

This changed the cost of infrastructure maintenance. Updating a base image became a set of new manifest requests. Revoking an old input became a policy over known artifacts.

The cache key doubled as an explanation of exposure.

Artifact reuse across branches should not erase why an artifact was requested. Each request recorded branch, review, commit event, and requester provenance separately from the shared build receipt.

The branch page could say “reused artifact built originally for review 38” while still showing the current branch's commit and manifest match. Logs did not imply the branch itself had been skipped without evidence.

If one review had more restrictive access, the artifact's distribution followed the strongest relevant repository and content policy. Shared bytes did not make metadata or preview routes universally visible.

Provenance formed many-to-one relationships with artifact. That was expected, not duplication to compress away.

The manifest stabilized technical identity without diminishing the social context around proposals.

Rebuild and reuse became separate actions

Developers sometimes wanted a clean rebuild to investigate nondeterminism or a suspected platform error. R7K1 offered an explicit rebuild of manifest M rather than changing a dummy input to miss the cache.

The rebuild produced a new attempt and compared its artifact digest with the verified receipt. It did not replace the canonical cached artifact automatically if bytes differed.

Reuse remained the default for ordinary environment requests. Rebuild was diagnostic or policy-driven. A base-image refresh naturally produced a new manifest rather than a forced rebuild of the old one.

This vocabulary clarified intent:

reuse M -> use verified A
rebuild M -> test reproducibility against A
refresh moving input -> resolve new manifest N

Cache control stopped being a mysterious checkbox labeled “no cache.”

R7K1 could only hash inputs it knew and controlled. Remote build scripts could fetch unpinned resources. Compilers could depend on host kernel behavior. Time, randomness, locale, and network services could influence output.

Clean workers, restricted egress, pinned tools, and repeated-build comparison reduced those gaps. The manifest included a completeness status and documented known nondeterministic paths.

Projects that violated the declared build contract could still build but lost cross-request reuse until fixed. Safety took precedence over cache hit rate.

I avoided calling the digest a proof of reproducible build. It was a proof that R7K1 had identified one declared input set and attached one verified artifact receipt to it.

That narrower claim was useful and defensible.

The stable thing was the explanation

Branches moved, review titles changed, workers disappeared, and preview environments expired. The manifest remained a compact explanation of why R7K1 considered two build requests equivalent.

It connected source tree, dependency resolution, recipe, base image, tooling, and declared configuration. Its digest gave concurrency and cache a stable key. Its stored canonical form gave people something to inspect when reuse was wrong.

The artifact digest answered which bytes existed. The build receipt answered who produced and verified them. The environment receipt answered where and how they ran. None of those identities could replace the others.

R7K1's breakthrough was not hashing a JSON file. It was moving build equality out of branch names and into an explicit causal model with visible limits.

The manifest was more stable than the branch because it named the thing the branch had only requested.