3 Commits

Author SHA1 Message Date
Ajay Krishnan
82378391c6 Merge pull request #8 from ajaynomics/fix/github-only-upstream-watcher-20260720
All checks were successful
Candidate compatibility / prepare (push) Successful in 33s
Candidate compatibility / image ${{ matrix.id }} (push) Has been skipped
Candidate compatibility / repository (push) Successful in 47s
Candidate compatibility / fixture-contract (push) Successful in 1m56s
Candidate compatibility / lockstep clients Ruby 3.4 (push) Successful in 2m8s
Candidate compatibility / lockstep clients Ruby 3.3 (push) Successful in 2m23s
Candidate compatibility / lockstep clients Ruby 3.2 (push) Successful in 2m53s
Candidate compatibility / lockstep clients Ruby 4.0 (push) Successful in 3m4s
Candidate compatibility / Gitea full exact image matrix (push) Successful in 3m51s
Watch upstream OpenCode / open-compatibility-pr (push) Has been skipped
Run upstream watcher only on GitHub
2026-07-20 11:44:38 -07:00
b25ac238f5 Run upstream watcher only on GitHub 2026-07-20 11:41:52 -07:00
Ajay Krishnan
3bb0877034 Certify alpha8 production and rollback tuples (#7)
Some checks failed
Candidate compatibility / prepare (push) Successful in 12s
Candidate compatibility / repository (push) Successful in 17s
Candidate compatibility / image ${{ matrix.id }} (push) Has been skipped
Candidate compatibility / fixture-contract (push) Successful in 48s
Candidate compatibility / lockstep clients Ruby 4.0 (push) Successful in 1m0s
Candidate compatibility / lockstep clients Ruby 3.4 (push) Successful in 1m1s
Candidate compatibility / lockstep clients Ruby 3.2 (push) Successful in 1m4s
Candidate compatibility / lockstep clients Ruby 3.3 (push) Successful in 1m43s
Candidate compatibility / Gitea full exact image matrix (push) Successful in 1m48s
Watch upstream OpenCode / open-compatibility-pr (push) Failing after 12s
* Add explicit rollback bootstrap and maintenance model

* Certify alpha8 production and rollback tuples
2026-07-20 01:33:27 -07:00
18 changed files with 2172 additions and 159 deletions

View File

@@ -15,6 +15,7 @@ concurrency:
jobs:
open-compatibility-pr:
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0

View File

@@ -93,10 +93,12 @@ and must not be described as review evidence. A person must review a passing
GitHub artifact and commit the durable certification document under `evidence/`;
no workflow commits or promotes its own result.
`manifests/image-matrix.json` binds the active pending matrix to both candidate
commits. Its `previous_certification` entries preserve the last alpha7 result;
they do not certify alpha8. Ajent and Mushu custom-image rows remain pending
until their host canaries run against this exact pair.
`manifests/image-matrix.json` binds the certified shared-client matrix to both
exact, unpublished alpha8 commits. Each public row is explicitly scoped to the
shared Ruby/SSE contract; its separate `required_consumer_profiles` are not
implied by that result. The host rows bind the Ajent, Travelwolf, and Mushu
application canaries to their exact current alpha8 and previous alpha7 consumer
commits and durable evidence documents.
The checked-in alpha8 local exact-image result is explicitly marked
`not-certified` and `shared-client-contract-only`. It proves the Ruby/SSE probe
@@ -122,7 +124,9 @@ durable certification documents and Git history are the archive. This keeps a
fast-moving upstream from making every future run retest every old release.
See [docs/certification.md](docs/certification.md) for promotion, canary, and
rollback evidence requirements.
rollback evidence requirements, and [docs/maintenance.md](docs/maintenance.md)
for consumer ownership, release order, runner boundaries, and expected failure
modes.
## License

View File

@@ -85,6 +85,13 @@ must explicitly contain values matching the promotion:
}
```
The promotion command stores each accepted document as a hash-bound reference,
`{"path":"evidence/...json","sha256":"sha256:..."}`. Later transitions
re-read the bytes, verify that digest, and cross-check any detailed consumer
tree, client, and runtime coordinates against the tuple. Editing an evidence
document therefore invalidates the certification until it is reviewed and
promoted again; a copied `tuple_sha256` alone is not sufficient.
Preview the exact manifest transition with `--dry-run`, then repeat without it
to write the manifest:
@@ -153,16 +160,34 @@ create and canary a distinct consumer rollback commit that preserves the same
known-good client and exact runtime. Only after both immutable consumer commits
have real passing evidence can the manifest honestly contain certified
`current` and `previous` tuples. Until then, `promotion_readiness` remains
blocked and the candidate PR must not be treated as a deploy authorization.
non-certified (`bootstrap-current-only` or `candidate`) and the candidate PR
must not be treated as deploy authorization.
The schema-v1 promotion command deliberately cannot perform the first degraded
bootstrap transition when `current` is a known-failing baseline. Do not bypass
that guard by hand-editing the manifest or relabeling alpha2 evidence. A
separate reviewed state-machine change must first add an explicit
`bootstrap-current-only` state that preserves the failed baseline as
uncertified emergency provenance and leaves `previous` null. Itemized rollback
certification is complete only after a later, materially distinct passing tuple
can move the first certified current into `previous`.
Use the explicit degraded-bootstrap transition for the first passing rollback
tuple. It requires passing evidence bound to the complete candidate fingerprint
and a literal acknowledgement; it preserves the failed baseline as uncertified
`emergency_provenance`, leaves `previous` null, and records the temporary
`bootstrap-current-only` state:
```sh
ruby scripts/promote_runtime_tuple.rb bootstrap-current \
--consumer travelwolf \
--consumer-commit FULL_40_CHARACTER_ROLLBACK_COMMIT \
--status pass \
--certified-at 2026-07-20T12:00:00Z \
--evidence evidence/travelwolf-rollback.json \
--acknowledge-degraded-rollback \
accept-degraded-rollback-with-failed-emergency-provenance \
--dry-run
```
Review the dry run, repeat without `--dry-run`, and then treat that passing
tuple as the base for a normal promotion. Normal promotion moves it into
`previous`, removes the degraded rollback marker, and keeps the original failed
baseline only as historical emergency provenance. The repository becomes
`certified` only when every consumer has exact passing `current` and `previous`
tuples. Never use bootstrap to replace an existing previous tuple or to certify
a baseline already known to fail.
## Rollback

158
docs/maintenance.md Normal file
View File

@@ -0,0 +1,158 @@
# Maintaining OpenCode consumers
OpenCode compatibility is an executable tuple, not a gem version. The tuple is
the exact client commit, Rails adapter commit where used, OpenCode image digest,
consumer commit, compatibility profile, and passing evidence. A change to any
coordinate creates a new candidate.
## Dependency ownership
The adjacent projects intentionally do not all use the same gems:
| Consumer | OpenCode boundary | Owned dependency |
| --- | --- | --- |
| Ajent Rails | Ruby REST/SSE plus persisted Rails turns | `opencode-ruby` and `opencode-rails` |
| Travelwolf | Ruby REST/SSE plus persisted Rails turns and Sprite lifecycle | `opencode-ruby` and `opencode-rails` |
| Mushu | Ruby REST/SSE with application-owned conversation, claim, recovery, Telegram scope, and idempotency | `opencode-ruby` only |
| Greenroom | Direct voice-stream worker | No Ruby adapter gem |
| Leela | Custom strict-v2 server, security, and toolchain lane | No Ruby adapter gem |
| opencode-ajent | Native CLI and plugin hook/event lane | No Ruby adapter gem |
| inference | Provider configuration, hooks, routing, and migration lane | No Ruby adapter gem |
| Context Kit | MCP and OpenCode configuration producer | No Ruby adapter gem |
Do not add `opencode-rails` to an application that owns different persistence
semantics, and do not route plugin, provider, voice, or strict-v2 behavior
through the Ruby REST/SSE adapter merely to make versions look uniform. Share
fixtures, provenance, and promotion policy across those lanes instead.
Generated release snapshots, detached operational copies, editor locks, caches,
and linked task worktrees are evidence or tooling. They are not additional
consumers and must not be bulk-upgraded.
Direct consumers still need immutable inputs even though they do not use the
Ruby gems. A system service must execute a clean certified checkout or image,
not whichever files happen to be in a mutable development worktree. Node and
Bun plugins must be pinned through their package lock and tested against the
same OpenCode digest; a globally cached, unversioned plugin is not release
provenance. Generated OpenCode or MCP configuration needs both schema validation
and a candidate-runtime smoke test. Keep OpenCode auto-update disabled in every
certified lane so a process restart cannot silently change the server half of a
previously passing tuple.
## Supported window
Support the exact current and previous certified runtime tuples. Do not claim
compatibility with arbitrary future OpenCode versions. Additive fields and
unknown events should remain tolerant, while terminal text, request count,
ownership, persistence, and cleanup invariants remain strict.
The Ruby/SSE profile covers the endpoints and events the client actually uses,
including session creation and deletion, asynchronous prompts, event
subscription, status, terminal idle/status events, part deltas and updates,
authoritative assistant messages, questions, and permissions. A passing shared
profile does not certify Rails persistence, voice streaming, plugin hooks,
provider migrations, or generated MCP configuration.
## Release and promotion order
1. The watcher records a new upstream release tag and resolved OCI digest in a
PR. It never merges or deploys.
2. Update fixtures for any observed protocol change before changing the client.
3. Build `opencode-ruby` and `opencode-rails` as one release train. Rails must
resolve the exact Ruby version and commit being tested.
4. Run the shared fixture corpus, Ruby 3.2 through 4.0 lockstep matrix, and every
exact public image still used by a current, previous, or candidate tuple.
5. Run only the application-owned profiles for each consumer, in its own PR and
isolated environment. Health checks alone do not certify a tuple.
6. Promote and deploy one consumer at a time. Record the exact production
commit, image/base digest, loaded client commits, and live result.
7. Commit reviewed evidence and move the old passing current tuple to previous.
8. Publish annotated gem tags only after the exact commit candidate is green and
the trusted publisher is configured. Publication never implies deployment.
Commit pins are valid for an unpublished candidate, but the durable published
state must record the annotated tag object and peeled commit. Tags or `latest`
may be kept as human-readable provenance only; execution coordinates use full
Git commits and `image@sha256:...` references.
## Rollback
Rollback restores the whole certified `previous` tuple. Do not roll back only
the gem, only the consumer, or only the runtime image: the wire contract is the
unit of compatibility.
The deployment platform's immediately preceding application image is not
automatically the certified OpenCode rollback. Keep it as emergency service
provenance, but if it contains a client/runtime tuple known to fail the contract
it must not occupy `previous`. In that case rollback means deploying the exact
consumer commit and runtime coordinates recorded in the certified `previous`
tuple, even if that is different from the platform's one-click rollback target.
## Custom images
Record each provenance layer separately:
- consumer commit;
- exact output registry digest or Docker image ID;
- exact base image digest;
- custom OpenCode source commit, when the base is a fork;
- build-source commit when it differs from the deployment commit.
The tuple fingerprint binds these values. On the next rebuild of an older
unlabelled private image, add OCI labels for the custom OpenCode source, reported
version, consumer build revision, and base digest, then make preflight compare
the labels. Do not relabel an already certified image: that changes its digest.
## Runner and forge contract
Runner upgrades are a separate compatibility surface from OpenCode upgrades.
Workflows must install their required Ruby/toolchain explicitly, declare Bash
for scripts that use Bash syntax, and avoid relying on ambient runner packages.
Browser suites must provision the reviewed Chrome/Selenium path explicitly;
an absent browser is a runner failure, not product evidence. Keep a system-test
suite serial when its harness shares an ephemeral server port instead of
mistaking parallel `EADDRINUSE` failures for OpenCode regressions.
GitHub and Gitea do not implement every Actions feature identically. Keep the
same tests on both forges, but use forge-specific execution where necessary:
GitHub retains review artifacts and parallel dynamic image jobs; Gitea runs the
same manifest image set sequentially and makes no artifact-retention claim.
Neither path may mutate a runner or deploy a consumer.
A runner-only workflow repair needs exact-head CI, not an application canary.
A client, runtime image, event, persistence, or toolchain change needs the
profile and consumer canaries described above.
For a frozen rollback snapshot that is no longer a merge candidate, provide a
default-branch audit workflow that accepts and checks out an explicit full SHA.
That lets current runner plumbing test the immutable old application tree
without adding a CI-only commit to the rollback coordinate or pretending a
known runner-workflow failure is an application incompatibility.
## Expected breakages
- Prompt submission before event subscription can miss a terminal event and
hang a turn.
- An SSE parser that recognizes only `\n\n` can stall on CRLF, bare-CR,
byte-order-mark, comment, or multiline-data framing that is valid on the
wire.
- Changes to terminal or message-part events can duplicate or lose final text.
- Usage events can undercount multi-step requests if totals are overwritten.
- Reconnect logic can replay a prompt and create duplicate model requests.
- A Bun/Node server close can wait forever on idle keep-alive sockets unless
idle connections are explicitly reaped after admissions stop; the resulting
orphan can exhaust a service cgroup's PID budget and block a safe upgrade.
- Asset builds can fail when runtime configuration is evaluated without the
image variables available only at deploy time.
- A workflow can pass locally but fail under `sh` when it uses Bash arrays or
`mapfile`.
- Plugin hook names, provider schema, config/MCP schema, or CLI flags can break
direct consumers even when the Ruby profile stays green.
- `Opencode::Turn` is still an alpha-stage Rails composition seam; constructor,
observer, persistence, or finalization changes can break hosts even when the
lower-level Ruby wire client remains compatible.
- A custom fork can silently lose its required ordering or permission patch if
only an opaque output digest is retained.
When one of these changes, update the owning profile and consumer evidence. Do
not weaken a strict invariant to make a new upstream release pass.

View File

@@ -0,0 +1,76 @@
{
"schema_version": 1,
"consumer": "ajent-rails",
"profile": "rails-persisted-turn",
"consumer_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"consumer_tree": "29e6cd35313a06cef6f9c1f40a36e3ee582fd035",
"status": "pass",
"certified_at": "2026-07-20T08:14:53Z",
"tuple_sha256": "sha256:ae48333ba453f442d14263a916418a384f576db34d09a503111fc28c340eb408",
"clients": {
"opencode_ruby": {
"version": "0.0.1.alpha7",
"commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
},
"opencode_rails": {
"version": "0.0.1.alpha7",
"commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb"
}
},
"runtime": {
"application_image": "docker-registry:5000/ajent_rails@sha256:fe6fdc2325d304ddb276df7d35807c183e7e574876a0d76a6c6de6014619c76e",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"consumer_verification": {
"test_runs": 4247,
"assertions": 15740,
"failures": 0,
"errors": 0,
"skips": 4,
"rubocop_files": 1430,
"status": "pass"
},
"application_canary": {
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"source_availability": {
"origin_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"backup_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"github_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"status": "pass"
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": true,
"application_rebuild_required": false,
"deployed": false
}
}

View File

@@ -0,0 +1,80 @@
{
"schema_version": 1,
"consumer": "ajent-rails",
"profile": "rails-persisted-turn",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"consumer_tree": "a1e044f3a4f5c5ac0510a1d998c4afb55286da5a",
"status": "pass",
"certified_at": "2026-07-20T08:15:17Z",
"tuple_sha256": "sha256:326a87cdb67061bdd36dfcee989ae149511a0c627e857488d59e0f2b3c796720",
"clients": {
"publication_state": "unpublished",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f"
}
},
"runtime": {
"application_image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"forge_ci": {
"pr_run_id": "2718",
"main_build_run_id": "2719",
"protected_main_run_id": "2720",
"deploy_job_id": "12065",
"completed_at": "2026-07-20T08:06:22Z",
"status": "pass"
},
"application_canary": {
"checked_at": "2026-07-20T08:15:17Z",
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"production": {
"checked_at": "2026-07-20T08:15:17Z",
"application_health_status": 200,
"blackline_health_status": 200,
"ajent_public_status": 308,
"voice_contract_status": "pass",
"scotus_contract_status": "pass",
"status": "pass"
},
"source_availability": {
"origin_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"backup_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"github_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"status": "pass"
}
}

View File

@@ -0,0 +1,68 @@
{
"schema_version": 1,
"consumer": "mushu",
"profile": "ruby-rest-sse",
"consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"consumer_tree": "f43f48972ab444310508e9067831dfc6977f1a24",
"status": "pass",
"certified_at": "2026-07-20T06:39:46.921805Z",
"tuple_sha256": "sha256:8d7d3cb2d3e2d920c900a9c1ada365026872904588cff0b166903d1d6d32183f",
"clients": {
"opencode_ruby": {
"version": "0.0.1.alpha7",
"commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
},
"opencode_rails": null
},
"runtime": {
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"custom_opencode_source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": null,
"reported_version": "0.0.0-permission-shortcuts-202606300721",
"provenance_strength": "one-time-tag-source-attestation"
},
"source_availability": {
"origin_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719",
"backup_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719",
"status": "archived"
},
"shared_contract": {
"review_input": "opencode-compat-mushu-final-head.json",
"review_input_path_at_capture": "/tmp/opencode-compat-mushu-final-head.json",
"review_input_sha256": "sha256:57bf4b4f3c65159de4476b530aa0bc9b6077f1eacc69f7ea404f02314cb9760c",
"status": "pass"
},
"application_canary": {
"started_at": "2026-07-20T06:37:55Z",
"completed_at": "2026-07-20T06:39:46.921805Z",
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 7,
"mcp_context_count": 7,
"database_sha256": "sha256:a567d2ff9a906c9b78831bb6fa43153af539f1e76ee63bf35961472f55651d74",
"log_sha256": "sha256:a35a83b003a1f082b8a721b3faad8b35cb71dc9d1ceaedfc3cec3b26916e85b9",
"clean_worktree": true,
"status": "pass"
},
"raw_artifact_retention": "raw clean-worktree database and log artifacts are ephemeral; durable counts, hashes, and sanitized results are retained in this committed evidence document",
"forge_ci": {
"status": "not_run",
"run_ids": [],
"reason": "The frozen snapshot predates a runner-only empty-system-test repair. Earlier runs 2588 and 2590 tested different commits and are deliberately not attributed to this tuple."
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true,
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922",
"reason": "The retained one-click application rollback uses alpha2 and the old runtime; the certified compatibility rollback is this alpha7 source tuple."
},
"limitations": [
"The old runtime image metadata does not recover an exact Mushu overlay-build commit.",
"No exact-head forge run is claimed; the clean-worktree full application canary is the application evidence."
]
}

View File

@@ -0,0 +1,87 @@
{
"schema_version": 1,
"consumer": "mushu",
"profile": "ruby-rest-sse",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047",
"consumer_tree": "726c3878e71f7a4cf8c0b8a3fa8fc15382bbab45",
"status": "pass",
"certified_at": "2026-07-20T07:13:51Z",
"tuple_sha256": "sha256:804410a63cf2b6e4056d6dd9ce289ac72464f441c9785b6fc8dd50016263a1a8",
"clients": {
"publication_state": "unpublished",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
},
"opencode_rails": null
},
"runtime": {
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:a16bf0c4cd17698eba02244f6ba0e429b2238b6455e1415ff69fb08965c863a7",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"custom_opencode_source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": "521185613a864a16a76a21c7a60597ab3973bae9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"forge_ci": {
"pr": {
"run_id": "2700",
"job_ids": ["11903", "11904", "11905", "11906", "11907"],
"status": "pass",
"started_at": "2026-07-20T06:29:41Z",
"completed_at": "2026-07-20T06:41:14Z"
},
"main": {
"run_id": "2705",
"job_ids": ["11934", "11935", "11936", "11937", "11938"],
"status": "pass",
"started_at": "2026-07-20T06:43:03Z",
"completed_at": "2026-07-20T06:54:58Z"
}
},
"shared_contract": {
"review_input_path_at_capture": "/tmp/opencode-alpha8-mushu-permission-shortcuts.json",
"review_input_sha256": "sha256:6be88f0929815fa80839687e357473b232830aca4ccd1686f2a38ac9381088de",
"durable_ci_evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json",
"status": "pass"
},
"preproduction_canary": {
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 2,
"mcp_context_count": 2,
"database_sha256": "sha256:7fbe32ff9621196a2504aeba5eca78cb0c9c670edd7a1168b1f81f667872e2b6",
"log_path_at_capture": "tmp/alpha8-canary-rails.log",
"log_sha256": "sha256:51b698fcc8b7bce68e79cbae10997e19eb47a88985cbe24871d10740dda26225",
"status": "pass"
},
"raw_artifact_retention": "raw clean-worktree database and log artifacts are ephemeral; durable counts, hashes, and sanitized results are retained in this committed evidence document",
"application_canary": {
"started_at": "2026-07-20T07:03:17Z",
"completed_at": "2026-07-20T07:04:51.376351Z",
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 1,
"mcp_context_count": 1,
"status": "pass"
},
"production": {
"checked_at": "2026-07-20T07:13:20Z",
"application_tag_provenance": "docker-registry:5000/mushu:3f6754caab3f66537a5834f44ee63c1f05ecb047",
"application_image": "docker-registry:5000/mushu@sha256:30f204184520d504c7c384c7f6e503fa3547217aaaef2f1d0d65884533bdeacd",
"web_health_status": 200,
"status_health_status": 200,
"runtime_readiness": "ready",
"containers": ["web", "calendar", "telegram", "opencode"],
"deployment_mode": "manual",
"status": "pass"
},
"rollback_operability": {
"certified_previous_consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922"
}
}

View File

@@ -0,0 +1,107 @@
{
"schema_version": 1,
"kind": "shared-client-and-lockstep-certification",
"status": "pass",
"certification_scope": "shared-ruby-rest-sse-and-rails-lockstep",
"publication_state": "unpublished",
"clients": {
"opencode_ruby": {
"version": "0.0.1.alpha8",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"runtime_opencode_ruby_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
}
},
"github_workflow": {
"run_id": "29725021192",
"run_attempt": "1",
"head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"repository": "ajaynomics/opencode-compat",
"run_url": "https://github.com/ajaynomics/opencode-compat/actions/runs/29725021192",
"completed_at": "2026-07-20T07:34:59Z",
"status": "pass",
"artifact_retention_days": 30
},
"gitea_workflow": {
"run_id": "2717",
"head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"repository": "ajaynomics/opencode-compat",
"run_url": "https://gitea.ajay.to/ajaynomics/opencode-compat/actions/runs/2717",
"completed_at": "2026-07-20T07:36:06Z",
"status": "pass",
"artifact_evidence_claimed": false
},
"fixture_contract": {
"checked_at": "2026-07-20T07:34:34Z",
"fixture_count": 7,
"status": "pass"
},
"rails_lockstep": [
{
"ruby_runtime_version": "3.2.11",
"status": "pass"
},
{
"ruby_runtime_version": "3.3.12",
"status": "pass"
},
{
"ruby_runtime_version": "3.4.10",
"status": "pass"
},
{
"ruby_runtime_version": "4.0.6",
"status": "pass"
}
],
"exact_image_contracts": [
{
"id": "upstream-1.16.1",
"checked_at": "2026-07-20T07:34:50Z",
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn"]
},
{
"id": "upstream-1.17.18",
"checked_at": "2026-07-20T07:34:55Z",
"image": "ghcr.io/anomalyco/opencode@sha256:bf9d0e84b7cedef436a8f57db4d48767cd5d8fc6604f666335c1cc916b199a97",
"reported_version": "1.17.18",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["plugin-ledger"]
},
{
"id": "upstream-1.18.3",
"checked_at": "2026-07-20T07:34:48Z",
"image": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
"reported_version": "1.18.3",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"]
}
],
"certified_at": "2026-07-20T07:36:06Z",
"limitations": [
"The exact-image contracts execute opencode-ruby, not opencode-rails.",
"Rails persistence and each consumer application profile require separate passing evidence.",
"The exact client commits are certified candidates but remain unpublished until RubyGems trusted publishers are configured and registry publication is verified."
]
}

View File

@@ -0,0 +1,73 @@
{
"schema_version": 1,
"consumer": "travelwolf",
"profile": "rails-persisted-turn",
"consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"consumer_tree": "d0e47b2d90ace87c6cc9c4b94b2f22987441c1f6",
"status": "pass",
"certified_at": "2026-07-20T06:31:42Z",
"tuple_sha256": "sha256:20ecec3d2c3743efcd8575e71ec46dd7b86c2865e472511d16a96d57ddb8b40c",
"clients": {
"opencode_ruby": {
"version": "0.0.1.alpha7",
"commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
},
"opencode_rails": {
"version": "0.0.1.alpha7",
"commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb"
}
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1"
},
"forge_ci": {
"pr": {
"run_id": "2696",
"job_id": "11898",
"status": "pass",
"started_at": "2026-07-20T06:20:57Z",
"completed_at": "2026-07-20T06:23:32Z"
},
"main": {
"run_id": "2698",
"job_id": "11900",
"status": "pass",
"started_at": "2026-07-20T06:23:58Z",
"completed_at": "2026-07-20T06:25:49Z"
}
},
"shared_contract": {
"artifact": "opencode-alpha7-shared-contract-531011de.json",
"source_path_at_capture": "/data/projects/travelwolf/.worktrees/opencode-alpha8-candidate-20260720/tmp/opencode-alpha7-shared-contract-531011de.json",
"artifact_sha256": "sha256:12cef4d8b713f32289dc98ce3a0a3ef2094a54205a160d3a5b4bcfc81f2805c9",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"status": "pass"
},
"application_canary": {
"artifact": "opencode-alpha7-application-531011de.json",
"source_path_at_capture": "/data/projects/travelwolf/.worktrees/opencode-alpha8-candidate-20260720/tmp/opencode-alpha7-application-531011de.json",
"artifact_sha256": "sha256:5f5693bba27a16bc703c3211827a09171c77d5f9ef1acf6def1917df43a6cebc",
"checked_at": "2026-07-20T06:22:36.958Z",
"final_text": "TRAVELWOLF_ALPHA7_CANARY_OK",
"application_prompt_count": 1,
"user_turn_count": 1,
"authoritative_assistant_turn_count": 1,
"child_turn_count": 0,
"model_request_count_observed": false,
"cleanup_status": "pass",
"status": "pass"
},
"raw_artifact_retention": "ephemeral clean-worktree files; durable sanitized results and hashes are retained in this committed evidence document",
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true,
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "8494c52d11bf2995c378398e977d82d06a09e046",
"reason": "The retained one-click application rollback uses alpha6 clients; the certified compatibility rollback is this alpha7 source tuple."
}
}

View File

@@ -0,0 +1,84 @@
{
"schema_version": 1,
"consumer": "travelwolf",
"profile": "rails-persisted-turn",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"consumer_tree": "f436a7248d90e5ae9e84c45f264acd913a4162aa",
"status": "pass",
"certified_at": "2026-07-20T06:51:48Z",
"tuple_sha256": "sha256:0321c4246bed6ab62587924a83204a9e2ca7517b1dec5e4aef35dcac400fcc8e",
"clients": {
"publication_state": "unpublished",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f"
}
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1"
},
"forge_ci": {
"pr": {
"run_id": "2702",
"job_id": "11920",
"status": "pass",
"started_at": "2026-07-20T06:33:58Z",
"completed_at": "2026-07-20T06:35:29Z"
},
"main": {
"run_id": "2704",
"job_id": "11933",
"status": "pass",
"started_at": "2026-07-20T06:36:57Z",
"completed_at": "2026-07-20T06:38:11Z"
}
},
"shared_contract": {
"review_input_path_at_capture": "/data/projects/travelwolf/.worktrees/opencode-alpha8-candidate-20260720/tmp/opencode-alpha8-shared-contract-02aed70e.json",
"review_input_sha256": "sha256:f63edbafba8fb7646d1c88e33472636af56770110d920006211b4ff3f115b977",
"durable_ci_evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"status": "pass"
},
"application_canary": {
"review_input_path_at_capture": "/data/projects/travelwolf/.worktrees/opencode-alpha8-candidate-20260720/tmp/opencode-alpha8-application-02aed70e.json",
"review_input_sha256": "sha256:6f4dfe14a74bb84c13cf566302222f737603d2fbafa7231c7bb332a2567bca24",
"offline_input_path_at_capture": "/data/projects/travelwolf/.worktrees/opencode-alpha8-candidate-20260720/tmp/opencode-alpha8-offline-contract-02aed70e.json",
"offline_input_sha256": "sha256:4e8157fbb44d6fd0346bea79aee1271e400cd7fde57bc8a45afa77250134029c",
"checked_at": "2026-07-20T06:31:13.506Z",
"final_text": "TRAVELWOLF_ALPHA8_CANARY_OK",
"application_prompt_count": 1,
"user_turn_count": 1,
"authoritative_assistant_turn_count": 1,
"child_turn_count": 0,
"model_request_count_observed": false,
"cleanup_status": "pass",
"status": "pass"
},
"raw_artifact_retention": "ephemeral clean-worktree files; durable sanitized results and hashes are retained in this committed evidence document",
"production": {
"checked_at": "2026-07-20T06:51:48Z",
"application_tag_provenance": "127.0.0.1:5000/travelwolf:02aed70eb4de201c6510e3be583699f8eb984ce7",
"application_image_id": "sha256:031371e20f34e1014712ddc668b4e182483664bb70f116b683c0ec83d6668338",
"application_image_created_at": "2026-07-20T06:42:55.929825054Z",
"internal_health_status": 200,
"authenticated_public_health_status": 200,
"container_status": "running",
"deployment_mode": "manual-isolated-kamal",
"status": "pass"
},
"rollback_operability": {
"certified_previous_consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "8494c52d11bf2995c378398e977d82d06a09e046",
"immediate_platform_rollback_image_id": "sha256:4291b256b37eeb694435093655db17154c837e53335b8d0eb8f1b07a744e856e"
}
}

View File

@@ -15,6 +15,8 @@ module OpenCodeCompat
IMMUTABLE_IMAGE = /\A[^@\s]+@sha256:[0-9a-f]{64}\z/
UTC_TIMESTAMP = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\z/
CERTIFICATION_STATUS = "pass"
DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT =
"accept-degraded-rollback-with-failed-emergency-provenance"
NON_CERTIFIABLE_TUPLE_STATUSES = %w[observed-production-contract-failed].freeze
TUPLE_METADATA_KEYS = %w[
certification
@@ -30,6 +32,26 @@ module OpenCodeCompat
source_image
upstream_base
].freeze
EVIDENCE_REFERENCE_KEYS = %w[path sha256].freeze
NULLABLE_COMMIT_KEYS = %w[build_source_commit].freeze
EVIDENCE_RUNTIME_KEY_MAP = {
"custom_opencode_source_commit" => "source_commit"
}.freeze
EVIDENCE_RUNTIME_COORDINATE_KEYS = %w[
application_image
application_image_id
base_image
build_source_commit
docker_image_id
image
provenance_strength
registry_ref
reported_version
source_commit
source_image
tag_provenance
upstream_base
].freeze
def initialize(root:, manifest_path: File.join(root, "manifests/runtime-tuples.json"))
@root = File.realpath(root)
@@ -82,6 +104,37 @@ module OpenCodeCompat
end
end
def bootstrap_current(consumer:, consumer_commit:, certification:, acknowledgement:, dry_run: false)
validate_full_commit!(consumer_commit, "consumer commit")
unless acknowledgement == DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT
raise PromotionError,
"bootstrap requires explicit acknowledgement #{DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT.inspect}"
end
if dry_run
manifest = read_manifest
return bootstrap_current_manifest(
manifest,
consumer: consumer,
consumer_commit: consumer_commit,
certification: certification,
acknowledgement: acknowledgement
)
end
with_current_manifest_lock do |manifest|
bootstrapped = bootstrap_current_manifest(
manifest,
consumer: consumer,
consumer_commit: consumer_commit,
certification: certification,
acknowledgement: acknowledgement
)
atomic_write(bootstrapped)
bootstrapped
end
end
private
def read_manifest
@@ -124,7 +177,55 @@ module OpenCodeCompat
consumer_entry["previous"] = certified_previous
consumer_entry["current"] = certified_candidate
consumer_entry["candidate"] = nil
manifest["migration_state"] = all_consumers_certified?(manifest) ? "certified" : "candidate"
consumer_entry.delete("rollback_state")
refresh_promotion_state!(manifest)
manifest
end
def bootstrap_current_manifest(manifest, consumer:, consumer_commit:, certification:, acknowledgement:)
consumer_entry = fetch_consumer!(manifest, consumer)
profile = fetch_profile!(consumer_entry, consumer)
candidate = prepare_candidate!(consumer_entry, consumer_commit)
current = consumer_entry.fetch("current") do
raise PromotionError, "#{consumer} has no current tuple to retain as emergency provenance"
end
validate_tuple!(current, "#{consumer} current")
unless NON_CERTIFIABLE_TUPLE_STATUSES.include?(current["status"])
raise PromotionError, "degraded bootstrap is only for a current tuple known to fail the contract"
end
unless consumer_entry["previous"].nil?
raise PromotionError, "degraded bootstrap cannot replace an existing previous tuple"
end
if consumer_entry.key?("emergency_provenance") || consumer_entry.key?("rollback_state")
raise PromotionError, "degraded bootstrap has already been recorded for this consumer"
end
candidate_fingerprint = tuple_fingerprint(candidate, consumer: consumer, profile: profile)
current_fingerprint = tuple_fingerprint(current, consumer: consumer, profile: profile)
if candidate_fingerprint == current_fingerprint
raise PromotionError, "#{consumer} candidate is identical to its failed current tuple"
end
certified_candidate = certify_tuple!(
candidate,
consumer: consumer,
profile: profile,
supplied: certification,
expected_fingerprint: candidate_fingerprint,
label: "bootstrap candidate"
)
consumer_entry["emergency_provenance"] = deep_copy(current)
consumer_entry["current"] = certified_candidate
consumer_entry["candidate"] = nil
consumer_entry["previous"] = nil
consumer_entry["rollback_state"] = {
"status" => "degraded-no-certified-previous",
"acknowledgement" => acknowledgement,
"recorded_at" => certification.fetch("certified_at"),
"emergency_provenance_status" => current.fetch("status")
}
refresh_promotion_state!(manifest)
manifest
end
@@ -236,21 +337,23 @@ module OpenCodeCompat
end
validate_timestamp!(certified_at, "#{label} certification timestamp")
raise PromotionError, "#{label} certification requires at least one evidence file" if evidence.empty?
raise PromotionError, "#{label} certification evidence files must be unique" unless evidence.uniq == evidence
consumer_commit = tuple.fetch("consumer_commit")
normalized_evidence = evidence.map do |reference|
validate_evidence!(
reference,
consumer: consumer,
profile: profile,
consumer_commit: consumer_commit,
tuple: tuple,
status: status,
certified_at: certified_at,
tuple_fingerprint: expected_fingerprint,
label: label
)
end
evidence_paths = normalized_evidence.map { |reference| reference.fetch("path") }
unless evidence_paths.uniq == evidence_paths
raise PromotionError, "#{label} certification evidence files must be unique"
end
{
"status" => status,
@@ -266,7 +369,13 @@ module OpenCodeCompat
raise PromotionError, "#{label} tuple has invalid or stale certification metadata"
end
validate_supplied_certification!(
references = Array(certification["evidence"])
unless references.all? { |reference| structured_evidence_reference?(reference) }
raise PromotionError,
"#{label} recorded certification evidence must use hash-bound {path, sha256} references"
end
validated = validate_supplied_certification!(
certification,
consumer: consumer,
profile: profile,
@@ -274,23 +383,24 @@ module OpenCodeCompat
expected_fingerprint: expected_fingerprint,
label: label
)
unless certification["evidence"] == validated["evidence"]
raise PromotionError, "#{label} recorded certification evidence references are not canonical"
end
end
def validate_evidence!(
reference,
consumer:,
profile:,
consumer_commit:,
tuple:,
status:,
certified_at:,
tuple_fingerprint:,
label:
)
unless reference.is_a?(String) && !reference.empty?
raise PromotionError, "#{label} evidence references must be non-empty strings"
end
reference_path, expected_sha256 = evidence_reference_parts!(reference, label)
relative = Pathname.new(reference).cleanpath
relative = Pathname.new(reference_path).cleanpath
if relative.absolute? || relative.each_filename.first != "evidence"
raise PromotionError, "#{label} evidence must be a repository-relative path under evidence/"
end
@@ -302,14 +412,23 @@ module OpenCodeCompat
raise PromotionError, "#{label} evidence resolves outside evidence/"
end
document = parse_json(File.read(real_path), relative.to_s)
contents = File.binread(real_path)
actual_sha256 = "sha256:#{Digest::SHA256.hexdigest(contents)}"
if expected_sha256 && expected_sha256 != actual_sha256
raise PromotionError,
"#{label} evidence #{relative} has sha256=#{actual_sha256.inspect}; " \
"expected #{expected_sha256.inspect}"
end
document = parse_json(contents, relative.to_s)
unless document.is_a?(Hash) && document["schema_version"] == 1
raise PromotionError, "#{label} evidence #{relative} must use schema_version 1"
end
validate_immutable_fields!(document, "#{label} evidence #{relative}")
expected = {
"consumer" => consumer,
"profile" => profile,
"consumer_commit" => consumer_commit,
"consumer_commit" => tuple.fetch("consumer_commit"),
"status" => status,
"certified_at" => certified_at,
"tuple_sha256" => tuple_fingerprint
@@ -321,14 +440,20 @@ module OpenCodeCompat
"#{label} evidence #{relative} has #{key}=#{document[key].inspect}; expected #{value.inspect}"
end
relative.to_s
validate_evidence_tuple_details!(document, tuple, "#{label} evidence #{relative}")
{
"path" => relative.to_s,
"sha256" => actual_sha256
}
rescue Errno::ENOENT
raise PromotionError, "#{label} evidence does not exist: #{reference}"
raise PromotionError, "#{label} evidence does not exist: #{reference_path || reference}"
end
def validate_tuple!(tuple, label)
raise PromotionError, "#{label} tuple must be an object" unless tuple.is_a?(Hash)
validate_immutable_fields!(tuple, label)
validate_full_commit!(tuple["consumer_commit"], "#{label} consumer commit")
validate_client!(tuple["opencode_ruby"], "#{label} opencode_ruby", required: true)
validate_client!(tuple["opencode_rails"], "#{label} opencode_rails", required: false)
@@ -366,8 +491,10 @@ module OpenCodeCompat
raise PromotionError, "#{label} #{key} must be an immutable image@sha256 digest, not a tag"
end
exact_execution_coordinate = true if %w[image registry_ref].include?(key)
elsif key == "source_commit"
validate_full_commit!(value, "#{label} source commit")
elsif key.end_with?("_commit")
next if value.nil? && NULLABLE_COMMIT_KEYS.include?(key)
validate_full_commit!(value, "#{label} #{key.tr('_', ' ')}")
end
end
@@ -382,6 +509,154 @@ module OpenCodeCompat
(key.end_with?("_ref") && key != "tag_provenance")
end
def structured_evidence_reference?(reference)
reference.is_a?(Hash) && reference.keys.sort == EVIDENCE_REFERENCE_KEYS
end
def evidence_reference_parts!(reference, label)
if reference.is_a?(String)
unless !reference.empty?
raise PromotionError, "#{label} evidence references must contain a non-empty path"
end
return [reference, nil]
end
unless structured_evidence_reference?(reference)
raise PromotionError,
"#{label} evidence references must be a path string or an object containing only path and sha256"
end
path = reference["path"]
sha256 = reference["sha256"]
unless path.is_a?(String) && !path.empty?
raise PromotionError, "#{label} evidence references must contain a non-empty path"
end
unless sha256.is_a?(String) && sha256.match?(DIGEST)
raise PromotionError, "#{label} evidence reference sha256 must be an exact sha256 digest"
end
[path, sha256]
end
def validate_evidence_tuple_details!(document, tuple, label)
if document.key?("consumer_tree")
expected_tree = tuple["consumer_tree"]
unless expected_tree && document["consumer_tree"] == expected_tree
raise PromotionError,
"#{label} has consumer_tree=#{document['consumer_tree'].inspect}; " \
"expected #{expected_tree.inspect}"
end
end
validate_evidence_clients!(document["clients"], tuple, label) if document.key?("clients")
validate_evidence_runtime!(document["runtime"], tuple.fetch("runtime"), label) if document.key?("runtime")
end
def validate_evidence_clients!(clients, tuple, label)
unless clients.is_a?(Hash)
raise PromotionError, "#{label} clients must be an object"
end
%w[opencode_ruby opencode_rails].each do |client_name|
next unless clients.key?(client_name)
evidence_client = clients[client_name]
tuple_client = tuple[client_name]
if evidence_client.nil?
unless tuple_client.nil?
raise PromotionError, "#{label} #{client_name} is null but the tuple declares a client"
end
next
end
unless evidence_client.is_a?(Hash) && tuple_client.is_a?(Hash)
raise PromotionError, "#{label} #{client_name} must match the tuple client"
end
unless evidence_client["version"].is_a?(String) && !evidence_client["version"].empty?
raise PromotionError, "#{label} #{client_name} must include a non-empty version"
end
commits = %w[commit git_commit].filter_map do |key|
evidence_client[key] if evidence_client.key?(key)
end
if commits.empty?
raise PromotionError, "#{label} #{client_name} must include commit or git_commit"
end
compare_evidence_coordinate!(
evidence_client["version"],
tuple_client["version"],
"#{label} #{client_name}.version"
)
commits.each do |commit|
compare_evidence_coordinate!(commit, tuple_client["git_commit"], "#{label} #{client_name}.commit")
end
end
end
def validate_evidence_runtime!(runtime, tuple_runtime, label)
unless runtime.is_a?(Hash)
raise PromotionError, "#{label} runtime must be an object"
end
runtime.each do |key, value|
tuple_key = EVIDENCE_RUNTIME_KEY_MAP.fetch(key, key)
next unless EVIDENCE_RUNTIME_COORDINATE_KEYS.include?(tuple_key)
unless tuple_runtime.key?(tuple_key)
raise PromotionError, "#{label} runtime.#{key} is not present in the certified tuple"
end
compare_evidence_coordinate!(value, tuple_runtime[tuple_key], "#{label} runtime.#{key}")
end
end
def compare_evidence_coordinate!(actual, expected, label)
return if actual == expected
raise PromotionError, "#{label}=#{actual.inspect}; expected #{expected.inspect}"
end
def validate_immutable_fields!(value, label, path = [])
case value
when Hash
value.each do |key, child|
field_path = path + [key]
field_label = "#{label} #{field_path.join('.')}"
if key == "commit" || key.end_with?("_commit") || key == "head_sha" || key.end_with?("_head_sha")
unless child.nil? && NULLABLE_COMMIT_KEYS.include?(key)
validate_full_commit!(child, field_label)
end
elsif key == "tree" || key.end_with?("_tree")
validate_full_oid!(child, field_label)
elsif key == "annotated_tag_object" || key.end_with?("_tag_object")
validate_full_oid!(child, field_label)
elsif digest_field?(key)
unless child.is_a?(String) && child.match?(DIGEST)
raise PromotionError, "#{field_label} must be an exact sha256 digest"
end
end
validate_immutable_fields!(child, label, field_path)
end
when Array
value.each_with_index { |child, index| validate_immutable_fields!(child, label, path + [index]) }
end
end
def digest_field?(key)
key == "sha256" || key.end_with?("_sha256") ||
key == "digest" || key.end_with?("_digest") ||
key == "docker_image_id" || key.end_with?("_image_id")
end
def validate_full_oid!(value, label)
return if value.is_a?(String) && value.match?(FULL_COMMIT)
raise PromotionError, "#{label} must be a full 40-character lowercase Git object ID"
end
def validate_full_commit!(value, label)
return if value.is_a?(String) && value.match?(FULL_COMMIT)
@@ -455,6 +730,61 @@ module OpenCodeCompat
end
end
def all_consumers_bootstrapped?(manifest)
manifest.fetch("consumers").all? do |consumer, entry|
next false unless entry["candidate"].nil? && entry["previous"].nil?
next false unless entry.dig("rollback_state", "status") == "degraded-no-certified-previous"
next false unless NON_CERTIFIABLE_TUPLE_STATUSES.include?(entry.dig("emergency_provenance", "status"))
profile = fetch_profile!(entry, consumer)
tuple = entry["current"]
next false unless tuple.is_a?(Hash) && tuple["status"] == "certified"
validate_tuple!(tuple, "#{consumer} current")
validate_recorded_certification!(
tuple,
consumer: consumer,
profile: profile,
expected_fingerprint: tuple_fingerprint(tuple, consumer: consumer, profile: profile),
label: "#{consumer} current"
)
true
rescue PromotionError
false
end
end
def migration_state_for(manifest)
return "certified" if all_consumers_certified?(manifest)
return "bootstrap-current-only" if all_consumers_bootstrapped?(manifest)
"candidate"
end
def refresh_promotion_state!(manifest)
manifest["migration_state"] = migration_state_for(manifest)
manifest["promotion_readiness"] = case manifest.fetch("migration_state")
when "certified"
{
"status" => "certified",
"reason" => "Every consumer has exact current and previous passing tuples.",
"required_action" => "Promote only a newly certified, materially changed tuple."
}
when "bootstrap-current-only"
{
"status" => "bootstrap-current-only",
"reason" => "Every current tuple is certified, but no independently passing previous tuple exists yet.",
"required_action" => "Certify the next meaningful release so normal promotion retains the current tuple as previous."
}
else
{
"status" => "candidate",
"reason" => "At least one consumer transition remains incomplete.",
"required_action" => "Finish exact tuple certification without treating failed emergency provenance as rollback evidence."
}
end
end
def with_current_manifest_lock
loop do
retry_with_new_inode = false

View File

@@ -5,7 +5,9 @@
"publication_state": "unpublished",
"opencode_ruby_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"opencode_rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"certification_status": "pending"
"certification_status": "certified",
"certified_at": "2026-07-20T07:36:06Z",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"public_ci": [
{
@@ -16,7 +18,20 @@
"consumers": ["travelwolf"],
"profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn"],
"certification_status": "pending",
"certification_scope": "shared-client-contract-only",
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:50Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
@@ -34,7 +49,20 @@
"consumers": ["opencode-ajent"],
"profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["plugin-ledger"],
"certification_status": "pending",
"certification_scope": "shared-client-contract-only",
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:55Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
@@ -52,7 +80,20 @@
"consumers": ["ajent-rails"],
"profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"],
"certification_status": "pending",
"certification_scope": "shared-client-contract-only",
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:48Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
@@ -65,42 +106,87 @@
],
"host_canary": [
{
"id": "ajent-blackline-alpha7-canary",
"id": "ajent-rails-alpha8-production",
"consumer": "ajent-rails",
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
"tag_provenance": "ajent-opencode-blackline-canary:96552893",
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
"source_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"application_image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"product_images": {
"aigl": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3"
},
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
"profiles": ["rails-persisted-turn"],
"certification_status": "pending",
"profiles": ["rails-persisted-turn", "plugin-ledger"],
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"certified_at": "2026-07-20T08:15:17Z",
"evidence": "evidence/2026-07-20-ajent-rails-alpha8-production.json"
},
"previous_certification": {
"status": "candidate-certified",
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"certified_at": "2026-07-18T21:58:00Z",
"persisted_final_text": "compat-ok",
"model_request_count": 1
"consumer_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"certified_at": "2026-07-20T08:14:53Z",
"evidence": "evidence/2026-07-20-ajent-rails-alpha7-rollback.json"
}
},
{
"id": "mushu-permission-shortcuts-alpha7-canary",
"id": "travelwolf-alpha8-production",
"consumer": "travelwolf",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:031371e20f34e1014712ddc668b4e182483664bb70f116b683c0ec83d6668338",
"reported_version": "1.16.1",
"profiles": ["rails-persisted-turn"],
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"certified_at": "2026-07-20T06:51:48Z",
"evidence": "evidence/2026-07-20-travelwolf-alpha8-production.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"certified_at": "2026-07-20T06:31:42Z",
"evidence": "evidence/2026-07-20-travelwolf-alpha7-rollback.json"
}
},
{
"id": "mushu-permission-shortcuts-alpha8-production",
"consumer": "mushu",
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:a16bf0c4cd17698eba02244f6ba0e429b2238b6455e1415ff69fb08965c863a7",
"application_image": "docker-registry:5000/mushu@sha256:30f204184520d504c7c384c7f6e503fa3547217aaaef2f1d0d65884533bdeacd",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": "521185613a864a16a76a21c7a60597ab3973bae9",
"reported_version": "0.0.0-permission-shortcuts-202606300721",
"profiles": ["ruby-rest-sse"],
"certification_status": "pending",
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047",
"certified_at": "2026-07-20T07:13:51Z",
"evidence": "evidence/2026-07-20-mushu-alpha8-production.json"
},
"previous_certification": {
"status": "candidate-certified",
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"certified_at": "2026-07-18T21:53:00Z",
"persisted_final_text": "compat-ok",
"model_request_count": 1
"consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"certified_at": "2026-07-20T06:39:46.921805Z",
"evidence": "evidence/2026-07-20-mushu-alpha7-rollback.json"
}
}
]

View File

@@ -1,16 +1,200 @@
{
"schema_version": 1,
"migration_state": "candidate",
"migration_state": "certified",
"promotion_readiness": {
"status": "blocked",
"reason": "Every observed alpha2 baseline fails the hardened exact-final-text contract, so none can be certified as the previous rollback tuple.",
"evidence": "evidence/2026-07-18-opencode-ruby-alpha2-regression.json",
"required_action": "Create and canary a distinct rollback consumer commit that pins alpha7 and the exact runtime before promoting any candidate."
"status": "certified",
"reason": "Every consumer has exact current and previous passing tuples.",
"required_action": "Promote only a newly certified, materially changed tuple."
},
"consumers": {
"ajent-rails": {
"profile": "rails-persisted-turn",
"current": {
"status": "certified",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"consumer_tree": "a1e044f3a4f5c5ac0510a1d998c4afb55286da5a",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"git_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"application_image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"build_source_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"application_canary": {
"checked_at": "2026-07-20T08:15:17Z",
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"forge_ci": {
"pr_run_id": "2718",
"main_build_run_id": "2719",
"protected_main_run_id": "2720",
"deploy_job_id": "12065",
"status": "pass"
},
"production": {
"checked_at": "2026-07-20T08:15:17Z",
"application_health_status": 200,
"blackline_health_status": 200,
"ajent_public_status": 308,
"voice_contract_status": "pass",
"scotus_contract_status": "pass",
"status": "pass"
},
"source_availability": {
"origin_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"backup_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"github_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"status": "pass"
},
"compatibility_certified_at": "2026-07-20T08:15:17Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T08:15:17Z",
"tuple_sha256": "sha256:326a87cdb67061bdd36dfcee989ae149511a0c627e857488d59e0f2b3c796720",
"evidence": [
{
"path": "evidence/2026-07-20-ajent-rails-alpha8-production.json",
"sha256": "sha256:1db57a6df214d03a3466ce8668c773723e733f00d08d072dfa55206c3e8b09a1"
}
]
}
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"consumer_tree": "29e6cd35313a06cef6f9c1f40a36e3ee582fd035",
"opencode_ruby": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
}
},
"opencode_rails": {
"version": "0.0.1.alpha7",
"git_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
}
},
"runtime": {
"image": "docker-registry:5000/ajent_rails@sha256:fe6fdc2325d304ddb276df7d35807c183e7e574876a0d76a6c6de6014619c76e",
"application_image": "docker-registry:5000/ajent_rails@sha256:fe6fdc2325d304ddb276df7d35807c183e7e574876a0d76a6c6de6014619c76e",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"build_source_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"application_canary": {
"checked_at": "2026-07-20T08:14:53Z",
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"consumer_verification": {
"test_runs": 4247,
"assertions": 15740,
"failures": 0,
"errors": 0,
"skips": 4,
"rubocop_files": 1430,
"status": "pass"
},
"source_availability": {
"origin_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"backup_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"github_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720"
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": true,
"application_rebuild_required": false,
"deployed": false
},
"compatibility_certified_at": "2026-07-20T08:14:53Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T08:14:53Z",
"tuple_sha256": "sha256:ae48333ba453f442d14263a916418a384f576db34d09a503111fc28c340eb408",
"evidence": [
{
"path": "evidence/2026-07-20-ajent-rails-alpha7-rollback.json",
"sha256": "sha256:c3554a346a6585c7f98fde2aee2ad4f06ef4db7b621c98160183274e9e6ddc5c"
}
]
}
},
"emergency_provenance": {
"status": "observed-production-contract-failed",
"consumer_commit": "83eed3c247a352842b08fc5cf339c4d0acdaacba",
"opencode_ruby": {
@@ -27,10 +211,95 @@
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
}
}
},
"travelwolf": {
"profile": "rails-persisted-turn",
"current": {
"status": "certified",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"consumer_tree": "f436a7248d90e5ae9e84c45f264acd913a4162aa",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"git_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:031371e20f34e1014712ddc668b4e182483664bb70f116b683c0ec83d6668338",
"reported_version": "1.16.1"
},
"application_canary": {
"checked_at": "2026-07-20T06:31:13.506Z",
"final_text": "TRAVELWOLF_ALPHA8_CANARY_OK",
"application_prompt_count": 1,
"user_turn_count": 1,
"authoritative_assistant_turn_count": 1,
"child_turn_count": 0,
"model_request_count_observed": false,
"cleanup_status": "pass",
"status": "pass"
},
"forge_ci": {
"status": "pass",
"pr_run_id": "2702",
"main_run_id": "2704"
},
"production": {
"checked_at": "2026-07-20T06:51:48Z",
"application_image_created_at": "2026-07-20T06:42:55.929825054Z",
"internal_health_status": 200,
"authenticated_public_health_status": 200,
"container_status": "running",
"deployment_mode": "manual-isolated-kamal",
"status": "pass"
},
"rollback_operability": {
"certified_previous_consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "8494c52d11bf2995c378398e977d82d06a09e046"
},
"compatibility_certified_at": "2026-07-20T06:51:48Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T06:51:48Z",
"tuple_sha256": "sha256:0321c4246bed6ab62587924a83204a9e2ca7517b1dec5e4aef35dcac400fcc8e",
"evidence": [
{
"path": "evidence/2026-07-20-travelwolf-alpha8-production.json",
"sha256": "sha256:ab8d8de768d415b3a68090507fb6ec40bdfad439fcaf0471beea4f52450e9d2f"
}
]
}
},
"candidate": {
"status": "compatibility-certified",
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
"immediate_platform_rollback": {
"status": "uncertified-known-contract-mismatch",
"consumer_commit": "8494c52d11bf2995c378398e977d82d06a09e046",
"opencode_ruby": {
"version": "0.0.1.alpha6",
"git_commit": "9356268fb339269cd53763435af7ce88df1eeec7"
},
"opencode_rails": {
"version": "0.0.1.alpha6",
"git_commit": "8d1eb7701fe9e24baa64c16d6af03c6ed1f423eb"
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:4291b256b37eeb694435093655db17154c837e53335b8d0eb8f1b07a744e856e",
"reported_version": "1.16.1"
},
"reason": "This retained application image is the platform's one-click target, but its alpha6 clients are not the certified alpha7 previous tuple."
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"consumer_tree": "d0e47b2d90ace87c6cc9c4b94b2f22987441c1f6",
"opencode_ruby": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
@@ -48,26 +317,44 @@
}
},
"runtime": {
"tag_provenance": "ajent-opencode-blackline-canary:96552893",
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
"source_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1"
},
"application_canary": {
"checked_at": "2026-07-18T21:58:00Z",
"path": "Blackline::GenerateResponseJob",
"persisted_final_text": "compat-ok",
"model_request_count": 1,
"checked_at": "2026-07-20T06:22:36.958Z",
"final_text": "TRAVELWOLF_ALPHA7_CANARY_OK",
"application_prompt_count": 1,
"user_turn_count": 1,
"authoritative_assistant_turn_count": 1,
"child_turn_count": 0,
"model_request_count_observed": false,
"cleanup_status": "pass",
"status": "pass"
},
"certified_at": "2026-07-18T22:03:14Z"
"forge_ci": {
"status": "pass",
"pr_run_id": "2696",
"main_run_id": "2698"
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true
},
"compatibility_certified_at": "2026-07-20T06:31:42Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T06:31:42Z",
"tuple_sha256": "sha256:20ecec3d2c3743efcd8575e71ec46dd7b86c2865e472511d16a96d57ddb8b40c",
"evidence": [
{
"path": "evidence/2026-07-20-travelwolf-alpha7-rollback.json",
"sha256": "sha256:d5e36ffc40f44ff28cbd7eee7bf209fa595ce57619081a76f8d049499b5d30a4"
}
]
}
},
"previous": null
},
"travelwolf": {
"profile": "rails-persisted-turn",
"current": {
"emergency_provenance": {
"status": "observed-production-contract-failed",
"consumer_commit": "1ca756d64f058c9e0379f69fc4a3d53330cfcd15",
"opencode_ruby": {
@@ -82,10 +369,110 @@
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1"
}
}
},
"mushu": {
"profile": "ruby-rest-sse",
"current": {
"status": "certified",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047",
"consumer_tree": "726c3878e71f7a4cf8c0b8a3fa8fc15382bbab45",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-521185613a864a16a76a21c7a60597ab3973bae9",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:a16bf0c4cd17698eba02244f6ba0e429b2238b6455e1415ff69fb08965c863a7",
"application_image": "docker-registry:5000/mushu@sha256:30f204184520d504c7c384c7f6e503fa3547217aaaef2f1d0d65884533bdeacd",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": "521185613a864a16a76a21c7a60597ab3973bae9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"preproduction_canary": {
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 2,
"mcp_context_count": 2,
"database_sha256": "sha256:7fbe32ff9621196a2504aeba5eca78cb0c9c670edd7a1168b1f81f667872e2b6",
"log_sha256": "sha256:51b698fcc8b7bce68e79cbae10997e19eb47a88985cbe24871d10740dda26225",
"status": "pass"
},
"application_canary": {
"started_at": "2026-07-20T07:03:17Z",
"checked_at": "2026-07-20T07:04:51.376351Z",
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 1,
"mcp_context_count": 1,
"status": "pass"
},
"forge_ci": {
"status": "pass",
"pr_run_id": "2700",
"main_run_id": "2705"
},
"production": {
"checked_at": "2026-07-20T07:13:20Z",
"web_health_status": 200,
"status_health_status": 200,
"runtime_readiness": "ready",
"containers": [
"web",
"calendar",
"telegram",
"opencode"
],
"deployment_mode": "manual",
"status": "pass"
},
"rollback_operability": {
"certified_previous_consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922"
},
"compatibility_certified_at": "2026-07-20T07:13:51Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T07:13:51Z",
"tuple_sha256": "sha256:804410a63cf2b6e4056d6dd9ce289ac72464f441c9785b6fc8dd50016263a1a8",
"evidence": [
{
"path": "evidence/2026-07-20-mushu-alpha8-production.json",
"sha256": "sha256:786bf92a02588b951b5b7f7a21ed915b7ac1120538c5f894af027c190379170f"
}
]
}
},
"candidate": {
"status": "compatibility-certified",
"consumer_commit": "19747b59fa21e12560afd9edd73ed674ab3a644c",
"immediate_platform_rollback": {
"status": "uncertified-known-contract-failure",
"consumer_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922",
"opencode_ruby": {
"version": "0.0.1.alpha2",
"git_commit": "889d38332f98f5f7b76d16952b7204d8a5b9a662"
},
"opencode_rails": null,
"runtime": {
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"application_image": null,
"application_image_provenance": "not-retained",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"reason": "The one-click application image is not retained and the alpha2 client failed the contract; use the certified alpha7 previous source tuple instead."
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"consumer_tree": "f43f48972ab444310508e9067831dfc6977f1a24",
"opencode_ruby": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
@@ -94,34 +481,56 @@
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
}
},
"opencode_rails": {
"version": "0.0.1.alpha7",
"git_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
}
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1"
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": null,
"provenance_strength": "one-time-tag-source-attestation",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"application_canary": {
"run_id": "20260718215135-8d8102c0",
"checked_at": "2026-07-18T21:51:56.552Z",
"final_text": "TRAVELWOLF_ALPHA7_SUBSCRIBE_READY",
"application_prompt_count": 1,
"model_request_count_observed": false,
"cleanup_status": "pass",
"started_at": "2026-07-20T06:37:55Z",
"checked_at": "2026-07-20T06:39:46.921805Z",
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 7,
"mcp_context_count": 7,
"database_sha256": "sha256:a567d2ff9a906c9b78831bb6fa43153af539f1e76ee63bf35961472f55651d74",
"log_sha256": "sha256:a35a83b003a1f082b8a721b3faad8b35cb71dc9d1ceaedfc3cec3b26916e85b9",
"status": "pass"
},
"certified_at": "2026-07-18T22:03:14Z"
"forge_ci": {
"status": "not_run",
"run_ids": [],
"reason": "Frozen exact head predates the runner-only system-test workflow repair; clean-worktree application canary is authoritative."
},
"source_availability": {
"origin_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719",
"backup_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719"
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true
},
"compatibility_certified_at": "2026-07-20T06:39:46.921805Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T06:39:46.921805Z",
"tuple_sha256": "sha256:8d7d3cb2d3e2d920c900a9c1ada365026872904588cff0b166903d1d6d32183f",
"evidence": [
{
"path": "evidence/2026-07-20-mushu-alpha7-rollback.json",
"sha256": "sha256:143deb6b400a05305991c956eaf80f07fdee76a9cce98af96f8eee395ed9847d"
}
]
}
},
"previous": null
},
"mushu": {
"profile": "ruby-rest-sse",
"current": {
"emergency_provenance": {
"status": "observed-production-contract-failed",
"consumer_commit": "fe16e5bab028e3c92cd5508ef2cebd9bfa23386f",
"opencode_ruby": {
@@ -134,34 +543,7 @@
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
}
},
"candidate": {
"status": "compatibility-certified",
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
"opencode_ruby": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
}
},
"runtime": {
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"application_canary": {
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
"checked_at": "2026-07-18T21:53:00Z",
"persisted_final_text": "compat-ok",
"model_request_count": 1,
"status": "pass"
},
"certified_at": "2026-07-18T22:03:14Z"
},
"previous": null
}
}
}
}

View File

@@ -20,6 +20,10 @@ parser = OptionParser.new do |opts|
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
[--previous-status pass --previous-certified-at TIMESTAMP \\
--previous-evidence evidence/FILE.json] [--dry-run]
ruby scripts/promote_runtime_tuple.rb bootstrap-current --consumer NAME --consumer-commit SHA \\
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
--acknowledge-degraded-rollback \\
accept-degraded-rollback-with-failed-emergency-provenance [--dry-run]
USAGE
opts.on("--consumer NAME") { |value| options["consumer"] = value }
@@ -30,6 +34,9 @@ parser = OptionParser.new do |opts|
opts.on("--previous-status STATUS") { |value| options["previous_status"] = value }
opts.on("--previous-certified-at TIMESTAMP") { |value| options["previous_certified_at"] = value }
opts.on("--previous-evidence PATH") { |value| options["previous_evidence"] << value }
opts.on("--acknowledge-degraded-rollback VALUE") do |value|
options["degraded_rollback_acknowledgement"] = value
end
opts.on("--dry-run") { options["dry_run"] = true }
end
@@ -94,8 +101,42 @@ begin
"migration_state" => promoted.fetch("migration_state")
)
end
when "bootstrap-current"
required!(
options,
"consumer",
"consumer_commit",
"status",
"certified_at",
"evidence",
"degraded_rollback_acknowledgement"
)
bootstrapped = promoter.bootstrap_current(
consumer: options.fetch("consumer"),
consumer_commit: options.fetch("consumer_commit"),
certification: {
"status" => options.fetch("status"),
"certified_at" => options.fetch("certified_at"),
"evidence" => options.fetch("evidence")
},
acknowledgement: options.fetch("degraded_rollback_acknowledgement"),
dry_run: options.fetch("dry_run", false)
)
if options.fetch("dry_run", false)
puts JSON.pretty_generate(bootstrapped)
else
consumer = bootstrapped.fetch("consumers").fetch(options.fetch("consumer"))
puts JSON.pretty_generate(
"consumer" => options.fetch("consumer"),
"current_consumer_commit" => consumer.dig("current", "consumer_commit"),
"current_tuple_sha256" => consumer.dig("current", "certification", "tuple_sha256"),
"previous" => consumer.fetch("previous"),
"rollback_state" => consumer.fetch("rollback_state"),
"migration_state" => bootstrapped.fetch("migration_state")
)
end
else
raise OptionParser::InvalidArgument, "command must be fingerprint or promote"
raise OptionParser::InvalidArgument, "command must be fingerprint, promote, or bootstrap-current"
end
rescue OpenCodeCompat::PromotionError, OptionParser::ParseError => e
warn "error: #{e.message}"

View File

@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "json"
require "digest"
require "minitest/autorun"
require_relative "../lib/opencode_compat/client_candidate"
require_relative "../lib/opencode_compat/runtime_tuple_promoter"
@@ -48,7 +49,14 @@ class RepositoryTest < Minitest::Test
assert_equal ["ruby-rest-sse"], target.fetch("profiles")
assert_equal "shared-client-contract-only",
target.fetch("certification_scope", "shared-client-contract-only")
assert_equal "pending", target.fetch("certification_status")
assert_equal "certified", target.fetch("certification_status")
current = target.fetch("current_certification")
assert_equal "certified", current.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("client_commit"))
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("rails_commit"))
assert_equal current.fetch("expected_text"), current.fetch("full_text")
assert_equal 1, current.fetch("llm_request_count")
assert_path_exists File.join(ROOT, current.fetch("evidence"))
previous = target["previous_certification"]
next unless previous
@@ -88,22 +96,29 @@ class RepositoryTest < Minitest::Test
assert_equal clients.fetch("opencode-ruby").fetch("ref"), dependency.fetch("ref")
end
def test_image_matrix_is_pending_and_bound_to_the_exact_client_candidate
def test_image_matrix_is_certified_and_bound_to_the_exact_unpublished_client_candidate
candidate = json("manifests/client-candidate.json")
clients = candidate.fetch("clients")
matrix_candidate = json("manifests/image-matrix.json").fetch("client_candidate")
assert_equal "pending", matrix_candidate.fetch("certification_status")
assert_equal "certified", matrix_candidate.fetch("certification_status")
assert_equal "unpublished", matrix_candidate.fetch("publication_state")
assert_path_exists File.join(ROOT, matrix_candidate.fetch("evidence"))
assert_equal candidate.fetch("release_train"), matrix_candidate.fetch("release_train")
assert_equal candidate.fetch("publication_state"), matrix_candidate.fetch("publication_state")
assert_equal clients.dig("opencode-ruby", "ref"), matrix_candidate.fetch("opencode_ruby_commit")
assert_equal clients.dig("opencode-rails", "ref"), matrix_candidate.fetch("opencode_rails_commit")
json("manifests/image-matrix.json").fetch("host_canary").each do |target|
assert_equal "pending", target.fetch("certification_status")
assert_equal "certified", target.fetch("certification_status")
current = target.fetch("current_certification")
assert_equal "certified", current.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("client_commit"))
assert_path_exists File.join(ROOT, current.fetch("evidence"))
previous = target.fetch("previous_certification")
assert_includes %w[candidate-certified certified], previous.fetch("status")
assert_equal "certified", previous.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, previous.fetch("client_commit"))
assert_path_exists File.join(ROOT, previous.fetch("evidence"))
end
end
@@ -129,6 +144,32 @@ class RepositoryTest < Minitest::Test
assert evidence.fetch("limitations").any? { |entry| entry.include?("remain unverified") }
end
def test_alpha8_ci_evidence_certifies_shared_and_lockstep_scopes_without_claiming_apps
evidence = json("evidence/2026-07-20-opencode-alpha8-shared-client-ci.json")
candidate = json("manifests/client-candidate.json").fetch("clients")
assert_equal "pass", evidence.fetch("status")
assert_equal "unpublished", evidence.fetch("publication_state")
assert_equal candidate.dig("opencode-ruby", "ref"), evidence.dig("clients", "opencode_ruby", "commit")
assert_equal candidate.dig("opencode-rails", "ref"), evidence.dig("clients", "opencode_rails", "commit")
assert_equal "29725021192", evidence.dig("github_workflow", "run_id")
assert_equal "2717", evidence.dig("gitea_workflow", "run_id")
assert_equal false, evidence.dig("gitea_workflow", "artifact_evidence_claimed")
assert_operator evidence.fetch("certified_at"), :>=, evidence.dig("github_workflow", "completed_at")
assert_operator evidence.fetch("certified_at"), :>=, evidence.dig("gitea_workflow", "completed_at")
assert_equal %w[3.2.11 3.3.12 3.4.10 4.0.6],
evidence.fetch("rails_lockstep").map { |entry| entry.fetch("ruby_runtime_version") }
assert evidence.fetch("rails_lockstep").all? { |entry| entry.fetch("status") == "pass" }
assert_equal 3, evidence.fetch("exact_image_contracts").length
evidence.fetch("exact_image_contracts").each do |contract|
assert_equal "pass", contract.fetch("status")
assert_equal contract.fetch("expected_text"), contract.fetch("full_text")
assert_equal 1, contract.fetch("authoritative_assistant_message_count")
assert_equal 1, contract.fetch("llm_request_count")
end
assert evidence.fetch("limitations").any? { |entry| entry.include?("application profile") }
end
def test_certified_migration_keeps_previous_tuple
tuples = json("manifests/runtime-tuples.json")
return unless tuples.fetch("migration_state") == "certified"
@@ -155,7 +196,7 @@ class RepositoryTest < Minitest::Test
def test_runtime_tuples_use_full_commits_and_no_mutable_registry_coordinate
tuples = json("manifests/runtime-tuples.json")
tuples.fetch("consumers").each_value do |consumer|
%w[current candidate previous].each do |slot|
%w[current candidate previous emergency_provenance].each do |slot|
tuple = consumer[slot]
next unless tuple
@@ -176,37 +217,83 @@ class RepositoryTest < Minitest::Test
end
end
def test_failed_alpha2_baselines_block_promotion_instead_of_becoming_previous
def test_failed_alpha2_baselines_remain_uncertified_emergency_provenance
tuples = json("manifests/runtime-tuples.json")
readiness = tuples.fetch("promotion_readiness")
assert_equal "blocked", readiness.fetch("status")
assert_path_exists File.join(ROOT, readiness.fetch("evidence"))
assert_equal "certified", tuples.fetch("migration_state")
assert_equal "certified", readiness.fetch("status")
tuples.fetch("consumers").each_value do |consumer|
assert_equal "observed-production-contract-failed", consumer.dig("current", "status")
assert_nil consumer.fetch("previous")
emergency = consumer.fetch("emergency_provenance")
assert_equal "observed-production-contract-failed", emergency.fetch("status")
refute emergency.key?("certification")
assert_equal "certified", consumer.dig("current", "status")
assert_equal "0.0.1.alpha8", consumer.dig("current", "opencode_ruby", "version")
assert_equal "certified", consumer.dig("previous", "status")
assert_equal "0.0.1.alpha7", consumer.dig("previous", "opencode_ruby", "version")
refute consumer.key?("rollback_state")
end
end
def test_candidate_evidence_is_bound_to_complete_tuple_fingerprints
def test_platform_one_click_targets_are_explicitly_uncertified_and_separate
consumers = json("manifests/runtime-tuples.json").fetch("consumers")
%w[travelwolf mushu].each do |name|
rollback = consumers.fetch(name).fetch("immediate_platform_rollback")
assert_match(/\Auncertified-/, rollback.fetch("status"))
assert_match(/\A[0-9a-f]{40}\z/, rollback.fetch("consumer_commit"))
assert_match(/\A[0-9a-f]{40}\z/, rollback.dig("opencode_ruby", "git_commit"))
refute rollback.key?("certification")
refute_equal consumers.dig(name, "previous", "consumer_commit"), rollback.fetch("consumer_commit")
end
travelwolf_runtime = consumers.dig("travelwolf", "immediate_platform_rollback", "runtime")
assert_match(/@sha256:[0-9a-f]{64}\z/, travelwolf_runtime.fetch("image"))
assert_match(/\Asha256:[0-9a-f]{64}\z/, travelwolf_runtime.fetch("application_image_id"))
mushu_runtime = consumers.dig("mushu", "immediate_platform_rollback", "runtime")
assert_match(/@sha256:[0-9a-f]{64}\z/, mushu_runtime.fetch("registry_ref"))
assert_nil mushu_runtime.fetch("application_image")
assert_equal "not-retained", mushu_runtime.fetch("application_image_provenance")
end
def test_certified_tuple_evidence_is_bound_to_complete_tuple_fingerprints
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
evidence_paths = {
"ajent-rails" => "evidence/2026-07-18-ajent-rails-alpha7-candidate.json",
"travelwolf" => "evidence/2026-07-18-travelwolf-alpha7-candidate.json",
"mushu" => "evidence/2026-07-18-mushu-alpha7-candidate.json"
}
evidence_paths.each do |consumer, path|
evidence = json(path)
fingerprints = promoter.fingerprints(
consumer: consumer,
consumer_commit: evidence.fetch("consumer_commit")
)
json("manifests/runtime-tuples.json").fetch("consumers").each do |consumer, entry|
profile = entry.fetch("profile")
%w[current previous].each do |slot|
tuple = entry.fetch(slot)
fingerprint = promoter.send(:tuple_fingerprint, tuple, consumer: consumer, profile: profile)
certification = tuple.fetch("certification")
promoter.send(
:validate_recorded_certification!,
tuple,
consumer: consumer,
profile: profile,
expected_fingerprint: fingerprint,
label: "#{consumer} #{slot}"
)
assert_equal consumer, evidence.fetch("consumer")
assert_equal fingerprints.fetch("profile"), evidence.fetch("profile")
assert_equal fingerprints.fetch("candidate_tuple_sha256"), evidence.fetch("tuple_sha256")
assert_equal "pass", evidence.fetch("status")
assert_equal "certified", tuple.fetch("status")
assert_equal "pass", certification.fetch("status")
assert_equal fingerprint, certification.fetch("tuple_sha256")
refute_empty certification.fetch("evidence")
certification.fetch("evidence").each do |reference|
assert_equal %w[path sha256], reference.keys.sort
path = reference.fetch("path")
bytes = File.binread(File.join(ROOT, path))
assert_equal "sha256:#{Digest::SHA256.hexdigest(bytes)}", reference.fetch("sha256")
evidence = json(path)
assert_equal consumer, evidence.fetch("consumer")
assert_equal profile, evidence.fetch("profile")
assert_equal tuple.fetch("consumer_commit"), evidence.fetch("consumer_commit")
assert_equal certification.fetch("certified_at"), evidence.fetch("certified_at")
assert_equal fingerprint, evidence.fetch("tuple_sha256")
assert_equal "pass", evidence.fetch("status")
end
end
end
end

View File

@@ -10,14 +10,22 @@ class RuntimeTuplePromoterTest < Minitest::Test
CONSUMER = "example"
CURRENT_COMMIT = "1" * 40
CANDIDATE_COMMIT = "2" * 40
NEXT_COMMIT = "7" * 40
RUBY_CURRENT = "3" * 40
RUBY_CANDIDATE = "4" * 40
RUBY_NEXT = "8" * 40
RAILS_CURRENT = "5" * 40
RAILS_CANDIDATE = "6" * 40
RAILS_NEXT = "9" * 40
CURRENT_TREE = "a" * 40
CANDIDATE_TREE = "b" * 40
NEXT_TREE = "c" * 40
CURRENT_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'a' * 64}"
CANDIDATE_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'b' * 64}"
NEXT_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'c' * 64}"
CURRENT_TIME = "2026-07-17T12:00:00Z"
CANDIDATE_TIME = "2026-07-18T12:00:00Z"
NEXT_TIME = "2026-07-19T12:00:00Z"
def setup
@root = Dir.mktmpdir("opencode-compat-promotion")
@@ -49,10 +57,12 @@ class RuntimeTuplePromoterTest < Minitest::Test
assert_equal RUBY_CANDIDATE, consumer.dig("current", "opencode_ruby", "git_commit")
assert_equal "certified", consumer.dig("current", "status")
assert_equal candidate.fetch("tuple_sha256"), consumer.dig("current", "certification", "tuple_sha256")
assert_equal evidence_reference(candidate), consumer.dig("current", "certification", "evidence", 0)
assert_equal CURRENT_COMMIT, consumer.dig("previous", "consumer_commit")
assert_equal RUBY_CURRENT, consumer.dig("previous", "opencode_ruby", "git_commit")
assert_equal "certified", consumer.dig("previous", "status")
assert_equal previous.fetch("tuple_sha256"), consumer.dig("previous", "certification", "tuple_sha256")
assert_equal evidence_reference(previous), consumer.dig("previous", "certification", "evidence", 0)
assert_equal "certified", promoted.fetch("migration_state")
assert_equal promoted, JSON.parse(File.read(manifest_path))
end
@@ -121,6 +131,41 @@ class RuntimeTuplePromoterTest < Minitest::Test
assert_match(/full 40-character/, error.message)
end
def test_rejects_malformed_immutable_tuple_coordinates_but_allows_unknown_build_source
mutations = {
"consumer tree" => lambda { |candidate| candidate["consumer_tree"] = "abc123" },
"build source commit" => lambda { |candidate| candidate.fetch("runtime")["build_source_commit"] = "abc123" },
"rollback commit" => lambda do |candidate|
candidate["rollback_operability"] = {"immediate_platform_rollback_commit" => "abc123"}
end,
"artifact digest" => lambda do |candidate|
candidate["application_canary"] = {"database_sha256" => "sha256:abc123"}
end
}
mutations.each do |label, mutate|
manifest = valid_manifest
mutate.call(manifest.dig("consumers", CONSUMER, "candidate"))
write_manifest(manifest)
error = assert_raises(OpenCodeCompat::PromotionError, label) do
@promoter.fingerprints(consumer: CONSUMER, consumer_commit: CANDIDATE_COMMIT)
end
assert_match(/full 40-character|exact sha256 digest/, error.message, label)
end
manifest = valid_manifest
manifest.dig("consumers", CONSUMER, "candidate", "runtime")["build_source_commit"] = nil
write_manifest(manifest)
assert_match(
/\Asha256:[0-9a-f]{64}\z/,
@promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT
).fetch("candidate_tuple_sha256")
)
end
def test_rejects_missing_server_version_or_unknown_profile
manifest = valid_manifest
manifest.dig("consumers", CONSUMER, "candidate", "runtime").delete("reported_version")
@@ -175,6 +220,163 @@ class RuntimeTuplePromoterTest < Minitest::Test
assert_equal before, File.binread(manifest_path)
end
def test_explicit_degraded_bootstrap_preserves_failed_emergency_provenance
manifest = valid_manifest
manifest.dig("consumers", CONSUMER, "current")["status"] = "observed-production-contract-failed"
write_manifest(manifest)
candidate_fingerprint = @promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT
).fetch("candidate_tuple_sha256")
evidence = write_evidence(
"bootstrap-candidate.json",
commit: CANDIDATE_COMMIT,
timestamp: CANDIDATE_TIME,
fingerprint: candidate_fingerprint
)
before = File.binread(manifest_path)
preview = @promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, evidence),
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT,
dry_run: true
)
assert_equal before, File.binread(manifest_path)
assert_equal "bootstrap-current-only", preview.fetch("migration_state")
bootstrapped = @promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, evidence),
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT
)
consumer = bootstrapped.dig("consumers", CONSUMER)
assert_equal "certified", consumer.dig("current", "status")
assert_equal CANDIDATE_COMMIT, consumer.dig("current", "consumer_commit")
assert_nil consumer["candidate"]
assert_nil consumer["previous"]
assert_equal "observed-production-contract-failed", consumer.dig("emergency_provenance", "status")
assert_nil consumer.dig("emergency_provenance", "certification")
assert_equal "degraded-no-certified-previous", consumer.dig("rollback_state", "status")
assert_equal "bootstrap-current-only", bootstrapped.dig("promotion_readiness", "status")
assert_equal bootstrapped, JSON.parse(File.read(manifest_path))
end
def test_bootstrap_then_normal_promotion_retains_the_first_passing_tuple_as_previous
manifest = valid_manifest
manifest.dig("consumers", CONSUMER, "current")["status"] = "observed-production-contract-failed"
write_manifest(manifest)
bootstrap_fingerprint = @promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT
).fetch("candidate_tuple_sha256")
bootstrap_evidence = write_evidence(
"bootstrap-rollback.json",
commit: CANDIDATE_COMMIT,
timestamp: CANDIDATE_TIME,
fingerprint: bootstrap_fingerprint
)
@promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, bootstrap_evidence),
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT
)
manifest = JSON.parse(File.read(manifest_path))
manifest.dig("consumers", CONSUMER)["candidate"] = {
"status" => "compatibility-certified",
"certified_at" => NEXT_TIME,
"consumer_commit" => NEXT_COMMIT,
"opencode_ruby" => {"version" => "0.0.1.alpha8", "git_commit" => RUBY_NEXT},
"opencode_rails" => {"version" => "0.0.1.alpha8", "git_commit" => RAILS_NEXT},
"runtime" => {"image" => NEXT_IMAGE, "reported_version" => "1.19.0"}
}
write_manifest(manifest)
next_fingerprint = @promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: NEXT_COMMIT
).fetch("candidate_tuple_sha256")
next_evidence = write_evidence(
"next-current.json",
commit: NEXT_COMMIT,
timestamp: NEXT_TIME,
fingerprint: next_fingerprint
)
promoted = @promoter.promote(
consumer: CONSUMER,
consumer_commit: NEXT_COMMIT,
certification: certification(NEXT_TIME, next_evidence)
)
consumer = promoted.dig("consumers", CONSUMER)
assert_equal NEXT_COMMIT, consumer.dig("current", "consumer_commit")
assert_equal CANDIDATE_COMMIT, consumer.dig("previous", "consumer_commit")
assert_equal "certified", consumer.dig("previous", "status")
assert_equal "observed-production-contract-failed", consumer.dig("emergency_provenance", "status")
refute consumer.key?("rollback_state")
assert_equal "certified", promoted.fetch("migration_state")
assert_equal "certified", promoted.dig("promotion_readiness", "status")
end
def test_degraded_bootstrap_requires_exact_acknowledgement
error = assert_raises(OpenCodeCompat::PromotionError) do
@promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: {},
acknowledgement: "yes",
dry_run: true
)
end
assert_match(/explicit acknowledgement/, error.message)
end
def test_degraded_bootstrap_rejects_a_passing_baseline_or_existing_previous_tuple
candidate, = write_matching_evidence
error = assert_raises(OpenCodeCompat::PromotionError) do
@promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, candidate),
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT,
dry_run: true
)
end
assert_match(/only for a current tuple known to fail/, error.message)
manifest = valid_manifest
manifest.dig("consumers", CONSUMER, "current")["status"] = "observed-production-contract-failed"
manifest.dig("consumers", CONSUMER)["previous"] = manifest.dig("consumers", CONSUMER, "current").dup
write_manifest(manifest)
candidate_fingerprint = @promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT
).fetch("candidate_tuple_sha256")
evidence = write_evidence(
"bootstrap-existing-previous.json",
commit: CANDIDATE_COMMIT,
timestamp: CANDIDATE_TIME,
fingerprint: candidate_fingerprint
)
error = assert_raises(OpenCodeCompat::PromotionError) do
@promoter.bootstrap_current(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, evidence),
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT,
dry_run: true
)
end
assert_match(/cannot replace an existing previous/, error.message)
end
def test_rejects_evidence_that_does_not_match_the_complete_tuple
candidate, previous = write_matching_evidence
manifest = JSON.parse(File.read(manifest_path))
@@ -196,6 +398,114 @@ class RuntimeTuplePromoterTest < Minitest::Test
assert_equal before, File.binread(manifest_path)
end
def test_hash_binds_evidence_and_accepts_a_structured_reference
candidate, previous = write_matching_evidence
structured_candidate = evidence_reference(candidate)
structured_previous = evidence_reference(previous)
promoted = @promoter.promote(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, candidate).merge("evidence" => [structured_candidate]),
previous_certification: certification(CURRENT_TIME, previous).merge("evidence" => [structured_previous]),
dry_run: true
)
assert_equal structured_candidate, promoted.dig("consumers", CONSUMER, "current", "certification", "evidence", 0)
error = assert_raises(OpenCodeCompat::PromotionError) do
@promoter.promote(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, candidate).merge(
"evidence" => [structured_candidate.merge("sha256" => "sha256:#{'0' * 64}")]
),
previous_certification: certification(CURRENT_TIME, previous).merge("evidence" => [structured_previous]),
dry_run: true
)
end
assert_match(/has sha256=.*expected/, error.message)
end
def test_rejects_evidence_coordinates_that_disagree_with_the_tuple
mutations = {
"consumer_tree" => lambda { |document| document["consumer_tree"] = "f" * 40 },
"opencode_ruby.commit" => lambda do |document|
document["clients"] = {
"opencode_ruby" => {"version" => "0.0.1.alpha7", "commit" => "f" * 40}
}
end,
"runtime.image" => lambda do |document|
document["runtime"] = {
"image" => "ghcr.io/anomalyco/opencode@sha256:#{'f' * 64}",
"reported_version" => "1.18.3"
}
end
}
mutations.each do |field, mutate|
write_manifest(valid_manifest)
candidate, previous = write_matching_evidence
path = File.join(@root, candidate.fetch("path"))
document = JSON.parse(File.read(path))
mutate.call(document)
File.write(path, JSON.pretty_generate(document) + "\n")
error = assert_raises(OpenCodeCompat::PromotionError, field) do
@promoter.promote(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, candidate),
previous_certification: certification(CURRENT_TIME, previous),
dry_run: true
)
end
assert_match(/#{Regexp.escape(field)}/, error.message, field)
end
end
def test_recorded_certifications_require_hash_bound_evidence_references
candidate, previous = write_matching_evidence
promoted = @promoter.promote(
consumer: CONSUMER,
consumer_commit: CANDIDATE_COMMIT,
certification: certification(CANDIDATE_TIME, candidate),
previous_certification: certification(CURRENT_TIME, previous)
)
consumer = promoted.dig("consumers", CONSUMER)
consumer.dig("current", "certification")["evidence"] = [candidate.fetch("path")]
consumer["candidate"] = {
"status" => "compatibility-certified",
"certified_at" => NEXT_TIME,
"consumer_commit" => NEXT_COMMIT,
"consumer_tree" => NEXT_TREE,
"opencode_ruby" => {"version" => "0.0.1.alpha8", "git_commit" => RUBY_NEXT},
"opencode_rails" => {"version" => "0.0.1.alpha8", "git_commit" => RAILS_NEXT},
"runtime" => {"image" => NEXT_IMAGE, "reported_version" => "1.19.0"}
}
write_manifest(promoted)
fingerprint = @promoter.fingerprints(
consumer: CONSUMER,
consumer_commit: NEXT_COMMIT
).fetch("candidate_tuple_sha256")
next_evidence = write_evidence(
"next-after-legacy-reference.json",
commit: NEXT_COMMIT,
timestamp: NEXT_TIME,
fingerprint: fingerprint
)
error = assert_raises(OpenCodeCompat::PromotionError) do
@promoter.promote(
consumer: CONSUMER,
consumer_commit: NEXT_COMMIT,
certification: certification(NEXT_TIME, next_evidence),
dry_run: true
)
end
assert_match(/hash-bound \{path, sha256\}/, error.message)
end
def test_rejects_non_passing_or_implicit_certification_metadata
candidate, previous = write_matching_evidence
@@ -262,6 +572,7 @@ class RuntimeTuplePromoterTest < Minitest::Test
"current" => {
"status" => "observed-production",
"consumer_commit" => CURRENT_COMMIT,
"consumer_tree" => CURRENT_TREE,
"opencode_ruby" => {"version" => "0.0.1.alpha2", "git_commit" => RUBY_CURRENT},
"opencode_rails" => {"version" => "0.0.1.alpha2", "git_commit" => RAILS_CURRENT},
"runtime" => {"image" => CURRENT_IMAGE, "reported_version" => "1.16.1"}
@@ -269,6 +580,7 @@ class RuntimeTuplePromoterTest < Minitest::Test
"candidate" => {
"status" => "compatibility-certified",
"certified_at" => "2026-07-18T10:00:00Z",
"consumer_tree" => CANDIDATE_TREE,
"opencode_ruby" => {"version" => "0.0.1.alpha7", "git_commit" => RUBY_CANDIDATE},
"opencode_rails" => {"version" => "0.0.1.alpha7", "git_commit" => RAILS_CANDIDATE},
"runtime" => {"image" => CANDIDATE_IMAGE, "reported_version" => "1.18.3"}
@@ -319,6 +631,14 @@ class RuntimeTuplePromoterTest < Minitest::Test
}
end
def evidence_reference(evidence)
path = evidence.fetch("path")
{
"path" => path,
"sha256" => "sha256:#{Digest::SHA256.file(File.join(@root, path)).hexdigest}"
}
end
def manifest_path
File.join(@root, "manifests", "runtime-tuples.json")
end

View File

@@ -170,6 +170,10 @@ class WatcherTest < Minitest::Test
def test_workflow_can_only_update_manifests_and_open_a_pr
workflow = File.read(File.join(ROOT, ".github/workflows/watch-upstream.yml"))
assert_match(
/\n open-compatibility-pr:\n if: github\.server_url == 'https:\/\/github\.com'\n runs-on:/,
workflow
)
assert_includes workflow, "contents: write"
assert_includes workflow, "pull-requests: write"
assert_includes workflow, "git add manifests/image-matrix.json manifests/upstream.json"