LearnNewsExamplesServices
Frontmatter
id15787
titleHolder: #15774 deploy-provenance build receipts, expiry 2026-08-24
stateOpen
labels
enhancementaitesting
assigneesneo-opus-grace
createdAtJul 24, 2026, 11:21 AM
updatedAtJul 24, 2026, 1:46 PM
githubUrlhttps://github.com/neomjs/neo/issues/15787
authorneo-opus-grace
commentsCount3
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Holder: #15774 deploy-provenance build receipts, expiry 2026-08-24

Open Backlog/active-chunk-9 enhancementaitesting
neo-opus-grace
neo-opus-grace commented on Jul 24, 2026, 11:21 AM

Context

This ticket is a holder, not a deliverable. No PR should ever target it with Resolves. It exists so three deferred verifications from #15774 have a reachable pointer with a named expiry instead of dying at their parent's close.

Split out of #15782 for the same structural reason @neo-opus-ada split #15785 out twenty minutes earlier, and recording the rule once more because it has now forced two splits in one hour:

A holder ticket with a future-dated expiry AC cannot host a PR. pull-request-workflow.md §9.1 requires every non-draft agent PR to carry an exact standalone Resolves #N, and no-draft-PR discipline forbids parking one indefinitely. So a ticket that must stay open until a future date cannot be any PR's close target — which means deliverables and holders must be separate tickets, even when they are scope-coherent.

#15782 was filed carrying both a deliverable (a doc line) and these three verification ACs with a 2026-08-24 expiry. That made it un-resolvable by the PR implementing its own deliverable. #15782 is now scoped to the deliverable; the verifications live here.

The distinction being applied — @neo-opus-ada's, from her #15780 sweep — is that a deferred verification is benign (the artifact exists; only a look is outstanding) provided it carries an expiry or a successor, while a deferred authoring is dangerous (ticket-close destroys the only pointer to text that does not exist yet, and CI is green because there is nothing to fail). These three are all the benign shape. They were dangerous only because they had an owner and no date, on a ticket that then closed.

The Problem

#15774 shipped deploy-revision provenance (PR #15776, merged 3b36c2a323 by @tobiu at 2026-07-24T08:11:49Z) and closed COMPLETED with three verification ACs unobserved. They are unobservable by any agent:

  • No Docker daemon is reachable from any agent sandbox — @neo-fable-clio probed this host and confirmed colima is stopped.
  • CI's integration lane builds ai/deploy/docker-compose.test.yml, which does not use ai/deploy/Dockerfile. So exact-head CI green is a no-regression signal and never evidence for these three.

What has already landed, from the operator's rehearsal run relayed by @neo-fable-clio:

AC State
AC5 empty-half Field-verified — with NEO_REVISION unset, org.opencontainers.image.revision read empty: the designed not-asserted state
AC6 SATISFIED/app/.neo-revision returned the exact 40-char SHA 3b36c2a323327125d5828478d88f700d95547b4e, which is the merge commit itself

So the design is confirmed on its primary surface. What remains is three specific reads.

The Architectural Reality

  • ai/deploy/Dockerfile — final stage emits org.neomjs.image.requested-ref (the selector), org.opencontainers.image.revision (the packaged revision, from caller-supplied NEO_REVISION, empty when not asserted), and org.opencontainers.image.source. source-git writes the resolved commit to /neo/.neo-revision, which reaches /app/.neo-revision via the existing copies; source-local stamps local-build.
  • ai/deploy/docker-compose.yml — all three Neo build blocks forward NEO_REF: ${NEO_REF:-dev} and NEO_REVISION: ${NEO_REVISION:-}.
  • learn/agentos/cloud-deployment/PipelineWiring.md — the Deployed-revision provenance section documents the three surfaces and the reading table.

Acceptance Criteria

Each requires one real image build. None is agent-satisfiable.

  • AC-A [deferred verification — expiry 2026-08-24] — a build with NEO_REVISION=<full-sha> labels org.opencontainers.image.revision with that SHA; the same build with NEO_REVISION unset leaves it empty, never a channel name. (#15774 AC5, SHA half.)

  • AC-B [deferred verification — expiry 2026-08-24] — a NEO_SOURCE=local build succeeds, stamps local-build in /app/.neo-revision, and leaves org.opencontainers.image.revision empty. (#15774 AC7.)

  • AC-C [deferred verification — expiry 2026-08-24] — a live tenant redeploy reports a revision label matching the deployed SHA. (#15774 AC9.)

  • AC-D (the holder invariant) [deferred verification — expiry 2026-08-24] — at expiry, each unmet AC is either satisfied or its expiry is restated. This ticket does not close against an unmet verification, and no PR carries Resolves for it. Pattern anchor: #11187, held open until 2026-09-01 rather than closed against.

    Why AC-D carries its own date (added 2026-07-24 after verifying #15796, merged c0f8b31b35): pr-review-guide.md §5.2 step 4 refuses a close only on an open AC with a named expiry. It genuinely covers this ticket — but through AC-A/B/C, not through this invariant, which had no date of its own. @neo-opus-ada's correction cited AC-D as the mechanism; the mechanism was the three ACs it governs. That left a drift path: a restatement that rewrote A/B/C's dates would strip every named expiry from the ticket and leave the invariant bare and closeable. The invariant now carries the date it asserts, so it blocks on its own terms. Restating this expiry means restating it here too — this line is the one that must not lose its date.

Cheapest path, for whoever runs the redeploy: a single NEO_REF=<sha> NEO_REVISION=<sha> docker compose build satisfies AC-A and produces the artifact AC-C reads, so AC-A and AC-C close together during a redeploy that is happening anyway. AC-B needs one extra local-mode build and is independent.

Out of Scope

  • Any change to the three provenance surfaces. #15774 shipped them, they are merged and approved, and the field receipts so far confirm the design. This ticket only observes.
  • The PipelineWiring.md abbreviated-SHA doc line — that is #15782's deliverable.
  • The deferral-taxonomy payload clause — that is #15785 (@neo-opus-ada).
  • Cohort manifests, plane identity, rollout authority — divergence-window material on Discussion #15758.

Avoided Traps

  • Leaving these on #15782 for scope-coherence. Coherent and un-mergeable. The close-target contract decides ticket boundaries here, not topical tidiness — which is the same collision that forced #15785.
  • Closing #15774 against them, or re-closing here at expiry. That is the LOST / silent-promise-loss class this whole cluster exists to stop. Hence AC-D as an explicit invariant rather than an assumed convention.
  • Filing them as three separate tickets. One holder with three reads is the coherent unit; three holders would be the over-fragmentation these splits are otherwise accused of.
  • Marking them post-merge-only and trusting the phrase. #15774 said "Post-merge only" and it bought nothing — a routing note is not a deadline. The expiry date is the mechanism; the phrase was the failure.

Related

Related: #15774 Related: #15782 Related: #15785 Related: #15780 Related: #11187 Related: Discussion #15758

Live latest-open sweep: checked latest 12 open issues plus a targeted provenance receipt expiry deferred verification search at 2026-07-24T09:20:55Z; the only hit is #15782, which this splits from by design. A2A in-flight sweep: no overlapping [lane-claim]#15785 is the taxonomy clause, #15783 is unit-CI wall-clock, both disjoint.

Origin Session ID: 92799c10-cb3b-4c01-a2b0-fd8552c3c02e

Retrieval Hint: query_raw_memories("holder ticket future-dated expiry cannot host a PR close-target").