Compare commits
1 Commits
main
...
chore/fina
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d0ab1f23f |
26
README.md
26
README.md
@@ -16,11 +16,29 @@ prompts, credentials, sessions, or user data belong here.
|
|||||||
can pull them. Public CI certifies their upstream base plus the same profile.
|
can pull them. Public CI certifies their upstream base plus the same profile.
|
||||||
- Promotion is explicit. CI may certify a tuple and the watcher may open a PR;
|
- Promotion is explicit. CI may certify a tuple and the watcher may open a PR;
|
||||||
neither workflow deploys or changes a consumer.
|
neither workflow deploys or changes a consumer.
|
||||||
- Each consumer retains both `current` and `previous` certified tuples so a
|
- A passing pull-request head is candidate evidence only. Promotion and
|
||||||
rollback restores the gem and server together.
|
deployed certification use the actual main/merge commit, or an explicit
|
||||||
|
equal-tree attestation paired with a passing post-merge canary.
|
||||||
|
- Git-sourced gems use the full peeled commit as the Bundler `ref` and lockfile
|
||||||
|
revision. A consumer test must also prove the running process loaded that
|
||||||
|
exact `Bundler::Source::Git`; a release tag or lockfile alone is provenance,
|
||||||
|
not loaded-source proof.
|
||||||
|
- Ajent records AIGL, Blackline, and Raven selections together. Every selected
|
||||||
|
product needs a content digest or local image ID before promotion; a full
|
||||||
|
commit tag selects a candidate build but does not prove its content.
|
||||||
|
- In steady state, each consumer retains both `current` and `previous`
|
||||||
|
certified tuples so a rollback restores the gem and server together. The
|
||||||
|
explicitly labeled bootstrap state is the temporary degraded exception.
|
||||||
- A failing bootstrap baseline is never relabeled as a certified `previous`
|
- A failing bootstrap baseline is never relabeled as a certified `previous`
|
||||||
tuple. Promotion stays blocked until a distinct rollback consumer commit is
|
tuple. After a real main/deploy canary, an explicitly acknowledged degraded
|
||||||
pinned to a passing client/runtime tuple and canaried.
|
bootstrap may certify only `current`; it leaves `previous` null and keeps the
|
||||||
|
failed baseline as emergency provenance. The next meaningful passing release
|
||||||
|
becomes the first genuine certified rollback tuple.
|
||||||
|
|
||||||
|
The runtime ledger is schema version 2; its machine-readable shape lives at
|
||||||
|
[`schemas/runtime-tuples.schema.json`](schemas/runtime-tuples.schema.json).
|
||||||
|
The current candidates remain deliberately unpromotable PR-head records, and
|
||||||
|
all `previous` slots remain `null` because no passing rollback tuple exists.
|
||||||
|
|
||||||
## What is covered
|
## What is covered
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ A candidate tuple is the complete set of client version or commit, Rails gem
|
|||||||
version or commit where applicable, exact OpenCode image, profile, and consumer
|
version or commit where applicable, exact OpenCode image, profile, and consumer
|
||||||
commit. Changing any member invalidates the certification.
|
commit. Changing any member invalidates the certification.
|
||||||
|
|
||||||
|
There are two distinct scopes:
|
||||||
|
|
||||||
|
- `pre-merge-pr-head-candidate-only` proves that one reviewed PR head is
|
||||||
|
compatible. It is never a deployment or promotion authorization.
|
||||||
|
- `promotion-deployed` is recorded only after a passing post-merge canary and
|
||||||
|
the consumer coordinate is the actual main/merge commit. If policy
|
||||||
|
deliberately retains the PR-head commit, the evidence must additionally
|
||||||
|
record both Git trees as identical and name the actual main/merge commit.
|
||||||
|
Comparing diffs is not an identical-tree attestation.
|
||||||
|
|
||||||
The gate requires:
|
The gate requires:
|
||||||
|
|
||||||
1. repository validation and the shared fixture corpus;
|
1. repository validation and the shared fixture corpus;
|
||||||
@@ -16,6 +26,19 @@ The gate requires:
|
|||||||
6. captured evidence including image ID/digest, server version, source commit,
|
6. captured evidence including image ID/digest, server version, source commit,
|
||||||
gem commits, timestamps, and probe outcome.
|
gem commits, timestamps, and probe outcome.
|
||||||
|
|
||||||
|
For a Git-sourced gem, “gem commit” means the peeled 40-character commit in
|
||||||
|
both the Gemfile `ref` and lockfile `revision`, plus runtime loaded-source proof:
|
||||||
|
the loaded version, `Bundler::Source::Git`, observed revision (and observed ref
|
||||||
|
when the consumer test exposes it), and the consumer test that made those
|
||||||
|
assertions. Annotated tag objects remain useful release provenance, but they
|
||||||
|
are not an execution coordinate.
|
||||||
|
|
||||||
|
Ajent's runtime is a three-product selection. The ledger records AIGL,
|
||||||
|
Blackline, and Raven under one source commit. Candidate evidence may say that a
|
||||||
|
product artifact has not been built; promotion may not. At promotion time the
|
||||||
|
set of content-addressed product artifacts must exactly equal the selected
|
||||||
|
product set.
|
||||||
|
|
||||||
Health-only probes do not certify a tuple.
|
Health-only probes do not certify a tuple.
|
||||||
|
|
||||||
The shared live probe is intentionally strict: `full_text` must equal the
|
The shared live probe is intentionally strict: `full_text` must equal the
|
||||||
@@ -26,9 +49,18 @@ duplicated `compat-ok\n\ncompat-ok`, fails.
|
|||||||
## Promotion
|
## Promotion
|
||||||
|
|
||||||
Promotion is a reviewed manifest change that moves the old `current` tuple to
|
Promotion is a reviewed manifest change that moves the old `current` tuple to
|
||||||
`previous` and the passing candidate to `current`. The consumer change is then
|
`previous` and the passing candidate to `current`. The consumer change must
|
||||||
merged and deployed separately. Workflows in this repository have no deploy
|
already have a main/merge identity (or the equal-tree exception below) before
|
||||||
credentials or deploy steps.
|
that manifest change. Production rollout remains a separate consumer-owned
|
||||||
|
operation; workflows in this repository have no deploy credentials or deploy
|
||||||
|
steps.
|
||||||
|
|
||||||
|
Before invoking the promoter, replace PR-head-only metadata with either the
|
||||||
|
actual main/merge consumer ref and `main-commit` promotion provenance, or an
|
||||||
|
explicit identical-tree attestation containing the main commit/tree. Both
|
||||||
|
forms require a post-merge canary evidence path. Set `promotion_eligible` only
|
||||||
|
after that evidence exists. The promoter rejects the current PR-head candidates
|
||||||
|
even when their compatibility probes pass.
|
||||||
|
|
||||||
Use the repository promotion command; do not hand-edit the three tuple slots.
|
Use the repository promotion command; do not hand-edit the three tuple slots.
|
||||||
It binds evidence to a canonical SHA-256 of the complete tuple, requires full
|
It binds evidence to a canonical SHA-256 of the complete tuple, requires full
|
||||||
@@ -89,10 +121,13 @@ being able to observe the underlying model request. Record that distinction
|
|||||||
explicitly; do not infer a model request count from an application prompt
|
explicitly; do not infer a model request count from an application prompt
|
||||||
count. The exact-image shared contract supplies the model-request proof.
|
count. The exact-image shared contract supplies the model-request proof.
|
||||||
|
|
||||||
Likewise, the public image job executes `ruby-rest-sse`; it does not silently
|
Likewise, the public image job executes only `ruby-rest-sse`; it does not
|
||||||
certify Rails persistence, plugin hooks, voice streaming, strict route gates,
|
silently certify Rails persistence, plugin hooks, voice streaming, strict route
|
||||||
or provider hooks. Those appear as `required_consumer_profiles` in the image
|
gates, or provider hooks. Every other profile is labeled
|
||||||
matrix and need their own consumer evidence before a full tuple can pass.
|
`executable-consumer-attestation` with `shared_ruby_probe_sufficient: false`.
|
||||||
|
Those profiles appear as `required_consumer_profiles` in the image matrix and
|
||||||
|
need executable evidence from their owning consumer before a full tuple can
|
||||||
|
pass. A prose review or a successful shared Ruby probe is not that evidence.
|
||||||
|
|
||||||
The command clears `candidate` after promotion. It sets the repository-wide
|
The command clears `candidate` after promotion. It sets the repository-wide
|
||||||
`migration_state` to `certified` only after every consumer has certified
|
`migration_state` to `certified` only after every consumer has certified
|
||||||
@@ -105,12 +140,39 @@ coordinates for emergency recovery but mark it failed. Do not create passing
|
|||||||
evidence for it and do not promote it into `previous`. The promoter rejects
|
evidence for it and do not promote it into `previous`. The promoter rejects
|
||||||
known-failed baselines even if a document claims `pass`.
|
known-failed baselines even if a document claims `pass`.
|
||||||
|
|
||||||
The safe bootstrap is two-stage: certify and roll out the new candidate, then
|
Do not manufacture a no-op second commit merely to fill `previous`. Once the
|
||||||
create and canary a distinct consumer rollback commit that preserves the same
|
candidate has been merged, deployed, and canaried under promotion-grade
|
||||||
known-good client and exact runtime. Only after both immutable consumer commits
|
provenance, a reviewer may use `bootstrap-current` with this exact
|
||||||
have real passing evidence can the manifest honestly contain certified
|
acknowledgement:
|
||||||
`current` and `previous` tuples. Until then, `promotion_readiness` remains
|
|
||||||
blocked and the candidate PR must not be treated as a deploy authorization.
|
```text
|
||||||
|
accept-degraded-rollback-with-failed-emergency-provenance
|
||||||
|
```
|
||||||
|
|
||||||
|
That operation certifies only `current`, moves the failed baseline to
|
||||||
|
`emergency_provenance` without relabeling it, leaves `previous` null, and sets
|
||||||
|
`migration_state` to `bootstrap-current-only`. This is an intentionally
|
||||||
|
degraded rollback state, not full certification. The next meaningful passing
|
||||||
|
release uses normal promotion; the already-certified current tuple then becomes
|
||||||
|
the first honest certified `previous` tuple.
|
||||||
|
|
||||||
|
Until main/deploy evidence exists, `promotion_readiness` remains blocked and a
|
||||||
|
candidate PR must not be treated as deploy authorization.
|
||||||
|
|
||||||
|
After reviewing the degraded rollback consequence, preview the explicit
|
||||||
|
bootstrap before writing it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ruby scripts/promote_runtime_tuple.rb bootstrap-current \
|
||||||
|
--consumer travelwolf \
|
||||||
|
--consumer-commit FULL_MAIN_OR_ATTESTED_PR_COMMIT \
|
||||||
|
--status pass \
|
||||||
|
--certified-at 2026-07-18T12:00:00Z \
|
||||||
|
--evidence evidence/travelwolf-post-merge.json \
|
||||||
|
--acknowledge-degraded-rollback \
|
||||||
|
accept-degraded-rollback-with-failed-emergency-provenance \
|
||||||
|
--dry-run
|
||||||
|
```
|
||||||
|
|
||||||
## Rollback
|
## Rollback
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,87 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"consumer": "ajent-rails",
|
"consumer": "ajent-rails",
|
||||||
"profile": "rails-persisted-turn",
|
"profile": "rails-persisted-turn",
|
||||||
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"consumer_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"consumer_tree": "9e93834ad16b60fa5edfe1cb3baaba193402be74",
|
||||||
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/ajent-rails/pulls/946",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
"status": "pass",
|
"status": "pass",
|
||||||
"certified_at": "2026-07-18T22:03:14Z",
|
"certified_at": "2026-07-18T23:28:16.435369Z",
|
||||||
"tuple_sha256": "sha256:2fc1026cf247b5ad5677bfa4673c1073f7c2478769dca0cc8443a0924cebfca3",
|
"tuple_sha256": "sha256:e7026c294d0b2981f6ec785a45afb69969f4dc2b4d0ed6533f9bda5153304663",
|
||||||
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
||||||
"canary": {
|
"loaded_sources": {
|
||||||
"checked_at": "2026-07-18T21:58:00Z",
|
"opencode-ruby": {
|
||||||
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
|
"version": "0.0.1.alpha7",
|
||||||
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
|
||||||
|
},
|
||||||
|
"opencode-rails": {
|
||||||
|
"version": "0.0.1.alpha7",
|
||||||
|
"requested_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"locked_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"observed_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb"
|
||||||
|
},
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"proof_test": "test/lib/opencode/runtime_image_provenance_test.rb",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"deterministic_contract": {
|
||||||
|
"checked_at": "2026-07-18T23:16:13Z",
|
||||||
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"application_canary": {
|
||||||
|
"checked_at": "2026-07-18T23:28:16.435369Z",
|
||||||
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
"persisted_final_text": "compat-ok",
|
"persisted_final_text": "compat-ok",
|
||||||
"model_request_count": 1
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"cleanup_status": "pass",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"consumer_verification": {
|
||||||
|
"tests": {
|
||||||
|
"runs": 4077,
|
||||||
|
"assertions": 14495,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0,
|
||||||
|
"existing_skips": 4
|
||||||
|
},
|
||||||
|
"rubocop": {
|
||||||
|
"files": 1365,
|
||||||
|
"offenses": 0
|
||||||
|
},
|
||||||
|
"gitea_actions": {
|
||||||
|
"run_id": 2545,
|
||||||
|
"status": "success",
|
||||||
|
"commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2"
|
||||||
|
},
|
||||||
|
"remotes_exact": [
|
||||||
|
"origin",
|
||||||
|
"github",
|
||||||
|
"backup"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"product_artifact_coverage": {
|
||||||
|
"selected_products": [
|
||||||
|
"aigl",
|
||||||
|
"blackline",
|
||||||
|
"raven"
|
||||||
|
],
|
||||||
|
"content_addressed_artifacts": [
|
||||||
|
"blackline"
|
||||||
|
],
|
||||||
|
"missing_before_promotion": [
|
||||||
|
"aigl",
|
||||||
|
"raven"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,88 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"consumer": "mushu",
|
"consumer": "mushu",
|
||||||
"profile": "ruby-rest-sse",
|
"profile": "ruby-rest-sse",
|
||||||
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
|
"consumer_commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
|
"consumer_tree": "762eb8dcc7042a52cd240ca69acdb9cb45150a84",
|
||||||
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/mushu/pulls/1",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
"status": "pass",
|
"status": "pass",
|
||||||
"certified_at": "2026-07-18T22:03:14Z",
|
"certified_at": "2026-07-18T23:26:06.865190Z",
|
||||||
"tuple_sha256": "sha256:6171647c7db6d88d52882695c6106a4391e87d0bb1f9e0c48151b2bc1e95b318",
|
"tuple_sha256": "sha256:648d53b1d800c065e8ecdb95974049ee179617569170cfc169c31c3bfc1e8865",
|
||||||
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
||||||
"canary": {
|
"loaded_source": {
|
||||||
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
|
"gem": "opencode-ruby",
|
||||||
"checked_at": "2026-07-18T21:53:00Z",
|
"version": "0.0.1.alpha7",
|
||||||
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
"source_class": "Bundler::Source::Git",
|
||||||
"persisted_final_text": "compat-ok",
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"model_request_count": 1
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"observed_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"loaded_file_within_spec": true,
|
||||||
|
"proof_test": "test/services/mushu/runtime/opencode_dependency_provenance_test.rb",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"deterministic_contract": {
|
||||||
|
"checked_at": "2026-07-18T23:23:41Z",
|
||||||
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"application_canary": {
|
||||||
|
"checked_at": "2026-07-18T23:26:06.865190Z",
|
||||||
|
"response_text": "compat-ok",
|
||||||
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"message_roles": [
|
||||||
|
"user",
|
||||||
|
"assistant"
|
||||||
|
],
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"provider_request_count": 1,
|
||||||
|
"mapping_and_last_turn_persisted": true,
|
||||||
|
"active_claim_cleared": true,
|
||||||
|
"opencode_session_deleted": true,
|
||||||
|
"synthetic_database_rows_after_cleanup": 0,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"consumer_verification": {
|
||||||
|
"focused_tests": {
|
||||||
|
"runs": 126,
|
||||||
|
"assertions": 1413,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0
|
||||||
|
},
|
||||||
|
"full_tests": {
|
||||||
|
"runs": 632,
|
||||||
|
"assertions": 9013,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0
|
||||||
|
},
|
||||||
|
"rubocop": {
|
||||||
|
"files": 216,
|
||||||
|
"offenses": 0
|
||||||
|
},
|
||||||
|
"gitea_actions": {
|
||||||
|
"run_id": 2544,
|
||||||
|
"url": "https://gitea.ajay.to/ajaynomics/mushu/actions/runs/2544",
|
||||||
|
"overall_status": "failure",
|
||||||
|
"commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
|
"classification": "inherited-unrelated-baseline-failures",
|
||||||
|
"jobs": {
|
||||||
|
"lint": "success",
|
||||||
|
"scan_js": "success",
|
||||||
|
"scan_ruby": "inherited-vendor-bundle-source-scan-failure",
|
||||||
|
"test": "inherited-evidence-report-single-failure",
|
||||||
|
"system-test": "inherited-missing-test-system-directory"
|
||||||
|
},
|
||||||
|
"baseline_run_id": 2508
|
||||||
|
},
|
||||||
|
"remotes_exact": [
|
||||||
|
"origin",
|
||||||
|
"backup"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,9 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"target": "ajent-production-artifact",
|
"target": "ajent-production-artifact",
|
||||||
"docker_image_id": "sha256:6fb2b3bf8e4cdf48e16bf6c3475e953df2f8304e77f5efe816ceb46f905eead8",
|
"consumer_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"product": "blackline",
|
||||||
|
"docker_image_id": "sha256:af479243e6ccf1206f57dffd13b8e9d2c028de401cccc954bcc2f00940274c60",
|
||||||
"server_version": "1.18.3",
|
"server_version": "1.18.3",
|
||||||
"status": "fail"
|
"status": "fail"
|
||||||
},
|
},
|
||||||
@@ -47,5 +49,38 @@
|
|||||||
"status": "fail"
|
"status": "fail"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"promotion_effect": "The observed alpha2 consumer baselines are not eligible for certified previous tuples. A distinct, tested rollback consumer commit pinned to alpha7 is required before the two-certified-tuples policy can be satisfied."
|
"observed_ajent_production_runtime": {
|
||||||
|
"consumer_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"upstream_base_layer_match": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
|
"shared_accessory": {
|
||||||
|
"product": "blackline",
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-blackline:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:af479243e6ccf1206f57dffd13b8e9d2c028de401cccc954bcc2f00940274c60"
|
||||||
|
},
|
||||||
|
"deployed_per_user_product_configuration": {
|
||||||
|
"strategy": "mutable-latest",
|
||||||
|
"status": "observed-configuration-drift-failure",
|
||||||
|
"references": {
|
||||||
|
"aigl": "docker-registry:5000/opencode-aigl:latest",
|
||||||
|
"blackline": "docker-registry:5000/opencode-blackline:latest",
|
||||||
|
"raven": "docker-registry:5000/opencode-raven:latest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ci_built_sha_artifacts": {
|
||||||
|
"aigl": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-aigl:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:72c454f39d423618b75439122c9073dbba7c9071421e5f3abd12cf288dc1f898"
|
||||||
|
},
|
||||||
|
"blackline": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-blackline:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:af479243e6ccf1206f57dffd13b8e9d2c028de401cccc954bcc2f00940274c60"
|
||||||
|
},
|
||||||
|
"raven": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-raven:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:6c4da94a8dec6160f0b1b4d0e2cd9ff38a3faa18dbd2a8d6023a71f6416a544c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scope_note": "The hardened alpha2 contract was rerun against the exact shared Blackline accessory. The three SHA-tagged image IDs are CI artifact provenance, not evidence that the deployed per-user ProductConfig selected them; it selected mutable latest references."
|
||||||
|
},
|
||||||
|
"promotion_effect": "The observed alpha2 consumer baselines are not eligible for certified previous tuples. After a real main/deploy/post-merge canary, an explicitly acknowledged bootstrap may certify current while leaving previous null and retaining alpha2 only as failed emergency provenance; the next meaningful passing release creates the first certified previous tuple."
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
"target": "travelwolf-1.16.1",
|
"target": "travelwolf-1.16.1",
|
||||||
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
||||||
"server_version": "1.16.1",
|
"server_version": "1.16.1",
|
||||||
"checked_at": "2026-07-18T22:03:12Z",
|
"checked_at": "2026-07-18T23:35:57Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1,
|
"llm_request_count": 1,
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
"target": "opencode-ajent-1.17.18",
|
"target": "opencode-ajent-1.17.18",
|
||||||
"image": "ghcr.io/anomalyco/opencode@sha256:bf9d0e84b7cedef436a8f57db4d48767cd5d8fc6604f666335c1cc916b199a97",
|
"image": "ghcr.io/anomalyco/opencode@sha256:bf9d0e84b7cedef436a8f57db4d48767cd5d8fc6604f666335c1cc916b199a97",
|
||||||
"server_version": "1.17.18",
|
"server_version": "1.17.18",
|
||||||
"checked_at": "2026-07-18T22:03:13Z",
|
"checked_at": "2026-07-18T23:35:58Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1,
|
"llm_request_count": 1,
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
"target": "ajent-base-1.18.3",
|
"target": "ajent-base-1.18.3",
|
||||||
"image": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
"image": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
"server_version": "1.18.3",
|
"server_version": "1.18.3",
|
||||||
"checked_at": "2026-07-18T22:03:14Z",
|
"checked_at": "2026-07-18T23:35:58Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1,
|
"llm_request_count": 1,
|
||||||
@@ -71,42 +71,55 @@
|
|||||||
"consumer_canaries": [
|
"consumer_canaries": [
|
||||||
{
|
{
|
||||||
"consumer": "ajent-rails",
|
"consumer": "ajent-rails",
|
||||||
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"consumer_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
"tag_provenance": "ajent-opencode-blackline-canary:96552893",
|
"consumer_ref_kind": "pull-request-head",
|
||||||
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
"source_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"tag_provenance": "ajent-opencode-blackline-canary:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
|
"source_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
"server_version": "1.18.3",
|
"server_version": "1.18.3",
|
||||||
"checked_at": "2026-07-18T21:58:00Z",
|
"checked_at": "2026-07-18T23:28:16.435369Z",
|
||||||
"application_path": "Blackline::GenerateResponseJob",
|
"application_path": "Blackline::GenerateResponseJob",
|
||||||
"persisted_final_text": "compat-ok",
|
"persisted_final_text": "compat-ok",
|
||||||
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
"model_request_count": 1,
|
"model_request_count": 1,
|
||||||
|
"cleanup_status": "pass",
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"consumer": "mushu",
|
"consumer": "mushu",
|
||||||
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
|
"consumer_commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
||||||
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
||||||
"server_version": "0.0.0-permission-shortcuts-202606300721",
|
"server_version": "0.0.0-permission-shortcuts-202606300721",
|
||||||
"checked_at": "2026-07-18T21:53:00Z",
|
"checked_at": "2026-07-18T23:26:06.865190Z",
|
||||||
"persisted_final_text": "compat-ok",
|
"response_text": "compat-ok",
|
||||||
"model_request_count": 1,
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"provider_request_count": 1,
|
||||||
|
"cleanup_status": "pass",
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"consumer": "travelwolf",
|
"consumer": "travelwolf",
|
||||||
"consumer_commit": "19747b59fa21e12560afd9edd73ed674ab3a644c",
|
"consumer_commit": "b9a2c2dd361a6b2ea547cc67ee817b8eb4ce31e8",
|
||||||
"run_id": "20260718215135-8d8102c0",
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"run_id": "travelwolf-alpha7-ref-20260718T232211Z-48eadf",
|
||||||
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
||||||
"server_version": "1.16.1",
|
"server_version": "1.16.1",
|
||||||
"started_at": "2026-07-18T21:51:35Z",
|
"checked_at": "2026-07-18T23:22:26.216140Z",
|
||||||
"checked_at": "2026-07-18T21:51:56.552Z",
|
"final_text": "TRAVELWOLF_ALPHA7_REF_PIN_READY",
|
||||||
"final_text": "TRAVELWOLF_ALPHA7_SUBSCRIBE_READY",
|
"subscription_ready_before_prompt": true,
|
||||||
"application_prompt_count": 1,
|
"opencode_api_prompt_count": 1,
|
||||||
"model_request_count_observed": false,
|
"provider_request_count_observed": false,
|
||||||
|
"message_roles": ["user", "assistant"],
|
||||||
"cleanup_status": "pass",
|
"cleanup_status": "pass",
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,86 @@
|
|||||||
"schema_version": 1,
|
"schema_version": 1,
|
||||||
"consumer": "travelwolf",
|
"consumer": "travelwolf",
|
||||||
"profile": "rails-persisted-turn",
|
"profile": "rails-persisted-turn",
|
||||||
"consumer_commit": "19747b59fa21e12560afd9edd73ed674ab3a644c",
|
"consumer_commit": "b9a2c2dd361a6b2ea547cc67ee817b8eb4ce31e8",
|
||||||
|
"consumer_tree": "8b3f8a97dc1f1d8b558a49523bf312d2810422df",
|
||||||
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/travelwolf/pulls/29",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
"status": "pass",
|
"status": "pass",
|
||||||
"certified_at": "2026-07-18T22:03:14Z",
|
"certified_at": "2026-07-18T23:35:57Z",
|
||||||
"tuple_sha256": "sha256:a7519e661f74d86788928540429c953edcbf20d26321c0dd2b72cd0a1b995b61",
|
"tuple_sha256": "sha256:bed7d6f6a5e6dd9138c3b960b8bf13ac6f5bf402d0aa7e5c01a52a8e8ea1083d",
|
||||||
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
"shared_evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
||||||
"canary": {
|
"loaded_sources": {
|
||||||
"run_id": "20260718215135-8d8102c0",
|
"opencode-ruby": {
|
||||||
"checked_at": "2026-07-18T21:51:56.552Z",
|
"version": "0.0.1.alpha7",
|
||||||
"final_text": "TRAVELWOLF_ALPHA7_SUBSCRIBE_READY",
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"application_prompt_count": 1,
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"model_request_count_observed": false,
|
"observed_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"cleanup_status": "pass"
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
|
||||||
|
},
|
||||||
|
"opencode-rails": {
|
||||||
|
"version": "0.0.1.alpha7",
|
||||||
|
"requested_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"locked_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"observed_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"observed_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb"
|
||||||
|
},
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"proof_test": "test/services/travelwolf/opencode_env_wiring_test.rb",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"deterministic_contract": {
|
||||||
|
"checked_at": "2026-07-18T23:23:03Z",
|
||||||
|
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
||||||
|
"opencode_ruby_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"application_canary": {
|
||||||
|
"run_id": "travelwolf-alpha7-ref-20260718T232211Z-48eadf",
|
||||||
|
"checked_at": "2026-07-18T23:22:26.216140Z",
|
||||||
|
"final_text": "TRAVELWOLF_ALPHA7_REF_PIN_READY",
|
||||||
|
"subscription_ready_before_prompt": true,
|
||||||
|
"opencode_api_prompt_count": 1,
|
||||||
|
"provider_request_count_observed": false,
|
||||||
|
"message_roles": [
|
||||||
|
"user",
|
||||||
|
"assistant"
|
||||||
|
],
|
||||||
|
"cleanup_status": "pass",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"consumer_verification": {
|
||||||
|
"source_guard": {
|
||||||
|
"runs": 4,
|
||||||
|
"assertions": 110,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0
|
||||||
|
},
|
||||||
|
"focused_tests": {
|
||||||
|
"runs": 31,
|
||||||
|
"assertions": 248,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0
|
||||||
|
},
|
||||||
|
"bounded_full_tests": {
|
||||||
|
"runs": 2300,
|
||||||
|
"assertions": 21679,
|
||||||
|
"failures": 0,
|
||||||
|
"errors": 0
|
||||||
|
},
|
||||||
|
"rubocop": {
|
||||||
|
"changed_ruby_files": 6,
|
||||||
|
"offenses": 0
|
||||||
|
},
|
||||||
|
"remotes_exact": [
|
||||||
|
"origin",
|
||||||
|
"github",
|
||||||
|
"backup"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ module OpenCodeCompat
|
|||||||
class PromotionError < StandardError; end
|
class PromotionError < StandardError; end
|
||||||
|
|
||||||
class RuntimeTuplePromoter
|
class RuntimeTuplePromoter
|
||||||
|
SCHEMA_VERSION = 2
|
||||||
FULL_COMMIT = /\A[0-9a-f]{40}\z/
|
FULL_COMMIT = /\A[0-9a-f]{40}\z/
|
||||||
DIGEST = /\Asha256:[0-9a-f]{64}\z/
|
DIGEST = /\Asha256:[0-9a-f]{64}\z/
|
||||||
IMMUTABLE_IMAGE = /\A[^@\s]+@sha256:[0-9a-f]{64}\z/
|
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/
|
UTC_TIMESTAMP = /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z\z/
|
||||||
CERTIFICATION_STATUS = "pass"
|
CERTIFICATION_STATUS = "pass"
|
||||||
|
DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT =
|
||||||
|
"accept-degraded-rollback-with-failed-emergency-provenance"
|
||||||
NON_CERTIFIABLE_TUPLE_STATUSES = %w[observed-production-contract-failed].freeze
|
NON_CERTIFIABLE_TUPLE_STATUSES = %w[observed-production-contract-failed].freeze
|
||||||
TUPLE_METADATA_KEYS = %w[
|
TUPLE_METADATA_KEYS = %w[
|
||||||
certification
|
certification
|
||||||
@@ -41,7 +44,7 @@ module OpenCodeCompat
|
|||||||
manifest = read_manifest
|
manifest = read_manifest
|
||||||
consumer_entry = fetch_consumer!(manifest, consumer)
|
consumer_entry = fetch_consumer!(manifest, consumer)
|
||||||
profile = fetch_profile!(consumer_entry, consumer)
|
profile = fetch_profile!(consumer_entry, consumer)
|
||||||
candidate = prepare_candidate!(consumer_entry, consumer_commit)
|
candidate = prepare_candidate!(consumer_entry, consumer_commit, for_promotion: false)
|
||||||
current = consumer_entry.fetch("current") do
|
current = consumer_entry.fetch("current") do
|
||||||
raise PromotionError, "#{consumer} has no current tuple to preserve as previous"
|
raise PromotionError, "#{consumer} has no current tuple to preserve as previous"
|
||||||
end
|
end
|
||||||
@@ -82,6 +85,37 @@ module OpenCodeCompat
|
|||||||
end
|
end
|
||||||
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
|
private
|
||||||
|
|
||||||
def read_manifest
|
def read_manifest
|
||||||
@@ -93,7 +127,7 @@ module OpenCodeCompat
|
|||||||
def promote_manifest(manifest, consumer:, consumer_commit:, certification:, previous_certification:)
|
def promote_manifest(manifest, consumer:, consumer_commit:, certification:, previous_certification:)
|
||||||
consumer_entry = fetch_consumer!(manifest, consumer)
|
consumer_entry = fetch_consumer!(manifest, consumer)
|
||||||
profile = fetch_profile!(consumer_entry, consumer)
|
profile = fetch_profile!(consumer_entry, consumer)
|
||||||
candidate = prepare_candidate!(consumer_entry, consumer_commit)
|
candidate = prepare_candidate!(consumer_entry, consumer_commit, for_promotion: true)
|
||||||
current = consumer_entry.fetch("current") do
|
current = consumer_entry.fetch("current") do
|
||||||
raise PromotionError, "#{consumer} has no current tuple to preserve as previous"
|
raise PromotionError, "#{consumer} has no current tuple to preserve as previous"
|
||||||
end
|
end
|
||||||
@@ -124,13 +158,60 @@ module OpenCodeCompat
|
|||||||
consumer_entry["previous"] = certified_previous
|
consumer_entry["previous"] = certified_previous
|
||||||
consumer_entry["current"] = certified_candidate
|
consumer_entry["current"] = certified_candidate
|
||||||
consumer_entry["candidate"] = nil
|
consumer_entry["candidate"] = nil
|
||||||
manifest["migration_state"] = all_consumers_certified?(manifest) ? "certified" : "candidate"
|
consumer_entry.delete("rollback_state")
|
||||||
|
manifest["migration_state"] = migration_state_for(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, for_promotion: true)
|
||||||
|
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
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
manifest["migration_state"] = migration_state_for(manifest)
|
||||||
|
manifest["promotion_readiness"] = {
|
||||||
|
"status" => "bootstrap-current-only",
|
||||||
|
"reason" => "The current tuple is certified, but no independently passing previous tuple exists.",
|
||||||
|
"required_action" => "Certify the next meaningful release; normal promotion will then retain this current tuple as the first certified previous rollback."
|
||||||
|
}
|
||||||
manifest
|
manifest
|
||||||
end
|
end
|
||||||
|
|
||||||
def fetch_consumer!(manifest, consumer)
|
def fetch_consumer!(manifest, consumer)
|
||||||
unless manifest.is_a?(Hash) && manifest["schema_version"] == 1 && manifest["consumers"].is_a?(Hash)
|
unless manifest.is_a?(Hash) && manifest["schema_version"] == SCHEMA_VERSION && manifest["consumers"].is_a?(Hash)
|
||||||
raise PromotionError, "runtime tuple manifest must use schema_version 1 and contain consumers"
|
raise PromotionError, "runtime tuple manifest must use schema_version #{SCHEMA_VERSION} and contain consumers"
|
||||||
end
|
end
|
||||||
|
|
||||||
manifest.fetch("consumers").fetch(consumer) do
|
manifest.fetch("consumers").fetch(consumer) do
|
||||||
@@ -138,7 +219,7 @@ module OpenCodeCompat
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_candidate!(consumer_entry, consumer_commit)
|
def prepare_candidate!(consumer_entry, consumer_commit, for_promotion:)
|
||||||
candidate = consumer_entry["candidate"]
|
candidate = consumer_entry["candidate"]
|
||||||
unless candidate.is_a?(Hash)
|
unless candidate.is_a?(Hash)
|
||||||
raise PromotionError, "consumer has no candidate tuple to promote"
|
raise PromotionError, "consumer has no candidate tuple to promote"
|
||||||
@@ -155,9 +236,151 @@ module OpenCodeCompat
|
|||||||
prepared = deep_copy(candidate)
|
prepared = deep_copy(candidate)
|
||||||
prepared["consumer_commit"] = consumer_commit
|
prepared["consumer_commit"] = consumer_commit
|
||||||
validate_tuple!(prepared, "candidate")
|
validate_tuple!(prepared, "candidate")
|
||||||
|
validate_candidate_consumer_ref!(prepared, consumer_commit)
|
||||||
|
validate_candidate_client_sources!(prepared)
|
||||||
|
if for_promotion
|
||||||
|
validate_promotion_provenance!(prepared, consumer_commit)
|
||||||
|
validate_promotion_product_artifacts!(prepared.fetch("runtime"))
|
||||||
|
end
|
||||||
prepared
|
prepared
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_candidate_consumer_ref!(candidate, consumer_commit)
|
||||||
|
reference = candidate["consumer_ref"]
|
||||||
|
unless reference.is_a?(Hash)
|
||||||
|
raise PromotionError, "candidate must record its exact consumer ref"
|
||||||
|
end
|
||||||
|
|
||||||
|
kind = reference["kind"]
|
||||||
|
unless %w[pull-request-head main-commit].include?(kind)
|
||||||
|
raise PromotionError, "candidate consumer ref must be a pull-request-head or main-commit"
|
||||||
|
end
|
||||||
|
validate_full_commit!(reference["commit"], "candidate consumer ref commit")
|
||||||
|
validate_full_commit!(reference["tree"], "candidate consumer ref tree")
|
||||||
|
unless reference["commit"] == consumer_commit
|
||||||
|
raise PromotionError, "candidate consumer ref does not match the tuple consumer commit"
|
||||||
|
end
|
||||||
|
unless reference["repository"].is_a?(String) && !reference["repository"].empty?
|
||||||
|
raise PromotionError, "candidate consumer ref must name its repository"
|
||||||
|
end
|
||||||
|
|
||||||
|
return unless kind == "pull-request-head"
|
||||||
|
|
||||||
|
validate_full_commit!(reference["base_commit"], "candidate pull request base commit")
|
||||||
|
unless reference["review_url"].is_a?(String) && reference["review_url"].start_with?("https://")
|
||||||
|
raise PromotionError, "candidate pull request ref must include its HTTPS review URL"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_candidate_client_sources!(candidate)
|
||||||
|
%w[opencode_ruby opencode_rails].each do |client_name|
|
||||||
|
client = candidate[client_name]
|
||||||
|
next unless client
|
||||||
|
|
||||||
|
validate_client_source!(client, "candidate #{client_name}", required: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_promotion_provenance!(candidate, consumer_commit)
|
||||||
|
unless candidate["certification_scope"] == "promotion-deployed" && candidate["promotion_eligible"] == true
|
||||||
|
raise PromotionError,
|
||||||
|
"pre-merge pull-request evidence is candidate-only; promotion requires deployed certification scope"
|
||||||
|
end
|
||||||
|
|
||||||
|
provenance = candidate["promotion_provenance"]
|
||||||
|
unless provenance.is_a?(Hash)
|
||||||
|
raise PromotionError, "promotion requires main-commit provenance or an identical-tree attestation"
|
||||||
|
end
|
||||||
|
|
||||||
|
case provenance["kind"]
|
||||||
|
when "main-commit"
|
||||||
|
unless candidate.dig("consumer_ref", "kind") == "main-commit" &&
|
||||||
|
provenance["main_commit"] == consumer_commit
|
||||||
|
raise PromotionError, "main-commit promotion provenance must match the tuple consumer commit"
|
||||||
|
end
|
||||||
|
validate_post_merge_canary!(
|
||||||
|
provenance["post_merge_canary"],
|
||||||
|
consumer_commit: provenance["main_commit"]
|
||||||
|
)
|
||||||
|
when "identical-tree-attestation"
|
||||||
|
validate_identical_tree_attestation!(candidate, provenance, consumer_commit)
|
||||||
|
else
|
||||||
|
raise PromotionError, "unknown promotion provenance kind #{provenance['kind'].inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_identical_tree_attestation!(candidate, provenance, consumer_commit)
|
||||||
|
validate_full_commit!(provenance["pull_request_commit"], "attested pull request commit")
|
||||||
|
validate_full_commit!(provenance["pull_request_tree"], "attested pull request tree")
|
||||||
|
validate_full_commit!(provenance["main_commit"], "attested main commit")
|
||||||
|
validate_full_commit!(provenance["main_tree"], "attested main tree")
|
||||||
|
validate_timestamp!(provenance["attested_at"], "identical-tree attestation timestamp")
|
||||||
|
|
||||||
|
unless provenance["pull_request_commit"] == consumer_commit &&
|
||||||
|
provenance["pull_request_tree"] == candidate.dig("consumer_ref", "tree") &&
|
||||||
|
provenance["main_tree"] == provenance["pull_request_tree"]
|
||||||
|
raise PromotionError, "identical-tree attestation does not match the candidate and main trees"
|
||||||
|
end
|
||||||
|
|
||||||
|
validate_post_merge_canary!(
|
||||||
|
provenance["post_merge_canary"],
|
||||||
|
consumer_commit: provenance["main_commit"]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_post_merge_canary!(canary, consumer_commit:)
|
||||||
|
unless canary.is_a?(Hash) && canary["status"] == "pass"
|
||||||
|
raise PromotionError, "promotion requires a passing post-merge canary"
|
||||||
|
end
|
||||||
|
validate_timestamp!(canary["checked_at"], "post-merge canary timestamp")
|
||||||
|
evidence = Array(canary["evidence"])
|
||||||
|
if evidence.empty? || evidence.any? { |path| !path.is_a?(String) || !path.start_with?("evidence/") }
|
||||||
|
raise PromotionError, "post-merge canary must reference evidence under evidence/"
|
||||||
|
end
|
||||||
|
evidence.each do |reference|
|
||||||
|
validate_post_merge_canary_evidence!(
|
||||||
|
reference,
|
||||||
|
checked_at: canary.fetch("checked_at"),
|
||||||
|
consumer_commit: consumer_commit
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_post_merge_canary_evidence!(reference, checked_at:, consumer_commit:)
|
||||||
|
relative = Pathname.new(reference).cleanpath
|
||||||
|
if relative.absolute? || relative.each_filename.first != "evidence"
|
||||||
|
raise PromotionError, "post-merge canary evidence must stay under evidence/"
|
||||||
|
end
|
||||||
|
|
||||||
|
evidence_root = File.realpath(File.join(@root, "evidence"))
|
||||||
|
real_path = File.realpath(File.join(@root, relative.to_s))
|
||||||
|
unless real_path.start_with?("#{evidence_root}#{File::SEPARATOR}")
|
||||||
|
raise PromotionError, "post-merge canary evidence resolves outside evidence/"
|
||||||
|
end
|
||||||
|
|
||||||
|
document = parse_json(File.read(real_path), relative.to_s)
|
||||||
|
unless document.is_a?(Hash) && document["schema_version"] == 1 &&
|
||||||
|
document["status"] == "pass" && document["checked_at"] == checked_at &&
|
||||||
|
document["consumer_commit"] == consumer_commit
|
||||||
|
raise PromotionError,
|
||||||
|
"post-merge canary evidence must match its passing timestamp and main consumer commit"
|
||||||
|
end
|
||||||
|
rescue Errno::ENOENT
|
||||||
|
raise PromotionError, "post-merge canary evidence does not exist: #{reference}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_promotion_product_artifacts!(runtime)
|
||||||
|
selection = runtime["product_selection"]
|
||||||
|
return unless selection
|
||||||
|
|
||||||
|
selected_products = selection.fetch("references").keys.sort
|
||||||
|
artifact_products = runtime.fetch("product_images", {}).keys.sort
|
||||||
|
return if selected_products == artifact_products
|
||||||
|
|
||||||
|
raise PromotionError,
|
||||||
|
"promotion requires exact content IDs or digests for every selected product image"
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_profile!(consumer_entry, consumer)
|
def fetch_profile!(consumer_entry, consumer)
|
||||||
profile = consumer_entry["profile"]
|
profile = consumer_entry["profile"]
|
||||||
unless profile.is_a?(String) && profile.match?(/\A[a-z0-9][a-z0-9-]*\z/)
|
unless profile.is_a?(String) && profile.match?(/\A[a-z0-9][a-z0-9-]*\z/)
|
||||||
@@ -346,6 +569,46 @@ module OpenCodeCompat
|
|||||||
end
|
end
|
||||||
|
|
||||||
validate_full_commit!(client["git_commit"], "#{label} git commit")
|
validate_full_commit!(client["git_commit"], "#{label} git commit")
|
||||||
|
validate_client_source!(client, label, required: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def validate_client_source!(client, label, required:)
|
||||||
|
source = client["source"]
|
||||||
|
if source.nil?
|
||||||
|
raise PromotionError, "#{label} must include loaded exact-ref source proof" if required
|
||||||
|
|
||||||
|
return
|
||||||
|
end
|
||||||
|
unless source.is_a?(Hash) && source["type"] == "git" && source["uri"].is_a?(String) && !source["uri"].empty?
|
||||||
|
raise PromotionError, "#{label} source must identify its Git URI"
|
||||||
|
end
|
||||||
|
|
||||||
|
commit = client.fetch("git_commit")
|
||||||
|
%w[requested_ref locked_revision].each do |key|
|
||||||
|
unless source[key] == commit
|
||||||
|
raise PromotionError, "#{label} source #{key} must equal the peeled client commit"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
proof = source["loaded_source_proof"]
|
||||||
|
unless proof.is_a?(Hash) && proof["status"] == "pass" && proof["source_class"] == "Bundler::Source::Git"
|
||||||
|
raise PromotionError, "#{label} must include passing Bundler Git loaded-source proof"
|
||||||
|
end
|
||||||
|
expected = {
|
||||||
|
"loaded_version" => client.fetch("version"),
|
||||||
|
"observed_revision" => commit
|
||||||
|
}
|
||||||
|
expected.each do |key, value|
|
||||||
|
unless proof[key] == value
|
||||||
|
raise PromotionError, "#{label} loaded-source proof #{key} must equal #{value.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if proof.key?("observed_ref") && proof["observed_ref"] != commit
|
||||||
|
raise PromotionError, "#{label} loaded-source proof observed_ref must equal #{commit.inspect}"
|
||||||
|
end
|
||||||
|
unless proof["test"].is_a?(String) && !proof["test"].empty?
|
||||||
|
raise PromotionError, "#{label} loaded-source proof must name the consumer test"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def validate_runtime!(runtime, label)
|
def validate_runtime!(runtime, label)
|
||||||
@@ -355,6 +618,47 @@ module OpenCodeCompat
|
|||||||
end
|
end
|
||||||
|
|
||||||
exact_execution_coordinate = false
|
exact_execution_coordinate = false
|
||||||
|
product_selection = runtime["product_selection"]
|
||||||
|
if product_selection
|
||||||
|
unless product_selection.is_a?(Hash) && product_selection["strategy"] == "full-consumer-commit-tag"
|
||||||
|
raise PromotionError, "#{label} product_selection must use the full-consumer-commit-tag strategy"
|
||||||
|
end
|
||||||
|
validate_full_commit!(product_selection["source_commit"], "#{label} product selection source commit")
|
||||||
|
references = product_selection["references"]
|
||||||
|
unless references.is_a?(Hash) && !references.empty?
|
||||||
|
raise PromotionError, "#{label} product selection references must be a non-empty object"
|
||||||
|
end
|
||||||
|
references.each do |product, reference|
|
||||||
|
unless product.match?(/\A[a-z0-9][a-z0-9-]*\z/) &&
|
||||||
|
reference.is_a?(String) && reference.end_with?(":#{product_selection['source_commit']}")
|
||||||
|
raise PromotionError,
|
||||||
|
"#{label} #{product} selection must end in the full consumer commit tag"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
exact_execution_coordinate = true
|
||||||
|
end
|
||||||
|
|
||||||
|
product_images = runtime["product_images"]
|
||||||
|
if product_images
|
||||||
|
unless product_images.is_a?(Hash) && !product_images.empty?
|
||||||
|
raise PromotionError, "#{label} product_images must be a non-empty object"
|
||||||
|
end
|
||||||
|
product_images.each do |product, coordinate|
|
||||||
|
unless product.match?(/\A[a-z0-9][a-z0-9-]*\z/) && coordinate.is_a?(Hash)
|
||||||
|
raise PromotionError, "#{label} product image entries must use named coordinate objects"
|
||||||
|
end
|
||||||
|
image_id = coordinate["docker_image_id"]
|
||||||
|
image = coordinate["image"]
|
||||||
|
unless (image_id.is_a?(String) && image_id.match?(DIGEST)) ||
|
||||||
|
(image.is_a?(String) && image.match?(IMMUTABLE_IMAGE))
|
||||||
|
raise PromotionError,
|
||||||
|
"#{label} #{product} product image must include an exact sha256 image ID or OCI digest"
|
||||||
|
end
|
||||||
|
validate_full_commit!(coordinate["source_commit"], "#{label} #{product} source commit")
|
||||||
|
exact_execution_coordinate = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
runtime.each do |key, value|
|
runtime.each do |key, value|
|
||||||
if key == "docker_image_id" || key.end_with?("_image_id")
|
if key == "docker_image_id" || key.end_with?("_image_id")
|
||||||
unless value.is_a?(String) && value.match?(DIGEST)
|
unless value.is_a?(String) && value.match?(DIGEST)
|
||||||
@@ -455,6 +759,15 @@ module OpenCodeCompat
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def migration_state_for(manifest)
|
||||||
|
return "certified" if all_consumers_certified?(manifest)
|
||||||
|
|
||||||
|
degraded = manifest.fetch("consumers").any? do |_consumer, entry|
|
||||||
|
entry.dig("rollback_state", "status") == "degraded-no-certified-previous"
|
||||||
|
end
|
||||||
|
degraded ? "bootstrap-current-only" : "candidate"
|
||||||
|
end
|
||||||
|
|
||||||
def with_current_manifest_lock
|
def with_current_manifest_lock
|
||||||
loop do
|
loop do
|
||||||
retry_with_new_inode = false
|
retry_with_new_inode = false
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"required_consumer_profiles": ["rails-persisted-turn"],
|
"required_consumer_profiles": ["rails-persisted-turn"],
|
||||||
"certification_status": "certified",
|
"certification_status": "certified",
|
||||||
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"certified_at": "2026-07-18T22:03:12Z",
|
"certified_at": "2026-07-18T23:35:57Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1
|
"llm_request_count": 1
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"required_consumer_profiles": ["plugin-ledger"],
|
"required_consumer_profiles": ["plugin-ledger"],
|
||||||
"certification_status": "certified",
|
"certification_status": "certified",
|
||||||
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"certified_at": "2026-07-18T22:03:13Z",
|
"certified_at": "2026-07-18T23:35:58Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1
|
"llm_request_count": 1
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"],
|
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"],
|
||||||
"certification_status": "certified",
|
"certification_status": "certified",
|
||||||
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"certified_at": "2026-07-18T22:03:14Z",
|
"certified_at": "2026-07-18T23:35:58Z",
|
||||||
"expected_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
"full_text": "compat-ok",
|
"full_text": "compat-ok",
|
||||||
"llm_request_count": 1
|
"llm_request_count": 1
|
||||||
@@ -51,35 +51,43 @@
|
|||||||
{
|
{
|
||||||
"id": "ajent-blackline-alpha7-canary",
|
"id": "ajent-blackline-alpha7-canary",
|
||||||
"consumer": "ajent-rails",
|
"consumer": "ajent-rails",
|
||||||
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"consumer_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
"tag_provenance": "ajent-opencode-blackline-canary:96552893",
|
"consumer_ref_kind": "pull-request-head",
|
||||||
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
"source_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"promotion_eligible": false,
|
||||||
|
"tag_provenance": "ajent-opencode-blackline-canary:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
|
"source_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
"reported_version": "1.18.3",
|
"reported_version": "1.18.3",
|
||||||
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
"profiles": ["rails-persisted-turn"],
|
"profiles": ["rails-persisted-turn"],
|
||||||
"certification_status": "candidate-certified",
|
"certification_status": "candidate-certified",
|
||||||
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"certified_rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
"certified_rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
"certified_at": "2026-07-18T21:58:00Z",
|
"certified_at": "2026-07-18T23:28:16.435369Z",
|
||||||
"persisted_final_text": "compat-ok",
|
"persisted_final_text": "compat-ok",
|
||||||
"model_request_count": 1
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"model_request_count": 1,
|
||||||
|
"product_artifacts_missing_before_promotion": ["aigl", "raven"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "mushu-permission-shortcuts-alpha7-canary",
|
"id": "mushu-permission-shortcuts-alpha7-canary",
|
||||||
"consumer": "mushu",
|
"consumer": "mushu",
|
||||||
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
|
"consumer_commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
|
"consumer_ref_kind": "pull-request-head",
|
||||||
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
||||||
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
||||||
"reported_version": "0.0.0-permission-shortcuts-202606300721",
|
"reported_version": "0.0.0-permission-shortcuts-202606300721",
|
||||||
"profiles": ["ruby-rest-sse"],
|
"profiles": ["ruby-rest-sse"],
|
||||||
"certification_status": "candidate-certified",
|
"certification_status": "candidate-certified",
|
||||||
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"certified_at": "2026-07-18T21:53:00Z",
|
"certified_at": "2026-07-18T23:26:06.865190Z",
|
||||||
"persisted_final_text": "compat-ok",
|
"authoritative_final_text": "compat-ok",
|
||||||
"model_request_count": 1
|
"provider_request_count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
{
|
{
|
||||||
"schema_version": 1,
|
"$schema": "../schemas/runtime-tuples.schema.json",
|
||||||
|
"schema_version": 2,
|
||||||
"migration_state": "candidate",
|
"migration_state": "candidate",
|
||||||
"promotion_readiness": {
|
"promotion_readiness": {
|
||||||
"status": "blocked",
|
"status": "blocked",
|
||||||
"reason": "Every observed alpha2 baseline fails the hardened exact-final-text contract, so none can be certified as the previous rollback tuple.",
|
"reason": "Every observed alpha2 baseline fails the hardened exact-final-text contract, so none can be certified as the previous rollback tuple; the passing evidence is also bound to unmerged pull-request heads.",
|
||||||
"evidence": "evidence/2026-07-18-opencode-ruby-alpha2-regression.json",
|
"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."
|
"required_action": "Merge, deploy, and canary the exact main/merge tuple (or record an identical-tree attestation plus post-merge canary), then explicitly acknowledge bootstrap-current-only; previous remains null until the next meaningful independently passing release."
|
||||||
},
|
},
|
||||||
"consumers": {
|
"consumers": {
|
||||||
"ajent-rails": {
|
"ajent-rails": {
|
||||||
"profile": "rails-persisted-turn",
|
"profile": "rails-persisted-turn",
|
||||||
"current": {
|
"current": {
|
||||||
"status": "observed-production-contract-failed",
|
"status": "observed-production-contract-failed",
|
||||||
"consumer_commit": "83eed3c247a352842b08fc5cf339c4d0acdaacba",
|
"consumer_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
"opencode_ruby": {
|
"opencode_ruby": {
|
||||||
"version": "0.0.1.alpha2",
|
"version": "0.0.1.alpha2",
|
||||||
"git_commit": "889d38332f98f5f7b76d16952b7204d8a5b9a662"
|
"git_commit": "889d38332f98f5f7b76d16952b7204d8a5b9a662"
|
||||||
@@ -22,21 +23,83 @@
|
|||||||
"git_commit": "9b0c4cd3cd31bdfebbb89567daca012d68a356ea"
|
"git_commit": "9b0c4cd3cd31bdfebbb89567daca012d68a356ea"
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"tag_provenance": "docker-registry:5000/opencode-blackline:83eed3c247a352842b08fc5cf339c4d0acdaacba",
|
"tag_provenance": "docker-registry:5000/opencode-blackline:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
"docker_image_id": "sha256:6fb2b3bf8e4cdf48e16bf6c3475e953df2f8304e77f5efe816ceb46f905eead8",
|
"docker_image_id": "sha256:af479243e6ccf1206f57dffd13b8e9d2c028de401cccc954bcc2f00940274c60",
|
||||||
|
"source_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
"reported_version": "1.18.3",
|
"reported_version": "1.18.3",
|
||||||
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
|
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
|
"upstream_base_proof": {
|
||||||
|
"method": "rootfs-prefix-layer-match",
|
||||||
|
"matched_layer_count": 4,
|
||||||
|
"status": "pass",
|
||||||
|
"note": "The deployed images match the exact 1.18.3 base layers even though this main commit selected the mutable upstream tag at build time."
|
||||||
|
},
|
||||||
|
"deployed_product_selection": {
|
||||||
|
"strategy": "mutable-latest",
|
||||||
|
"status": "observed-configuration-drift-failure",
|
||||||
|
"references": {
|
||||||
|
"aigl": "docker-registry:5000/opencode-aigl:latest",
|
||||||
|
"blackline": "docker-registry:5000/opencode-blackline:latest",
|
||||||
|
"raven": "docker-registry:5000/opencode-raven:latest"
|
||||||
|
},
|
||||||
|
"note": "The shared Blackline accessory used the SHA tag above, but Rails ProductConfig resolved per-user AIGL, Blackline, and Raven containers through mutable latest references."
|
||||||
|
},
|
||||||
|
"ci_built_product_images": {
|
||||||
|
"aigl": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-aigl:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:72c454f39d423618b75439122c9073dbba7c9071421e5f3abd12cf288dc1f898",
|
||||||
|
"source_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808"
|
||||||
|
},
|
||||||
|
"blackline": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-blackline:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:af479243e6ccf1206f57dffd13b8e9d2c028de401cccc954bcc2f00940274c60",
|
||||||
|
"source_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808"
|
||||||
|
},
|
||||||
|
"raven": {
|
||||||
|
"tag_provenance": "docker-registry:5000/opencode-raven:8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"docker_image_id": "sha256:6c4da94a8dec6160f0b1b4d0e2cd9ff38a3faa18dbd2a8d6023a71f6416a544c",
|
||||||
|
"source_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"candidate": {
|
"candidate": {
|
||||||
"status": "compatibility-certified",
|
"status": "compatibility-certified",
|
||||||
"consumer_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
|
"promotion_blockers": [
|
||||||
|
"consumer-ref-is-an-unmerged-pull-request-head",
|
||||||
|
"aigl-and-raven-candidate-content-artifacts-have-not-been-built-and-canaried",
|
||||||
|
"no-certified-previous-tuple"
|
||||||
|
],
|
||||||
|
"consumer_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"consumer_ref": {
|
||||||
|
"kind": "pull-request-head",
|
||||||
|
"repository": "ajaynomics/ajent-rails",
|
||||||
|
"commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"tree": "9e93834ad16b60fa5edfe1cb3baaba193402be74",
|
||||||
|
"base_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/ajent-rails/pulls/946"
|
||||||
|
},
|
||||||
"opencode_ruby": {
|
"opencode_ruby": {
|
||||||
"version": "0.0.1.alpha7",
|
"version": "0.0.1.alpha7",
|
||||||
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"tag_provenance": {
|
"tag_provenance": {
|
||||||
"tag": "v0.0.1.alpha7",
|
"tag": "v0.0.1.alpha7",
|
||||||
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"uri": "https://gitea.krishnan.ca/ajaynomics/opencode-ruby.git",
|
||||||
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"status": "pass",
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"loaded_version": "0.0.1.alpha7",
|
||||||
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"test": "test/lib/opencode/runtime_image_provenance_test.rb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"opencode_rails": {
|
"opencode_rails": {
|
||||||
@@ -45,23 +108,61 @@
|
|||||||
"tag_provenance": {
|
"tag_provenance": {
|
||||||
"tag": "v0.0.1.alpha7",
|
"tag": "v0.0.1.alpha7",
|
||||||
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
|
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"uri": "https://gitea.krishnan.ca/ajaynomics/opencode-rails.git",
|
||||||
|
"requested_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"locked_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"status": "pass",
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"loaded_version": "0.0.1.alpha7",
|
||||||
|
"observed_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"test": "test/lib/opencode/runtime_image_provenance_test.rb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"tag_provenance": "ajent-opencode-blackline-canary:96552893",
|
|
||||||
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
|
|
||||||
"source_commit": "96552893baccd5ccba1592b2585edfef1299b4f9",
|
|
||||||
"reported_version": "1.18.3",
|
"reported_version": "1.18.3",
|
||||||
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
|
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
|
||||||
|
"product_selection": {
|
||||||
|
"strategy": "full-consumer-commit-tag",
|
||||||
|
"source_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"references": {
|
||||||
|
"aigl": "docker-registry:5000/opencode-aigl:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"blackline": "docker-registry:5000/opencode-blackline:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"raven": "docker-registry:5000/opencode-raven:01cdf9da8e1b3f9a205d681e448aefefd9704db2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"product_images": {
|
||||||
|
"blackline": {
|
||||||
|
"tag_provenance": "ajent-opencode-blackline-canary:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
|
||||||
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
|
"source_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2"
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"application_canary": {
|
"deterministic_contract": {
|
||||||
"checked_at": "2026-07-18T21:58:00Z",
|
"checked_at": "2026-07-18T23:16:13Z",
|
||||||
"path": "Blackline::GenerateResponseJob",
|
"docker_image_id": "sha256:075cb49ac7fcb1f8972e051dff96d655fa0d0d911d83a523a6abf03a5e2e2935",
|
||||||
"persisted_final_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
"model_request_count": 1,
|
"model_request_count": 1,
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
},
|
},
|
||||||
"certified_at": "2026-07-18T22:03:14Z"
|
"application_canary": {
|
||||||
|
"checked_at": "2026-07-18T23:28:16.435369Z",
|
||||||
|
"path": "Blackline::GenerateResponseJob",
|
||||||
|
"persisted_final_text": "compat-ok",
|
||||||
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"cleanup_status": "pass",
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"certified_at": "2026-07-18T23:28:16.435369Z"
|
||||||
},
|
},
|
||||||
"previous": null
|
"previous": null
|
||||||
},
|
},
|
||||||
@@ -85,13 +186,41 @@
|
|||||||
},
|
},
|
||||||
"candidate": {
|
"candidate": {
|
||||||
"status": "compatibility-certified",
|
"status": "compatibility-certified",
|
||||||
"consumer_commit": "19747b59fa21e12560afd9edd73ed674ab3a644c",
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
|
"promotion_blockers": [
|
||||||
|
"consumer-ref-is-an-unmerged-pull-request-head",
|
||||||
|
"no-certified-previous-tuple"
|
||||||
|
],
|
||||||
|
"consumer_commit": "b9a2c2dd361a6b2ea547cc67ee817b8eb4ce31e8",
|
||||||
|
"consumer_ref": {
|
||||||
|
"kind": "pull-request-head",
|
||||||
|
"repository": "ajaynomics/travelwolf",
|
||||||
|
"commit": "b9a2c2dd361a6b2ea547cc67ee817b8eb4ce31e8",
|
||||||
|
"tree": "8b3f8a97dc1f1d8b558a49523bf312d2810422df",
|
||||||
|
"base_commit": "75c92ed5248996e5278ac9a3351cc4eaf03df820",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/travelwolf/pulls/29"
|
||||||
|
},
|
||||||
"opencode_ruby": {
|
"opencode_ruby": {
|
||||||
"version": "0.0.1.alpha7",
|
"version": "0.0.1.alpha7",
|
||||||
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"tag_provenance": {
|
"tag_provenance": {
|
||||||
"tag": "v0.0.1.alpha7",
|
"tag": "v0.0.1.alpha7",
|
||||||
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"uri": "https://gitea.krishnan.ca/ajaynomics/opencode-ruby.git",
|
||||||
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"status": "pass",
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"loaded_version": "0.0.1.alpha7",
|
||||||
|
"observed_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"test": "test/services/travelwolf/opencode_env_wiring_test.rb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"opencode_rails": {
|
"opencode_rails": {
|
||||||
@@ -100,22 +229,52 @@
|
|||||||
"tag_provenance": {
|
"tag_provenance": {
|
||||||
"tag": "v0.0.1.alpha7",
|
"tag": "v0.0.1.alpha7",
|
||||||
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
|
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"uri": "https://gitea.krishnan.ca/ajaynomics/opencode-rails.git",
|
||||||
|
"requested_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"locked_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"status": "pass",
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"loaded_version": "0.0.1.alpha7",
|
||||||
|
"observed_ref": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"observed_revision": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
|
||||||
|
"test": "test/services/travelwolf/opencode_env_wiring_test.rb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
||||||
"reported_version": "1.16.1"
|
"reported_version": "1.16.1"
|
||||||
},
|
},
|
||||||
|
"deterministic_contract": {
|
||||||
|
"evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
|
||||||
|
"checked_at": "2026-07-18T23:23:03Z",
|
||||||
|
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
|
||||||
|
"opencode_ruby_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"model_request_count": 1,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
"application_canary": {
|
"application_canary": {
|
||||||
"run_id": "20260718215135-8d8102c0",
|
"run_id": "travelwolf-alpha7-ref-20260718T232211Z-48eadf",
|
||||||
"checked_at": "2026-07-18T21:51:56.552Z",
|
"checked_at": "2026-07-18T23:22:26.216140Z",
|
||||||
"final_text": "TRAVELWOLF_ALPHA7_SUBSCRIBE_READY",
|
"final_text": "TRAVELWOLF_ALPHA7_REF_PIN_READY",
|
||||||
"application_prompt_count": 1,
|
"subscription_ready_before_prompt": true,
|
||||||
"model_request_count_observed": false,
|
"opencode_api_prompt_count": 1,
|
||||||
|
"provider_request_count_observed": false,
|
||||||
|
"message_roles": [
|
||||||
|
"user",
|
||||||
|
"assistant"
|
||||||
|
],
|
||||||
"cleanup_status": "pass",
|
"cleanup_status": "pass",
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
},
|
},
|
||||||
"certified_at": "2026-07-18T22:03:14Z"
|
"certified_at": "2026-07-18T23:35:57Z"
|
||||||
},
|
},
|
||||||
"previous": null
|
"previous": null
|
||||||
},
|
},
|
||||||
@@ -130,36 +289,87 @@
|
|||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
||||||
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
||||||
|
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
|
||||||
"reported_version": "0.0.0-permission-shortcuts-202606300721"
|
"reported_version": "0.0.0-permission-shortcuts-202606300721"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"candidate": {
|
"candidate": {
|
||||||
"status": "compatibility-certified",
|
"status": "compatibility-certified",
|
||||||
"consumer_commit": "f26d958b64f214a422aeb629860c8c9a46a4c084",
|
"certification_scope": "pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion_eligible": false,
|
||||||
|
"promotion_blockers": [
|
||||||
|
"consumer-ref-is-an-unmerged-pull-request-head",
|
||||||
|
"no-certified-previous-tuple"
|
||||||
|
],
|
||||||
|
"consumer_commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
|
"consumer_ref": {
|
||||||
|
"kind": "pull-request-head",
|
||||||
|
"repository": "ajaynomics/mushu",
|
||||||
|
"commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
|
||||||
|
"tree": "762eb8dcc7042a52cd240ca69acdb9cb45150a84",
|
||||||
|
"base_commit": "fe16e5bab028e3c92cd5508ef2cebd9bfa23386f",
|
||||||
|
"review_url": "https://gitea.ajay.to/ajaynomics/mushu/pulls/1"
|
||||||
|
},
|
||||||
"opencode_ruby": {
|
"opencode_ruby": {
|
||||||
"version": "0.0.1.alpha7",
|
"version": "0.0.1.alpha7",
|
||||||
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
"tag_provenance": {
|
"tag_provenance": {
|
||||||
"tag": "v0.0.1.alpha7",
|
"tag": "v0.0.1.alpha7",
|
||||||
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"uri": "https://gitea.krishnan.ca/ajaynomics/opencode-ruby.git",
|
||||||
|
"requested_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"locked_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"status": "pass",
|
||||||
|
"source_class": "Bundler::Source::Git",
|
||||||
|
"loaded_version": "0.0.1.alpha7",
|
||||||
|
"observed_ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"observed_revision": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
|
||||||
|
"loaded_file_within_spec": true,
|
||||||
|
"test": "test/services/mushu/runtime/opencode_dependency_provenance_test.rb"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
|
||||||
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
|
||||||
|
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
|
||||||
"reported_version": "0.0.0-permission-shortcuts-202606300721"
|
"reported_version": "0.0.0-permission-shortcuts-202606300721"
|
||||||
},
|
},
|
||||||
"application_canary": {
|
"deterministic_contract": {
|
||||||
"run_id": "mushu-alpha7-20260718T215300Z-f26d958",
|
"checked_at": "2026-07-18T23:23:41Z",
|
||||||
"checked_at": "2026-07-18T21:53:00Z",
|
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
|
||||||
"persisted_final_text": "compat-ok",
|
"expected_text": "compat-ok",
|
||||||
|
"full_text": "compat-ok",
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
"model_request_count": 1,
|
"model_request_count": 1,
|
||||||
"status": "pass"
|
"status": "pass"
|
||||||
},
|
},
|
||||||
"certified_at": "2026-07-18T22:03:14Z"
|
"application_canary": {
|
||||||
|
"checked_at": "2026-07-18T23:26:06.865190Z",
|
||||||
|
"response_text": "compat-ok",
|
||||||
|
"authoritative_final_text": "compat-ok",
|
||||||
|
"message_roles": [
|
||||||
|
"user",
|
||||||
|
"assistant"
|
||||||
|
],
|
||||||
|
"authoritative_assistant_count": 1,
|
||||||
|
"provider_request_count": 1,
|
||||||
|
"mapping_and_last_turn_persisted": true,
|
||||||
|
"active_claim_cleared": true,
|
||||||
|
"opencode_session_deleted": true,
|
||||||
|
"synthetic_database_rows_after_cleanup": 0,
|
||||||
|
"status": "pass"
|
||||||
|
},
|
||||||
|
"certified_at": "2026-07-18T23:26:06.865190Z"
|
||||||
},
|
},
|
||||||
"previous": null
|
"previous": null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "plugin-ledger",
|
"id": "plugin-ledger",
|
||||||
"owner": "opencode-ajent",
|
"owner": "opencode-ajent",
|
||||||
|
"certification_mode": "executable-consumer-attestation",
|
||||||
|
"shared_ruby_probe_sufficient": false,
|
||||||
"required_behaviors": [
|
"required_behaviors": [
|
||||||
"exact-upstream-image",
|
"exact-upstream-image",
|
||||||
"exact-plugin-version",
|
"exact-plugin-version",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "provider-hooks",
|
"id": "provider-hooks",
|
||||||
"owner": "inference",
|
"owner": "inference",
|
||||||
|
"certification_mode": "executable-consumer-attestation",
|
||||||
|
"shared_ruby_probe_sufficient": false,
|
||||||
"required_behaviors": [
|
"required_behaviors": [
|
||||||
"provider-schema",
|
"provider-schema",
|
||||||
"model-routing",
|
"model-routing",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "rails-persisted-turn",
|
"id": "rails-persisted-turn",
|
||||||
"owner": "opencode-rails",
|
"owner": "opencode-rails",
|
||||||
|
"certification_mode": "executable-consumer-attestation",
|
||||||
|
"shared_ruby_probe_sufficient": false,
|
||||||
"extends": ["ruby-rest-sse"],
|
"extends": ["ruby-rest-sse"],
|
||||||
"required_behaviors": [
|
"required_behaviors": [
|
||||||
"session-recovery",
|
"session-recovery",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "ruby-rest-sse",
|
"id": "ruby-rest-sse",
|
||||||
"owner": "opencode-ruby",
|
"owner": "opencode-ruby",
|
||||||
|
"certification_mode": "shared-ruby-fixture-and-live-probe",
|
||||||
|
"shared_ruby_probe_sufficient": true,
|
||||||
"required_fixtures": [
|
"required_fixtures": [
|
||||||
"legacy-idle",
|
"legacy-idle",
|
||||||
"current-status-idle",
|
"current-status-idle",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "strict-v2",
|
"id": "strict-v2",
|
||||||
"owner": "leela",
|
"owner": "leela",
|
||||||
|
"certification_mode": "executable-consumer-attestation",
|
||||||
|
"shared_ruby_probe_sufficient": false,
|
||||||
"required_behaviors": [
|
"required_behaviors": [
|
||||||
"exact-upstream-version-and-commit",
|
"exact-upstream-version-and-commit",
|
||||||
"bun-toolchain-provenance",
|
"bun-toolchain-provenance",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{
|
{
|
||||||
"id": "voice-stream",
|
"id": "voice-stream",
|
||||||
"owner": "greenroom",
|
"owner": "greenroom",
|
||||||
|
"certification_mode": "executable-consumer-attestation",
|
||||||
|
"shared_ruby_probe_sufficient": false,
|
||||||
"extends": ["ruby-rest-sse"],
|
"extends": ["ruby-rest-sse"],
|
||||||
"required_behaviors": [
|
"required_behaviors": [
|
||||||
"legacy-and-current-terminal-events",
|
"legacy-and-current-terminal-events",
|
||||||
|
|||||||
401
schemas/runtime-tuples.schema.json
Normal file
401
schemas/runtime-tuples.schema.json
Normal file
@@ -0,0 +1,401 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
|
"$id": "https://github.com/ajaynomics/opencode-compat/schemas/runtime-tuples.schema.json",
|
||||||
|
"title": "OpenCode runtime tuple ledger",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"schema_version",
|
||||||
|
"migration_state",
|
||||||
|
"promotion_readiness",
|
||||||
|
"consumers"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"schema_version": {
|
||||||
|
"const": 2
|
||||||
|
},
|
||||||
|
"migration_state": {
|
||||||
|
"enum": [
|
||||||
|
"candidate",
|
||||||
|
"bootstrap-current-only",
|
||||||
|
"certified"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"consumers": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"profile",
|
||||||
|
"current",
|
||||||
|
"candidate",
|
||||||
|
"previous"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"rollback_state": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"acknowledgement",
|
||||||
|
"recorded_at",
|
||||||
|
"emergency_provenance_status"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"const": "degraded-no-certified-previous"
|
||||||
|
},
|
||||||
|
"acknowledgement": {
|
||||||
|
"const": "accept-degraded-rollback-with-failed-emergency-provenance"
|
||||||
|
},
|
||||||
|
"emergency_provenance_status": {
|
||||||
|
"const": "observed-production-contract-failed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/candidate"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"fullCommit": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9a-f]{40}$"
|
||||||
|
},
|
||||||
|
"loadedGitSource": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"type",
|
||||||
|
"uri",
|
||||||
|
"requested_ref",
|
||||||
|
"locked_revision",
|
||||||
|
"loaded_source_proof"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"const": "git"
|
||||||
|
},
|
||||||
|
"requested_ref": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"locked_revision": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"loaded_source_proof": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"source_class",
|
||||||
|
"loaded_version",
|
||||||
|
"observed_revision",
|
||||||
|
"test"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"const": "pass"
|
||||||
|
},
|
||||||
|
"source_class": {
|
||||||
|
"const": "Bundler::Source::Git"
|
||||||
|
},
|
||||||
|
"observed_ref": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"observed_revision": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"version",
|
||||||
|
"git_commit",
|
||||||
|
"source"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"git_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"$ref": "#/$defs/loadedGitSource"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"consumerRef": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"repository",
|
||||||
|
"commit",
|
||||||
|
"tree"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"enum": [
|
||||||
|
"pull-request-head",
|
||||||
|
"main-commit"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"tree": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"base_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "pull-request-head"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": [
|
||||||
|
"base_commit",
|
||||||
|
"review_url"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"promotionProvenance": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"main_commit",
|
||||||
|
"post_merge_canary"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "main-commit"
|
||||||
|
},
|
||||||
|
"main_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"post_merge_canary": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"checked_at",
|
||||||
|
"evidence"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"const": "pass"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^evidence/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"kind",
|
||||||
|
"pull_request_commit",
|
||||||
|
"pull_request_tree",
|
||||||
|
"main_commit",
|
||||||
|
"main_tree",
|
||||||
|
"attested_at",
|
||||||
|
"post_merge_canary"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"kind": {
|
||||||
|
"const": "identical-tree-attestation"
|
||||||
|
},
|
||||||
|
"pull_request_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"pull_request_tree": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"main_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"main_tree": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"post_merge_canary": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"checked_at",
|
||||||
|
"evidence"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"const": "pass"
|
||||||
|
},
|
||||||
|
"evidence": {
|
||||||
|
"type": "array",
|
||||||
|
"minItems": 1,
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^evidence/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"reported_version"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"product_selection": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"strategy",
|
||||||
|
"source_commit",
|
||||||
|
"references"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"strategy": {
|
||||||
|
"const": "full-consumer-commit-tag"
|
||||||
|
},
|
||||||
|
"source_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"references": {
|
||||||
|
"type": "object",
|
||||||
|
"minProperties": 1,
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": ":[0-9a-f]{40}$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"product_images": {
|
||||||
|
"type": "object",
|
||||||
|
"minProperties": 1,
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"source_commit"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"source_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"docker_image_id": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^sha256:[0-9a-f]{64}$"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "@sha256:[0-9a-f]{64}$"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"docker_image_id"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"image"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"candidate": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"status",
|
||||||
|
"certification_scope",
|
||||||
|
"promotion_eligible",
|
||||||
|
"consumer_commit",
|
||||||
|
"consumer_ref",
|
||||||
|
"opencode_ruby",
|
||||||
|
"runtime"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"status": {
|
||||||
|
"const": "compatibility-certified"
|
||||||
|
},
|
||||||
|
"certification_scope": {
|
||||||
|
"enum": [
|
||||||
|
"pre-merge-pr-head-candidate-only",
|
||||||
|
"promotion-deployed"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"promotion_eligible": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"consumer_commit": {
|
||||||
|
"$ref": "#/$defs/fullCommit"
|
||||||
|
},
|
||||||
|
"consumer_ref": {
|
||||||
|
"$ref": "#/$defs/consumerRef"
|
||||||
|
},
|
||||||
|
"promotion_provenance": {
|
||||||
|
"$ref": "#/$defs/promotionProvenance"
|
||||||
|
},
|
||||||
|
"opencode_ruby": {
|
||||||
|
"$ref": "#/$defs/client"
|
||||||
|
},
|
||||||
|
"opencode_rails": {
|
||||||
|
"$ref": "#/$defs/client"
|
||||||
|
},
|
||||||
|
"runtime": {
|
||||||
|
"$ref": "#/$defs/runtime"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"if": {
|
||||||
|
"properties": {
|
||||||
|
"promotion_eligible": {
|
||||||
|
"const": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"then": {
|
||||||
|
"required": [
|
||||||
|
"promotion_provenance"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"certification_scope": {
|
||||||
|
"const": "promotion-deployed"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,10 @@ parser = OptionParser.new do |opts|
|
|||||||
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
|
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
|
||||||
[--previous-status pass --previous-certified-at TIMESTAMP \\
|
[--previous-status pass --previous-certified-at TIMESTAMP \\
|
||||||
--previous-evidence evidence/FILE.json] [--dry-run]
|
--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
|
USAGE
|
||||||
|
|
||||||
opts.on("--consumer NAME") { |value| options["consumer"] = value }
|
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-status STATUS") { |value| options["previous_status"] = value }
|
||||||
opts.on("--previous-certified-at TIMESTAMP") { |value| options["previous_certified_at"] = 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("--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 }
|
opts.on("--dry-run") { options["dry_run"] = true }
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -94,8 +101,42 @@ begin
|
|||||||
"migration_state" => promoted.fetch("migration_state")
|
"migration_state" => promoted.fetch("migration_state")
|
||||||
)
|
)
|
||||||
end
|
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
|
else
|
||||||
raise OptionParser::InvalidArgument, "command must be fingerprint or promote"
|
raise OptionParser::InvalidArgument, "command must be fingerprint, promote, or bootstrap-current"
|
||||||
end
|
end
|
||||||
rescue OpenCodeCompat::PromotionError, OptionParser::ParseError => e
|
rescue OpenCodeCompat::PromotionError, OptionParser::ParseError => e
|
||||||
warn "error: #{e.message}"
|
warn "error: #{e.message}"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class RepositoryTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_every_json_document_parses
|
def test_every_json_document_parses
|
||||||
paths = Dir.glob(File.join(ROOT, "{evidence,fixtures,manifests,profiles}/**/*.json"))
|
paths = Dir.glob(File.join(ROOT, "{evidence,fixtures,manifests,profiles,schemas}/**/*.json"))
|
||||||
refute_empty paths
|
refute_empty paths
|
||||||
paths.each { |path| JSON.parse(File.read(path)) }
|
paths.each { |path| JSON.parse(File.read(path)) }
|
||||||
end
|
end
|
||||||
@@ -36,6 +36,19 @@ class RepositoryTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_only_ruby_rest_sse_can_be_certified_by_the_shared_ruby_probe
|
||||||
|
Dir.glob(File.join(ROOT, "profiles/*.json")).each do |path|
|
||||||
|
profile = JSON.parse(File.read(path))
|
||||||
|
if profile.fetch("id") == "ruby-rest-sse"
|
||||||
|
assert_equal "shared-ruby-fixture-and-live-probe", profile.fetch("certification_mode")
|
||||||
|
assert_equal true, profile.fetch("shared_ruby_probe_sufficient")
|
||||||
|
else
|
||||||
|
assert_equal "executable-consumer-attestation", profile.fetch("certification_mode")
|
||||||
|
assert_equal false, profile.fetch("shared_ruby_probe_sufficient")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_public_matrix_uses_only_immutable_oci_digests
|
def test_public_matrix_uses_only_immutable_oci_digests
|
||||||
targets = json("manifests/image-matrix.json").fetch("public_ci")
|
targets = json("manifests/image-matrix.json").fetch("public_ci")
|
||||||
refute_empty targets
|
refute_empty targets
|
||||||
@@ -78,6 +91,10 @@ class RepositoryTest < Minitest::Test
|
|||||||
|
|
||||||
def test_runtime_tuple_profiles_exist
|
def test_runtime_tuple_profiles_exist
|
||||||
tuples = json("manifests/runtime-tuples.json")
|
tuples = json("manifests/runtime-tuples.json")
|
||||||
|
schema = json("schemas/runtime-tuples.schema.json")
|
||||||
|
|
||||||
|
assert_equal 2, tuples.fetch("schema_version")
|
||||||
|
assert_equal 2, schema.dig("properties", "schema_version", "const")
|
||||||
tuples.fetch("consumers").each_value do |consumer|
|
tuples.fetch("consumers").each_value do |consumer|
|
||||||
profile = consumer.fetch("profile")
|
profile = consumer.fetch("profile")
|
||||||
assert_path_exists File.join(ROOT, "profiles", "#{profile}.json")
|
assert_path_exists File.join(ROOT, "profiles", "#{profile}.json")
|
||||||
@@ -120,6 +137,96 @@ class RepositoryTest < Minitest::Test
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_pull_request_heads_are_explicitly_candidate_only
|
||||||
|
tuples = json("manifests/runtime-tuples.json")
|
||||||
|
|
||||||
|
tuples.fetch("consumers").each do |name, consumer|
|
||||||
|
candidate = consumer.fetch("candidate")
|
||||||
|
reference = candidate.fetch("consumer_ref")
|
||||||
|
|
||||||
|
assert_equal "pre-merge-pr-head-candidate-only", candidate.fetch("certification_scope"), name
|
||||||
|
assert_equal false, candidate.fetch("promotion_eligible"), name
|
||||||
|
assert_equal "pull-request-head", reference.fetch("kind"), name
|
||||||
|
assert_equal candidate.fetch("consumer_commit"), reference.fetch("commit"), name
|
||||||
|
assert_match(/\A[0-9a-f]{40}\z/, reference.fetch("tree"), name)
|
||||||
|
assert_match(%r{\Ahttps://.+/pulls/\d+\z}, reference.fetch("review_url"), name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_candidate_gems_use_peeled_refs_and_loaded_source_proof
|
||||||
|
tuples = json("manifests/runtime-tuples.json")
|
||||||
|
|
||||||
|
tuples.fetch("consumers").each do |consumer_name, consumer|
|
||||||
|
candidate = consumer.fetch("candidate")
|
||||||
|
%w[opencode_ruby opencode_rails].each do |client_name|
|
||||||
|
client = candidate[client_name]
|
||||||
|
next unless client
|
||||||
|
|
||||||
|
commit = client.fetch("git_commit")
|
||||||
|
source = client.fetch("source")
|
||||||
|
proof = source.fetch("loaded_source_proof")
|
||||||
|
|
||||||
|
assert_equal "git", source.fetch("type"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal commit, source.fetch("requested_ref"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal commit, source.fetch("locked_revision"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal "pass", proof.fetch("status"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal "Bundler::Source::Git", proof.fetch("source_class"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal client.fetch("version"), proof.fetch("loaded_version"), "#{consumer_name} #{client_name}"
|
||||||
|
assert_equal commit, proof.fetch("observed_ref"), "#{consumer_name} #{client_name}" if proof.key?("observed_ref")
|
||||||
|
assert_equal commit, proof.fetch("observed_revision"), "#{consumer_name} #{client_name}"
|
||||||
|
refute_empty proof.fetch("test"), "#{consumer_name} #{client_name}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ajent_records_current_selection_drift_and_exact_candidate_selection
|
||||||
|
ajent = json("manifests/runtime-tuples.json").fetch("consumers").fetch("ajent-rails")
|
||||||
|
expected_products = %w[aigl blackline raven]
|
||||||
|
|
||||||
|
current_runtime = ajent.fetch("current").fetch("runtime")
|
||||||
|
assert_match(/\Asha256:[0-9a-f]{64}\z/, current_runtime.fetch("docker_image_id"))
|
||||||
|
deployed = current_runtime.fetch("deployed_product_selection")
|
||||||
|
assert_equal "mutable-latest", deployed.fetch("strategy")
|
||||||
|
assert_equal "observed-configuration-drift-failure", deployed.fetch("status")
|
||||||
|
assert_equal expected_products, deployed.fetch("references").keys.sort
|
||||||
|
deployed.fetch("references").each_value { |reference| assert reference.end_with?(":latest") }
|
||||||
|
|
||||||
|
built = current_runtime.fetch("ci_built_product_images")
|
||||||
|
assert_equal expected_products, built.keys.sort
|
||||||
|
built.each_value do |coordinate|
|
||||||
|
assert_match(/\Asha256:[0-9a-f]{64}\z/, coordinate.fetch("docker_image_id"))
|
||||||
|
assert_equal ajent.dig("current", "consumer_commit"), coordinate.fetch("source_commit")
|
||||||
|
end
|
||||||
|
|
||||||
|
candidate_runtime = ajent.fetch("candidate").fetch("runtime")
|
||||||
|
selected = candidate_runtime.fetch("product_selection")
|
||||||
|
assert_equal expected_products, selected.fetch("references").keys.sort
|
||||||
|
selected.fetch("references").each_value do |reference|
|
||||||
|
assert reference.end_with?(":#{ajent.dig('candidate', 'consumer_commit')}")
|
||||||
|
refute reference.end_with?(":latest")
|
||||||
|
end
|
||||||
|
assert_equal ["blackline"], candidate_runtime.fetch("product_images").keys
|
||||||
|
assert_includes ajent.dig("candidate", "promotion_blockers"),
|
||||||
|
"aigl-and-raven-candidate-content-artifacts-have-not-been-built-and-canaried"
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_manifest_candidates_cannot_be_promoted_from_pre_merge_evidence
|
||||||
|
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
|
||||||
|
tuples = json("manifests/runtime-tuples.json")
|
||||||
|
|
||||||
|
tuples.fetch("consumers").each do |name, consumer|
|
||||||
|
error = assert_raises(OpenCodeCompat::PromotionError) do
|
||||||
|
promoter.promote(
|
||||||
|
consumer: name,
|
||||||
|
consumer_commit: consumer.dig("candidate", "consumer_commit"),
|
||||||
|
certification: {},
|
||||||
|
dry_run: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
assert_match(/pre-merge pull-request evidence is candidate-only/, error.message)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_candidate_evidence_is_bound_to_complete_tuple_fingerprints
|
def test_candidate_evidence_is_bound_to_complete_tuple_fingerprints
|
||||||
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
|
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
|
||||||
evidence_paths = {
|
evidence_paths = {
|
||||||
@@ -138,6 +245,8 @@ class RepositoryTest < Minitest::Test
|
|||||||
assert_equal consumer, evidence.fetch("consumer")
|
assert_equal consumer, evidence.fetch("consumer")
|
||||||
assert_equal fingerprints.fetch("profile"), evidence.fetch("profile")
|
assert_equal fingerprints.fetch("profile"), evidence.fetch("profile")
|
||||||
assert_equal fingerprints.fetch("candidate_tuple_sha256"), evidence.fetch("tuple_sha256")
|
assert_equal fingerprints.fetch("candidate_tuple_sha256"), evidence.fetch("tuple_sha256")
|
||||||
|
assert_equal "pre-merge-pr-head-candidate-only", evidence.fetch("certification_scope")
|
||||||
|
assert_equal false, evidence.fetch("promotion_eligible")
|
||||||
assert_equal "pass", evidence.fetch("status")
|
assert_equal "pass", evidence.fetch("status")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
CANDIDATE_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'b' * 64}"
|
CANDIDATE_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'b' * 64}"
|
||||||
CURRENT_TIME = "2026-07-17T12:00:00Z"
|
CURRENT_TIME = "2026-07-17T12:00:00Z"
|
||||||
CANDIDATE_TIME = "2026-07-18T12:00:00Z"
|
CANDIDATE_TIME = "2026-07-18T12:00:00Z"
|
||||||
|
CANDIDATE_TREE = "7" * 40
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
@root = Dir.mktmpdir("opencode-compat-promotion")
|
@root = Dir.mktmpdir("opencode-compat-promotion")
|
||||||
@@ -25,6 +26,7 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
FileUtils.mkdir_p(File.join(@root, "evidence"))
|
FileUtils.mkdir_p(File.join(@root, "evidence"))
|
||||||
FileUtils.mkdir_p(File.join(@root, "profiles"))
|
FileUtils.mkdir_p(File.join(@root, "profiles"))
|
||||||
File.write(File.join(@root, "profiles", "rails-persisted-turn.json"), "{}\n")
|
File.write(File.join(@root, "profiles", "rails-persisted-turn.json"), "{}\n")
|
||||||
|
write_post_merge_canary_evidence("post-merge-canary.json", CANDIDATE_COMMIT)
|
||||||
write_manifest(valid_manifest)
|
write_manifest(valid_manifest)
|
||||||
@promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: @root)
|
@promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: @root)
|
||||||
end
|
end
|
||||||
@@ -175,6 +177,154 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
assert_equal before, File.binread(manifest_path)
|
assert_equal before, File.binread(manifest_path)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_rejects_pre_merge_pull_request_evidence_as_candidate_only
|
||||||
|
manifest = valid_manifest
|
||||||
|
candidate = manifest.dig("consumers", CONSUMER, "candidate")
|
||||||
|
candidate["certification_scope"] = "pre-merge-pr-head-candidate-only"
|
||||||
|
candidate["promotion_eligible"] = false
|
||||||
|
candidate["consumer_ref"] = {
|
||||||
|
"kind" => "pull-request-head",
|
||||||
|
"repository" => "example/consumer",
|
||||||
|
"commit" => CANDIDATE_COMMIT,
|
||||||
|
"tree" => CANDIDATE_TREE,
|
||||||
|
"base_commit" => CURRENT_COMMIT,
|
||||||
|
"review_url" => "https://example.test/pulls/1"
|
||||||
|
}
|
||||||
|
candidate.delete("promotion_provenance")
|
||||||
|
write_manifest(manifest)
|
||||||
|
candidate_evidence, previous_evidence = write_matching_evidence
|
||||||
|
|
||||||
|
error = assert_raises(OpenCodeCompat::PromotionError) do
|
||||||
|
@promoter.promote(
|
||||||
|
consumer: CONSUMER,
|
||||||
|
consumer_commit: CANDIDATE_COMMIT,
|
||||||
|
certification: certification(CANDIDATE_TIME, candidate_evidence),
|
||||||
|
previous_certification: certification(CURRENT_TIME, previous_evidence),
|
||||||
|
dry_run: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/pre-merge pull-request evidence is candidate-only/, error.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_accepts_explicit_identical_tree_attestation_with_post_merge_canary
|
||||||
|
manifest = valid_manifest
|
||||||
|
candidate = manifest.dig("consumers", CONSUMER, "candidate")
|
||||||
|
candidate["consumer_ref"] = {
|
||||||
|
"kind" => "pull-request-head",
|
||||||
|
"repository" => "example/consumer",
|
||||||
|
"commit" => CANDIDATE_COMMIT,
|
||||||
|
"tree" => CANDIDATE_TREE,
|
||||||
|
"base_commit" => CURRENT_COMMIT,
|
||||||
|
"review_url" => "https://example.test/pulls/1"
|
||||||
|
}
|
||||||
|
main_commit = "8" * 40
|
||||||
|
write_post_merge_canary_evidence("post-merge-attested-canary.json", main_commit)
|
||||||
|
candidate["promotion_provenance"] = {
|
||||||
|
"kind" => "identical-tree-attestation",
|
||||||
|
"pull_request_commit" => CANDIDATE_COMMIT,
|
||||||
|
"pull_request_tree" => CANDIDATE_TREE,
|
||||||
|
"main_commit" => main_commit,
|
||||||
|
"main_tree" => CANDIDATE_TREE,
|
||||||
|
"attested_at" => CANDIDATE_TIME,
|
||||||
|
"post_merge_canary" => {
|
||||||
|
"status" => "pass",
|
||||||
|
"checked_at" => CANDIDATE_TIME,
|
||||||
|
"evidence" => ["evidence/post-merge-attested-canary.json"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write_manifest(manifest)
|
||||||
|
candidate_evidence, previous_evidence = write_matching_evidence
|
||||||
|
|
||||||
|
promoted = @promoter.promote(
|
||||||
|
consumer: CONSUMER,
|
||||||
|
consumer_commit: CANDIDATE_COMMIT,
|
||||||
|
certification: certification(CANDIDATE_TIME, candidate_evidence),
|
||||||
|
previous_certification: certification(CURRENT_TIME, previous_evidence),
|
||||||
|
dry_run: true
|
||||||
|
)
|
||||||
|
|
||||||
|
assert_equal "certified", promoted.dig("consumers", CONSUMER, "current", "status")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_main_commit_promotion_still_requires_a_post_merge_canary
|
||||||
|
manifest = valid_manifest
|
||||||
|
manifest.dig("consumers", CONSUMER, "candidate", "promotion_provenance").delete("post_merge_canary")
|
||||||
|
write_manifest(manifest)
|
||||||
|
|
||||||
|
error = assert_raises(OpenCodeCompat::PromotionError) do
|
||||||
|
@promoter.promote(
|
||||||
|
consumer: CONSUMER,
|
||||||
|
consumer_commit: CANDIDATE_COMMIT,
|
||||||
|
certification: {},
|
||||||
|
dry_run: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/passing post-merge canary/, error.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_rejects_candidate_without_loaded_exact_ref_source_proof
|
||||||
|
manifest = valid_manifest
|
||||||
|
manifest.dig("consumers", CONSUMER, "candidate", "opencode_ruby").delete("source")
|
||||||
|
write_manifest(manifest)
|
||||||
|
|
||||||
|
error = assert_raises(OpenCodeCompat::PromotionError) do
|
||||||
|
@promoter.fingerprints(consumer: CONSUMER, consumer_commit: CANDIDATE_COMMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match(/loaded exact-ref source proof/, error.message)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_explicit_degraded_bootstrap_certifies_current_without_faking_previous
|
||||||
|
manifest = valid_manifest
|
||||||
|
failed = manifest.dig("consumers", CONSUMER, "current")
|
||||||
|
failed["status"] = "observed-production-contract-failed"
|
||||||
|
write_manifest(manifest)
|
||||||
|
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: fingerprint
|
||||||
|
)
|
||||||
|
|
||||||
|
bootstrapped = @promoter.bootstrap_current(
|
||||||
|
consumer: CONSUMER,
|
||||||
|
consumer_commit: CANDIDATE_COMMIT,
|
||||||
|
certification: certification(CANDIDATE_TIME, evidence),
|
||||||
|
acknowledgement: OpenCodeCompat::RuntimeTuplePromoter::DEGRADED_BOOTSTRAP_ACKNOWLEDGEMENT,
|
||||||
|
dry_run: true
|
||||||
|
)
|
||||||
|
|
||||||
|
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.fetch("migration_state")
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_degraded_bootstrap_requires_exact_explicit_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_rejects_evidence_that_does_not_match_the_complete_tuple
|
def test_rejects_evidence_that_does_not_match_the_complete_tuple
|
||||||
candidate, previous = write_matching_evidence
|
candidate, previous = write_matching_evidence
|
||||||
manifest = JSON.parse(File.read(manifest_path))
|
manifest = JSON.parse(File.read(manifest_path))
|
||||||
@@ -254,7 +404,7 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
|
|
||||||
def valid_manifest
|
def valid_manifest
|
||||||
{
|
{
|
||||||
"schema_version" => 1,
|
"schema_version" => 2,
|
||||||
"migration_state" => "candidate",
|
"migration_state" => "candidate",
|
||||||
"consumers" => {
|
"consumers" => {
|
||||||
CONSUMER => {
|
CONSUMER => {
|
||||||
@@ -269,8 +419,25 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
"candidate" => {
|
"candidate" => {
|
||||||
"status" => "compatibility-certified",
|
"status" => "compatibility-certified",
|
||||||
"certified_at" => "2026-07-18T10:00:00Z",
|
"certified_at" => "2026-07-18T10:00:00Z",
|
||||||
"opencode_ruby" => {"version" => "0.0.1.alpha7", "git_commit" => RUBY_CANDIDATE},
|
"certification_scope" => "promotion-deployed",
|
||||||
"opencode_rails" => {"version" => "0.0.1.alpha7", "git_commit" => RAILS_CANDIDATE},
|
"promotion_eligible" => true,
|
||||||
|
"consumer_ref" => {
|
||||||
|
"kind" => "main-commit",
|
||||||
|
"repository" => "example/consumer",
|
||||||
|
"commit" => CANDIDATE_COMMIT,
|
||||||
|
"tree" => CANDIDATE_TREE
|
||||||
|
},
|
||||||
|
"promotion_provenance" => {
|
||||||
|
"kind" => "main-commit",
|
||||||
|
"main_commit" => CANDIDATE_COMMIT,
|
||||||
|
"post_merge_canary" => {
|
||||||
|
"status" => "pass",
|
||||||
|
"checked_at" => CANDIDATE_TIME,
|
||||||
|
"evidence" => ["evidence/post-merge-canary.json"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"opencode_ruby" => client("0.0.1.alpha7", RUBY_CANDIDATE),
|
||||||
|
"opencode_rails" => client("0.0.1.alpha7", RAILS_CANDIDATE),
|
||||||
"runtime" => {"image" => CANDIDATE_IMAGE, "reported_version" => "1.18.3"}
|
"runtime" => {"image" => CANDIDATE_IMAGE, "reported_version" => "1.18.3"}
|
||||||
},
|
},
|
||||||
"previous" => nil
|
"previous" => nil
|
||||||
@@ -279,6 +446,27 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def client(version, commit)
|
||||||
|
{
|
||||||
|
"version" => version,
|
||||||
|
"git_commit" => commit,
|
||||||
|
"source" => {
|
||||||
|
"type" => "git",
|
||||||
|
"uri" => "https://example.test/client.git",
|
||||||
|
"requested_ref" => commit,
|
||||||
|
"locked_revision" => commit,
|
||||||
|
"loaded_source_proof" => {
|
||||||
|
"status" => "pass",
|
||||||
|
"source_class" => "Bundler::Source::Git",
|
||||||
|
"loaded_version" => version,
|
||||||
|
"observed_ref" => commit,
|
||||||
|
"observed_revision" => commit,
|
||||||
|
"test" => "test/dependency_provenance_test.rb"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
def write_matching_evidence
|
def write_matching_evidence
|
||||||
fingerprints = @promoter.fingerprints(consumer: CONSUMER, consumer_commit: CANDIDATE_COMMIT)
|
fingerprints = @promoter.fingerprints(consumer: CONSUMER, consumer_commit: CANDIDATE_COMMIT)
|
||||||
candidate = write_evidence(
|
candidate = write_evidence(
|
||||||
@@ -311,6 +499,16 @@ class RuntimeTuplePromoterTest < Minitest::Test
|
|||||||
{"path" => path, "tuple_sha256" => fingerprint}
|
{"path" => path, "tuple_sha256" => fingerprint}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def write_post_merge_canary_evidence(name, commit)
|
||||||
|
document = {
|
||||||
|
"schema_version" => 1,
|
||||||
|
"status" => "pass",
|
||||||
|
"checked_at" => CANDIDATE_TIME,
|
||||||
|
"consumer_commit" => commit
|
||||||
|
}
|
||||||
|
File.write(File.join(@root, "evidence", name), JSON.pretty_generate(document) + "\n")
|
||||||
|
end
|
||||||
|
|
||||||
def certification(timestamp, evidence)
|
def certification(timestamp, evidence)
|
||||||
{
|
{
|
||||||
"status" => "pass",
|
"status" => "pass",
|
||||||
|
|||||||
Reference in New Issue
Block a user