LearnNewsExamplesServices
Frontmatter
id16087
titleCollapse duplicate deploy SHA inputs into one operator pin
stateClosed
labels
enhancementaiarchitecturebuild
assigneesneo-gpt
createdAtJul 28, 2026, 3:41 PM
updatedAtJul 28, 2026, 4:39 PM
githubUrlhttps://github.com/neomjs/neo/issues/16087
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtJul 28, 2026, 4:39 PM

Collapse duplicate deploy SHA inputs into one operator pin

Closed Backlog/active-chunk-10 enhancementaiarchitecturebuild
neo-gpt
neo-gpt commented on Jul 28, 2026, 3:41 PM

Context

A manual exact-SHA deployment currently requires the operator to provide the same 40-character commit twice:

NEO_REF=<sha>
NEO_REVISION=<sha>

That duplication is not a second decision. It is an implementation detail left exposed by the provenance work in #15774 and the resolve-once reference pipeline in #15792.

The three provenance facts remain valid and distinct:

  1. the selector requested before resolution;
  2. the commit packaged into the image;
  3. the commit measured inside the built artifact.

The remaining friction is at the operator boundary: once a selector has resolved to one commit, a human should enter that commit once.

The Problem

For a pinned deployment, NEO_REF and NEO_REVISION must contain the same resolved SHA. Requiring both values adds no information, but it creates a mismatch state where the source fetch can use one commit while the OCI revision label asserts another.

The current reference pipeline already demonstrates the intended shape: it accepts one NEO_SELECTOR, resolves it once, then exports both Docker-facing values from the same resolved_revision. Manual Compose use does not have that adapter and exposes both internal build arguments directly.

The Dockerfile also lacks a hard equality check between a non-empty asserted NEO_REVISION and the independently generated /app/.neo-revision. A caller can therefore stamp an image with a revision claim that disagrees with its packaged source.

The Architectural Reality

  • ai/examples/cloud-deployment/deploy-pipeline.sh owns the pre-resolution selector. It resolves branch, tag, or SHA input once and currently exports both NEO_REF and NEO_REVISION from the resulting full commit.
  • ai/deploy/docker-compose.yml forwards both variables independently into the kb-server, mc-server, and orchestrator builds.
  • ai/deploy/Dockerfile still needs separate internal build arguments: NEO_REF drives source acquisition and labels the requested ref; NEO_REVISION drives the OCI packaged-revision label. Docker labels cannot derive a later-stage build argument from a file emitted by an earlier stage.
  • /app/.neo-revision remains the independently measured artifact truth and must not be replaced by caller input.
  • The structure map confirms that ai/deploy and ai/examples/cloud-deployment already own this surface; no new module or placement decision is needed.

The Fix

  1. Define one operator-facing resolved deployment pin at the Compose/manual-deploy boundary. Reuse NEO_REVISION rather than introducing a third environment variable.
  2. Map that one pin internally to both Docker build arguments for all three services:
    • source fetch input (NEO_REF);
    • OCI packaged-revision assertion (NEO_REVISION).
  3. Keep NEO_SELECTOR as the pre-resolution pipeline input. Branches and tags resolve before reaching Compose; they do not become OCI revision values.
  4. Preserve the current unpinned path: with the resolved pin unset, source acquisition defaults to dev and the OCI revision remains empty.
  5. Add a Docker build-time integrity assertion: when NEO_REVISION is non-empty, it must equal the SHA written to /app/.neo-revision; fail the build with a clear error when they differ.
  6. Update the deployment guide and focused tests so no documented manual path asks a human to duplicate the SHA.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Evidence
Manual / Compose resolved pin ai/deploy/docker-compose.yml one full SHA supplied once and mapped to both internal build arguments for all three services unset keeps current unpinned behavior Compose render tests
Pipeline selector ai/examples/cloud-deployment/deploy-pipeline.sh branch, tag, or SHA resolves once; one resolved commit reaches Compose existing selector default remains unchanged DeployPipelineRevisionPin.spec.mjs
Docker source-fetch argument ai/deploy/Dockerfile receives the canonical resolved pin as internal NEO_REF dev when no pin is supplied focused Docker/Compose contract test
OCI packaged revision ai/deploy/Dockerfile receives the same canonical pin as internal NEO_REVISION; never a mutable selector empty means not asserted image provenance parity test
/app/.neo-revision source stage in ai/deploy/Dockerfile remains independently generated actual revision; mismatch with a non-empty assertion fails the build local-build for local source mode mutation test plus parity receipt
Operator documentation learn/agentos/cloud-deployment/PipelineWiring.md shows one-pin manual invocation and resolve-once pipeline flow none documentation assertion

Decision Record Impact

Aligned with existing decisions; no ADR change. This tightens the implementation boundary established by #15774 and #15792. It does not collapse requested, resolved, and observed provenance into one fact, and it does not choose rollout authority.

Acceptance Criteria

  • A manual exact-SHA Compose render requires one operator environment value, not duplicate NEO_REF and NEO_REVISION assignments.
  • That one full SHA reaches both internal Docker build arguments for kb-server, mc-server, and orchestrator.
  • With the resolved pin unset, all three builds retain the current behavior: source ref dev, OCI revision empty.
  • The reference pipeline still accepts branch, tag, or SHA selectors, resolves once, and hands one canonical full SHA to the Compose boundary.
  • A non-empty asserted revision that differs from /app/.neo-revision fails the Docker build with a clear integrity error.
  • For a correct git-source pin, org.neomjs.image.requested-ref, org.opencontainers.image.revision, and /app/.neo-revision all report the same full SHA across the three-service cohort.
  • Local-source builds remain honest: /app/.neo-revision is local-build and the OCI revision is empty.
  • DeployPipelineRevisionPin.spec.mjs and the provenance parity coverage are updated with positive, unset, and mismatch cases.
  • PipelineWiring.md no longer instructs operators to export the same SHA into two variables.

Out of Scope

  • Removing or merging the three provenance surfaces.
  • Changing /app/.neo-revision from measured artifact truth into caller-supplied metadata.
  • Choosing automated rollout authority, rollout cadence, or rollback policy.
  • Changes in downstream or private deployment repositories.
  • Changing local-source provenance semantics.

Avoided Traps

  • Collapsing selector and resolved revision. The pipeline selector remains distinct and resolves before the build boundary.
  • Putting mutable dev into the OCI revision label. Unpinned builds continue to leave that label empty.
  • Adding another environment variable. The goal is one operator pin, not a third alias.
  • Trusting the label assertion without checking the artifact. /app/.neo-revision remains the falsifier.
  • Removing internal Docker arguments because the operator sees one value. Their build-stage roles remain separate even when their pinned value shares one source.

Related

Related: #15774 Related: #15792 Related: #15787

Live latest-open sweep: checked the latest 20 open issues at 2026-07-28T13:40:45Z; no equivalent found. A2A in-flight claim sweep: checked 30 messages across all read states at the same timestamp; no overlapping lane claim or intent found. Semantic Knowledge Base and exact issue/discussion corpus searches found the existing two-variable provenance design, but no consolidation ticket.

Origin Session ID: 019fa530-53d6-7271-bf05-51497720b29c

Retrieval Hint: query_raw_memories("NEO_REF NEO_REVISION duplicate operator pin deployment provenance"); inspect ai/deploy/docker-compose.yml, ai/deploy/Dockerfile, ai/examples/cloud-deployment/deploy-pipeline.sh, and learn/agentos/cloud-deployment/PipelineWiring.md.