1 Commits

Author SHA1 Message Date
0d0ab1f23f Harden runtime tuple recertification policy 2026-07-18 16:46:56 -07:00
49 changed files with 1841 additions and 4975 deletions

View File

@@ -6,205 +6,67 @@ on:
pull_request: pull_request:
branches: [main] branches: [main]
workflow_dispatch: workflow_dispatch:
inputs:
permissions: opencode_ruby_ref:
contents: read description: Candidate opencode-ruby ref; blank uses the manifest
required: false
type: string
jobs: jobs:
repository: repository:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1 - uses: ruby/setup-ruby@v1
with: with:
ruby-version: "3.4" ruby-version: "3.4"
- run: | - run: ruby test/repository_test.rb && ruby test/runtime_tuple_promoter_test.rb && ruby test/exact_live_contract_test.rb
ruby test/repository_test.rb
ruby test/runtime_tuple_promoter_test.rb
ruby test/exact_live_contract_test.rb
ruby test/watcher_test.rb
ruby test/client_candidate_test.rb
ruby test/lockstep_client_contract_test.rb
ruby test/matrix_json_test.rb
ruby test/image_contract_evidence_test.rb
prepare: prepare:
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs: outputs:
matrix: ${{ steps.matrix.outputs.value }} matrix: ${{ steps.matrix.outputs.value }}
publication_state: ${{ steps.clients.outputs.publication_state }} client_ref: ${{ steps.client.outputs.value }}
ruby_provenance_kind: ${{ steps.clients.outputs.ruby_provenance_kind }}
ruby_ref: ${{ steps.clients.outputs.ruby_ref }}
ruby_version: ${{ steps.clients.outputs.ruby_version }}
ruby_tag: ${{ steps.clients.outputs.ruby_tag }}
ruby_tag_object: ${{ steps.clients.outputs.ruby_tag_object }}
rails_provenance_kind: ${{ steps.clients.outputs.rails_provenance_kind }}
rails_ref: ${{ steps.clients.outputs.rails_ref }}
rails_version: ${{ steps.clients.outputs.rails_version }}
rails_tag: ${{ steps.clients.outputs.rails_tag }}
rails_tag_object: ${{ steps.clients.outputs.rails_tag_object }}
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1
with:
ruby-version: "3.4"
- id: matrix - id: matrix
run: echo "value=$(ruby scripts/matrix_json.rb)" >> "$GITHUB_OUTPUT" run: echo "value=$(ruby scripts/matrix_json.rb)" >> "$GITHUB_OUTPUT"
- id: clients - id: client
run: ruby scripts/client_candidate_outputs.rb >> "$GITHUB_OUTPUT" env:
INPUT_REF: ${{ inputs.opencode_ruby_ref }}
run: |
if [ -n "$INPUT_REF" ]; then
echo "value=$INPUT_REF" >> "$GITHUB_OUTPUT"
else
echo "value=$(jq -r .ref manifests/client-candidate.json)" >> "$GITHUB_OUTPUT"
fi
fixture-contract: fixture-contract:
needs: prepare needs: prepare
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
with: with:
repository: ajaynomics/opencode-ruby repository: ajaynomics/opencode-ruby
ref: ${{ needs.prepare.outputs.ruby_ref }} ref: ${{ needs.prepare.outputs.client_ref }}
path: ruby-client path: client
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1 - uses: ruby/setup-ruby@v1
with: with:
ruby-version: "3.4" ruby-version: "3.4"
bundler-cache: ${{ github.server_url == 'https://github.com' }} bundler-cache: true
working-directory: ruby-client working-directory: client
- name: Install candidate dependencies without the unavailable Gitea cache service
if: github.server_url != 'https://github.com'
working-directory: ruby-client
env:
BUNDLE_APP_CONFIG: ${{ runner.temp }}/opencode-ruby-fixture-bundle-config
BUNDLE_PATH: ${{ runner.temp }}/opencode-ruby-fixture-bundle
run: |
echo "BUNDLE_APP_CONFIG=$BUNDLE_APP_CONFIG" >> "$GITHUB_ENV"
echo "BUNDLE_PATH=$BUNDLE_PATH" >> "$GITHUB_ENV"
bundle install --jobs 4 --retry 3
- id: client-sha
name: Verify immutable Ruby candidate checkout
env:
EXPECTED_CLIENT_SHA: ${{ needs.prepare.outputs.ruby_ref }}
run: |
actual_client_sha="$(git -C ruby-client rev-parse HEAD)"
test "$actual_client_sha" = "$EXPECTED_CLIENT_SHA"
echo "value=$actual_client_sha" >> "$GITHUB_OUTPUT"
- name: Candidate unit tests - name: Candidate unit tests
working-directory: ruby-client working-directory: client
run: bundle exec rake test run: bundle exec rake test
- name: Shared fixture contract - name: Shared fixture contract
working-directory: ruby-client working-directory: client
env: env:
OPENCODE_RUBY_PATH: ${{ github.workspace }}/ruby-client OPENCODE_RUBY_PATH: ${{ github.workspace }}/client
OPENCODE_RUBY_COMMIT: ${{ steps.client-sha.outputs.value }} OPENCODE_RUBY_COMMIT: ${{ needs.prepare.outputs.client_ref }}
OPENCODE_COMPAT_EVIDENCE_PATH: ${{ github.workspace }}/.compat-evidence/ruby-fixtures.json run: bundle exec ruby ../ruby/opencode_ruby_fixture_contract.rb
OPENCODE_COMPAT_RUN_ID: ${{ github.run_id }}
OPENCODE_COMPAT_RUN_ATTEMPT: ${{ github.run_attempt }}
OPENCODE_COMPAT_HEAD_SHA: ${{ github.sha }}
OPENCODE_COMPAT_REPOSITORY: ${{ github.repository }}
OPENCODE_COMPAT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
mkdir -p ../.compat-evidence
bundle exec ruby ../ruby/opencode_ruby_fixture_contract.rb
- name: Upload fixture evidence
if: always() && github.server_url == 'https://github.com'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ruby-fixtures-${{ github.run_id }}-${{ github.run_attempt }}
path: .compat-evidence/ruby-fixtures.json
include-hidden-files: true
if-no-files-found: warn
retention-days: 30
- name: State the Gitea evidence boundary
if: always() && github.server_url != 'https://github.com'
run: echo "Gitea executed the fixture contract, but this forge has no supported immutable artifact upload; generated JSON is transient and is not review evidence."
lockstep-client-contract:
needs: prepare
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
ruby: ["3.2", "3.3", "3.4", "4.0"]
name: lockstep clients Ruby ${{ matrix.ruby }}
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ajaynomics/opencode-ruby
ref: ${{ needs.prepare.outputs.ruby_ref }}
path: ruby-client
fetch-depth: 0
fetch-tags: true
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ajaynomics/opencode-rails
ref: ${{ needs.prepare.outputs.rails_ref }}
path: opencode-rails
fetch-depth: 0
fetch-tags: true
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: ${{ github.server_url == 'https://github.com' }}
working-directory: opencode-rails
- name: Install candidate dependencies without the unavailable Gitea cache service
if: github.server_url != 'https://github.com'
working-directory: opencode-rails
env:
BUNDLE_APP_CONFIG: ${{ runner.temp }}/opencode-rails-${{ matrix.ruby }}-bundle-config
BUNDLE_PATH: ${{ runner.temp }}/opencode-rails-${{ matrix.ruby }}-bundle
run: |
echo "BUNDLE_APP_CONFIG=$BUNDLE_APP_CONFIG" >> "$GITHUB_ENV"
echo "BUNDLE_PATH=$BUNDLE_PATH" >> "$GITHUB_ENV"
bundle install --jobs 4 --retry 3
- name: Verify immutable candidate checkouts
env:
EXPECTED_RUBY_SHA: ${{ needs.prepare.outputs.ruby_ref }}
EXPECTED_RAILS_SHA: ${{ needs.prepare.outputs.rails_ref }}
run: |
test "$(git -C ruby-client rev-parse HEAD)" = "$EXPECTED_RUBY_SHA"
test "$(git -C opencode-rails rev-parse HEAD)" = "$EXPECTED_RAILS_SHA"
- name: Rails candidate unit tests
working-directory: opencode-rails
run: bundle exec rake test
- name: Lockstep client provenance contract
working-directory: opencode-rails
env:
OPENCODE_RUBY_PATH: ${{ github.workspace }}/ruby-client
OPENCODE_RAILS_PATH: ${{ github.workspace }}/opencode-rails
OPENCODE_CLIENT_PUBLICATION_STATE: ${{ needs.prepare.outputs.publication_state }}
OPENCODE_RUBY_COMMIT: ${{ needs.prepare.outputs.ruby_ref }}
OPENCODE_RUBY_PROVENANCE_KIND: ${{ needs.prepare.outputs.ruby_provenance_kind }}
OPENCODE_RUBY_TAG: ${{ needs.prepare.outputs.ruby_tag }}
OPENCODE_RUBY_TAG_OBJECT: ${{ needs.prepare.outputs.ruby_tag_object }}
OPENCODE_RAILS_COMMIT: ${{ needs.prepare.outputs.rails_ref }}
OPENCODE_RAILS_PROVENANCE_KIND: ${{ needs.prepare.outputs.rails_provenance_kind }}
OPENCODE_RAILS_TAG: ${{ needs.prepare.outputs.rails_tag }}
OPENCODE_RAILS_TAG_OBJECT: ${{ needs.prepare.outputs.rails_tag_object }}
OPENCODE_RUBY_VERSION: ${{ needs.prepare.outputs.ruby_version }}
OPENCODE_RAILS_VERSION: ${{ needs.prepare.outputs.rails_version }}
OPENCODE_COMPAT_EVIDENCE_PATH: ${{ github.workspace }}/.compat-evidence/lockstep-ruby-${{ matrix.ruby }}.json
OPENCODE_COMPAT_RUN_ID: ${{ github.run_id }}
OPENCODE_COMPAT_RUN_ATTEMPT: ${{ github.run_attempt }}
OPENCODE_COMPAT_HEAD_SHA: ${{ github.sha }}
OPENCODE_COMPAT_REPOSITORY: ${{ github.repository }}
OPENCODE_COMPAT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
mkdir -p ../.compat-evidence
bundle exec ruby ../ruby/lockstep_client_contract.rb
- name: Upload lockstep evidence
if: always() && github.server_url == 'https://github.com'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: lockstep-ruby-${{ matrix.ruby }}-${{ github.run_id }}-${{ github.run_attempt }}
path: .compat-evidence/lockstep-ruby-${{ matrix.ruby }}.json
include-hidden-files: true
if-no-files-found: warn
retention-days: 30
- name: State the Gitea evidence boundary
if: always() && github.server_url != 'https://github.com'
run: echo "Gitea executed the lockstep client contract, but this forge has no supported immutable artifact upload; generated JSON is transient and is not review evidence."
exact-image-contract: exact-image-contract:
if: github.server_url == 'https://github.com'
needs: prepare needs: prepare
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@@ -212,109 +74,20 @@ jobs:
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }} matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
name: image ${{ matrix.id }} name: image ${{ matrix.id }}
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
with: with:
repository: ajaynomics/opencode-ruby repository: ajaynomics/opencode-ruby
ref: ${{ needs.prepare.outputs.ruby_ref }} ref: ${{ needs.prepare.outputs.client_ref }}
path: ruby-client path: client
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1 - uses: ruby/setup-ruby@v1
with: with:
ruby-version: "3.4" ruby-version: "3.4"
- name: Install candidate dependencies outside the checkout bundler-cache: true
working-directory: ruby-client working-directory: client
env:
BUNDLE_APP_CONFIG: ${{ runner.temp }}/opencode-ruby-bundle-config
BUNDLE_PATH: ${{ runner.temp }}/opencode-ruby-bundle
run: bundle install --jobs 4 --retry 3
- id: client-sha
name: Verify immutable Ruby candidate checkout
env:
EXPECTED_CLIENT_SHA: ${{ needs.prepare.outputs.ruby_ref }}
run: |
actual_client_sha="$(git -C ruby-client rev-parse HEAD)"
test "$actual_client_sha" = "$EXPECTED_CLIENT_SHA"
echo "value=$actual_client_sha" >> "$GITHUB_OUTPUT"
- name: Exercise complete REST and SSE turn - name: Exercise complete REST and SSE turn
env: env:
BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile BUNDLE_GEMFILE: ${{ github.workspace }}/client/Gemfile
BUNDLE_APP_CONFIG: ${{ runner.temp }}/opencode-ruby-bundle-config
BUNDLE_PATH: ${{ runner.temp }}/opencode-ruby-bundle
OPENCODE_IMAGE: ${{ matrix.image }} OPENCODE_IMAGE: ${{ matrix.image }}
OPENCODE_EXPECTED_VERSION: ${{ matrix.version }} OPENCODE_RUBY_PATH: ${{ github.workspace }}/client
OPENCODE_MATRIX_ID: ${{ matrix.id }} run: bundle exec scripts/run_image_contract.sh
OPENCODE_REQUIRED_CONSUMER_PROFILES: ${{ toJSON(matrix.required_consumer_profiles) }}
OPENCODE_RUBY_PATH: ${{ github.workspace }}/ruby-client
OPENCODE_RUBY_COMMIT: ${{ steps.client-sha.outputs.value }}
OPENCODE_RAILS_COMMIT: ${{ needs.prepare.outputs.rails_ref }}
OPENCODE_COMPAT_EVIDENCE_PATH: ${{ github.workspace }}/.compat-evidence/${{ matrix.id }}.json
OPENCODE_COMPAT_RUN_ID: ${{ github.run_id }}
OPENCODE_COMPAT_RUN_ATTEMPT: ${{ github.run_attempt }}
OPENCODE_COMPAT_HEAD_SHA: ${{ github.sha }}
OPENCODE_COMPAT_REPOSITORY: ${{ github.repository }}
OPENCODE_COMPAT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: |
mkdir -p .compat-evidence
bundle exec scripts/run_image_contract.sh
- name: Upload exact-image evidence
if: always() && github.server_url == 'https://github.com'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: image-${{ matrix.id }}-${{ github.run_id }}-${{ github.run_attempt }}
path: .compat-evidence/${{ matrix.id }}.json
include-hidden-files: true
if-no-files-found: warn
retention-days: 30
exact-image-contract-gitea:
if: github.server_url != 'https://github.com'
needs: prepare
runs-on: ubuntu-latest
name: Gitea full exact image matrix
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
repository: ajaynomics/opencode-ruby
ref: ${{ needs.prepare.outputs.ruby_ref }}
path: ruby-client
- uses: ruby/setup-ruby@003a5c4d8d6321bd302e38f6f0ec593f77f06600 # v1
with:
ruby-version: "3.4"
- name: Install candidate dependencies outside the checkout
working-directory: ruby-client
env:
BUNDLE_APP_CONFIG: ${{ runner.temp }}/opencode-ruby-bundle-config
BUNDLE_PATH: ${{ runner.temp }}/opencode-ruby-bundle
run: |
echo "BUNDLE_APP_CONFIG=$BUNDLE_APP_CONFIG" >> "$GITHUB_ENV"
echo "BUNDLE_PATH=$BUNDLE_PATH" >> "$GITHUB_ENV"
bundle install --jobs 4 --retry 3
- id: client-sha
name: Verify immutable Ruby candidate checkout
env:
EXPECTED_CLIENT_SHA: ${{ needs.prepare.outputs.ruby_ref }}
run: |
actual_client_sha="$(git -C ruby-client rev-parse HEAD)"
test "$actual_client_sha" = "$EXPECTED_CLIENT_SHA"
echo "value=$actual_client_sha" >> "$GITHUB_OUTPUT"
- name: Resolve the private Docker host transport
run: |
probe_host="$(ruby scripts/private_default_gateway.rb)"
echo "OPENCODE_PROBE_HOST=$probe_host" >> "$GITHUB_ENV"
- name: Exercise the complete manifest matrix without artifact claims
env:
BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile
OPENCODE_RUBY_PATH: ${{ github.workspace }}/ruby-client
OPENCODE_RUBY_COMMIT: ${{ steps.client-sha.outputs.value }}
OPENCODE_RAILS_COMMIT: ${{ needs.prepare.outputs.rails_ref }}
OPENCODE_COMPAT_EVIDENCE_DIR: ${{ github.workspace }}/.compat-evidence
OPENCODE_COMPAT_RUN_ID: ${{ github.run_id }}
OPENCODE_COMPAT_RUN_ATTEMPT: ${{ github.run_attempt }}
OPENCODE_COMPAT_HEAD_SHA: ${{ github.sha }}
OPENCODE_COMPAT_REPOSITORY: ${{ github.repository }}
OPENCODE_COMPAT_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
run: scripts/run_image_matrix_contract.sh
- name: State the Gitea evidence boundary
if: always()
run: echo "Gitea's exact-image path uploads nothing; generated JSON is transient and is not review evidence. This job passes only after every manifest image passes."

View File

@@ -15,10 +15,9 @@ concurrency:
jobs: jobs:
open-compatibility-pr: open-compatibility-pr:
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Resolve latest release - name: Resolve latest release
@@ -28,153 +27,55 @@ jobs:
run: | run: |
release="$(gh api repos/anomalyco/opencode/releases/latest)" release="$(gh api repos/anomalyco/opencode/releases/latest)"
tag="$(jq -r .tag_name <<<"$release")" tag="$(jq -r .tag_name <<<"$release")"
digest="$(docker buildx imagetools inspect "ghcr.io/anomalyco/opencode:${tag#v}" --format '{{json .Manifest}}' | jq -r .digest)"
image="ghcr.io/anomalyco/opencode@$digest"
published_at="$(jq -r .published_at <<<"$release")"
release_url="$(jq -r .html_url <<<"$release")"
current="$(jq -r .release_tag manifests/upstream.json)" current="$(jq -r .release_tag manifests/upstream.json)"
current_image="$(jq -r .image manifests/upstream.json)"
current_image_count="$(
jq \
--arg image "$image" \
'[.public_ci[]? | select(.image == $image)] | length' \
manifests/image-matrix.json
)"
ruby scripts/upstream_branch_name.rb "$tag" "$digest" >/dev/null
ruby -rtime -e 'Time.iso8601(ARGV.fetch(0))' "$published_at"
test "$release_url" = "https://github.com/anomalyco/opencode/releases/tag/$tag"
echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "tag=$tag" >> "$GITHUB_OUTPUT"
echo "digest=$digest" >> "$GITHUB_OUTPUT" echo "published_at=$(jq -r .published_at <<<"$release")" >> "$GITHUB_OUTPUT"
echo "published_at=$published_at" >> "$GITHUB_OUTPUT" echo "release_url=$(jq -r .html_url <<<"$release")" >> "$GITHUB_OUTPUT"
echo "release_url=$release_url" >> "$GITHUB_OUTPUT" if [ "$tag" = "$current" ]; then
if [ "$tag" = "$current" ] && [ "$image" != "$current_image" ]; then
echo "same_tag_digest_changed=true" >> "$GITHUB_OUTPUT"
else
echo "same_tag_digest_changed=false" >> "$GITHUB_OUTPUT"
fi
if [ "$tag" = "$current" ] && [ "$image" = "$current_image" ] && [ "$current_image_count" -eq 1 ]; then
echo "changed=false" >> "$GITHUB_OUTPUT" echo "changed=false" >> "$GITHUB_OUTPUT"
else else
echo "changed=true" >> "$GITHUB_OUTPUT" echo "changed=true" >> "$GITHUB_OUTPUT"
fi fi
- name: Resolve collision-safe compatibility branch - name: Stop when a PR already exists
if: steps.release.outputs.changed == 'true' if: steps.release.outputs.changed == 'true'
id: branch id: existing
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.release.outputs.tag }} TAG: ${{ steps.release.outputs.tag }}
DIGEST: ${{ steps.release.outputs.digest }}
run: | run: |
base_branch="$(ruby scripts/upstream_branch_name.rb "$TAG" "$DIGEST")" branch="compat/upstream-${TAG#v}"
expected_image="ghcr.io/anomalyco/opencode@$DIGEST" count="$(gh pr list --state open --head "$branch" --json number --jq length)"
open_branch=""
open_prs="$(
gh pr list --state open --base main --limit 1000 \
--json baseRefName,headRefName,headRefOid,headRepository,headRepositoryOwner,isCrossRepository
)"
while IFS=$'\t' read -r candidate_branch head_oid; do
[ -n "$candidate_branch" ] || continue
if ! candidate_upstream="$(
gh api \
-H "Accept: application/vnd.github.raw+json" \
"repos/$GITHUB_REPOSITORY/contents/manifests/upstream.json?ref=$head_oid" 2>/dev/null
)"; then
continue
fi
if ! candidate_matrix="$(
gh api \
-H "Accept: application/vnd.github.raw+json" \
"repos/$GITHUB_REPOSITORY/contents/manifests/image-matrix.json?ref=$head_oid" 2>/dev/null
)"; then
continue
fi
if jq -e \
--arg tag "$TAG" \
--arg image "$expected_image" \
'.release_tag == $tag and .image == $image' \
>/dev/null <<<"$candidate_upstream" &&
jq -e \
--arg image "$expected_image" \
'([.public_ci[]? | select(.image == $image)] | length) == 1' \
>/dev/null <<<"$candidate_matrix"; then
open_branch="$candidate_branch"
break
fi
done < <(
jq -r \
--arg base "$base_branch" \
--arg owner "${GITHUB_REPOSITORY%%/*}" \
--arg repository "${GITHUB_REPOSITORY#*/}" \
'.[] | select(
.baseRefName == "main" and
.isCrossRepository == false and
.headRepositoryOwner.login == $owner and
.headRepository.name == $repository and
(.headRefName == $base or (.headRefName | startswith($base + "-r")))
) | [.headRefName, .headRefOid] | @tsv' \
<<<"$open_prs"
)
if [ -n "$open_branch" ]; then
echo "branch=$open_branch" >> "$GITHUB_OUTPUT"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
branch="$base_branch"
existing_count="$(
gh pr list --state all --base main --head "$base_branch" --limit 1000 \
--json baseRefName,headRepository,headRepositoryOwner,isCrossRepository |
jq \
--arg owner "${GITHUB_REPOSITORY%%/*}" \
--arg repository "${GITHUB_REPOSITORY#*/}" \
'[.[] | select(
.baseRefName == "main" and
.isCrossRepository == false and
.headRepositoryOwner.login == $owner and
.headRepository.name == $repository
)] | length'
)"
if [ "$existing_count" -gt 0 ] || git ls-remote --exit-code --heads origin "$base_branch" >/dev/null 2>&1; then
branch="$(ruby scripts/upstream_branch_name.rb "$TAG" "$DIGEST" "${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}")"
fi
echo "branch=$branch" >> "$GITHUB_OUTPUT" echo "branch=$branch" >> "$GITHUB_OUTPUT"
if [ "$count" -gt 0 ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT" echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Resolve immutable image and update manifests - name: Resolve immutable image and update manifests
if: steps.release.outputs.changed == 'true' && steps.branch.outputs.skip == 'false' if: steps.release.outputs.changed == 'true' && steps.existing.outputs.skip == 'false'
env: env:
TAG: ${{ steps.release.outputs.tag }} TAG: ${{ steps.release.outputs.tag }}
PUBLISHED_AT: ${{ steps.release.outputs.published_at }} PUBLISHED_AT: ${{ steps.release.outputs.published_at }}
RELEASE_URL: ${{ steps.release.outputs.release_url }} RELEASE_URL: ${{ steps.release.outputs.release_url }}
DIGEST: ${{ steps.release.outputs.digest }}
run: | run: |
ruby scripts/record_upstream_candidate.rb "$TAG" "$PUBLISHED_AT" "$RELEASE_URL" "$DIGEST" digest="$(docker buildx imagetools inspect "ghcr.io/anomalyco/opencode:${TAG#v}" --format '{{json .Manifest}}' | jq -r .digest)"
ruby scripts/record_upstream_candidate.rb "$TAG" "$PUBLISHED_AT" "$RELEASE_URL" "$digest"
ruby test/repository_test.rb ruby test/repository_test.rb
ruby test/watcher_test.rb
- name: Open compatibility PR - name: Open compatibility PR
if: steps.release.outputs.changed == 'true' && steps.branch.outputs.skip == 'false' if: steps.release.outputs.changed == 'true' && steps.existing.outputs.skip == 'false'
env: env:
GH_TOKEN: ${{ github.token }} GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.release.outputs.tag }} TAG: ${{ steps.release.outputs.tag }}
DIGEST: ${{ steps.release.outputs.digest }} BRANCH: ${{ steps.existing.outputs.branch }}
BRANCH: ${{ steps.branch.outputs.branch }}
SAME_TAG_DIGEST_CHANGED: ${{ steps.release.outputs.same_tag_digest_changed }}
run: | run: |
git config user.name "opencode-compat-bot" git config user.name "opencode-compat-bot"
git config user.email "opencode-compat-bot@users.noreply.github.com" git config user.email "opencode-compat-bot@users.noreply.github.com"
short_digest="${DIGEST#sha256:}"
short_digest="${short_digest:0:12}"
git switch -c "$BRANCH" git switch -c "$BRANCH"
git add manifests/image-matrix.json manifests/upstream.json git add manifests/image-matrix.json manifests/upstream.json
git commit -m "Test OpenCode ${TAG} compatibility (${short_digest})" git commit -m "Test OpenCode ${TAG} compatibility"
git push --set-upstream origin "$BRANCH" git push --set-upstream origin "$BRANCH"
title="Test OpenCode ${TAG} compatibility (${short_digest})"
notice="Upstream watcher resolved ${TAG} to immutable OCI digest ${DIGEST}."
if [ "$SAME_TAG_DIGEST_CHANGED" = "true" ]; then
title="Review changed OCI digest for existing OpenCode ${TAG} (${short_digest})"
notice="Security-sensitive change: existing release ${TAG} now resolves to a different OCI digest, ${DIGEST}."
fi
gh pr create \ gh pr create \
--base main \ --base main \
--head "$BRANCH" \ --head "$BRANCH" \
--title "$title" \ --title "Test OpenCode ${TAG} compatibility" \
--body "${notice} The watcher added it as a pending compatibility target. This PR only runs certification checks; promotion and consumer rollout remain manual." --body "Upstream watcher detected ${TAG}, resolved its immutable OCI digest, and added it as a pending compatibility target. This PR only runs certification checks; promotion and consumer rollout remain manual."

107
README.md
View File

@@ -8,25 +8,37 @@ prompts, credentials, sessions, or user data belong here.
## Policy ## Policy
- An unpublished gem build is a candidate until the fixture suite and every - A gem release is a candidate until the fixture suite and every required image
required image profile pass. It is identified only by an exact commit. profile pass.
- `opencode-ruby` and `opencode-rails` are one candidate release train. The
manifest records both exact commits, and CI rejects a Rails candidate whose
exact runtime dependency resolves to a different Ruby version or commit.
- Once a train is published, commit-only provenance is no longer accepted. The
manifest and lockstep contract require each annotated tag object, local tag
ref, and peeled commit to bind to the tested checkout.
- Image references are immutable OCI index digests. A tag may be recorded as - Image references are immutable OCI index digests. A tag may be recorded as
human-readable provenance, but it is never an execution coordinate. human-readable provenance, but it is never an execution coordinate.
- Custom consumer images are certified with an isolated canary on the host that - Custom consumer images are certified with an isolated canary on the host that
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
@@ -52,26 +64,11 @@ Prerequisites are Ruby 3.2+, Python 3, `jq`, and Docker.
ruby test/repository_test.rb ruby test/repository_test.rb
ruby test/runtime_tuple_promoter_test.rb ruby test/runtime_tuple_promoter_test.rb
ruby test/exact_live_contract_test.rb ruby test/exact_live_contract_test.rb
ruby test/watcher_test.rb OPENCODE_RUBY_PATH=/path/to/opencode-ruby-at-78b6f9c9e9c7d58b699af1c3c17764acd33de798 \
ruby test/client_candidate_test.rb
OPENCODE_RUBY_PATH=/path/to/opencode-ruby-at-9277646a4bb2cf25a8384ffc140b154f49ea5766 \
ruby ruby/opencode_ruby_fixture_contract.rb ruby ruby/opencode_ruby_fixture_contract.rb
BUNDLE_GEMFILE=/path/to/opencode-rails/Gemfile \ OPENCODE_RUBY_PATH=/path/to/opencode-ruby-at-78b6f9c9e9c7d58b699af1c3c17764acd33de798 \
OPENCODE_RUBY_PATH=/path/to/opencode-ruby \
OPENCODE_RAILS_PATH=/path/to/opencode-rails \
OPENCODE_CLIENT_PUBLICATION_STATE=unpublished \
OPENCODE_RUBY_COMMIT=9277646a4bb2cf25a8384ffc140b154f49ea5766 \
OPENCODE_RUBY_PROVENANCE_KIND=commit \
OPENCODE_RAILS_COMMIT=a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f \
OPENCODE_RAILS_PROVENANCE_KIND=commit \
OPENCODE_RUBY_VERSION=0.0.1.alpha8 \
OPENCODE_RAILS_VERSION=0.0.1.alpha8 \
bundle exec ruby ruby/lockstep_client_contract.rb
BUNDLE_GEMFILE=/path/to/opencode-ruby-at-9277646a4bb2cf25a8384ffc140b154f49ea5766/Gemfile \
OPENCODE_RUBY_PATH=/path/to/opencode-ruby-at-9277646a4bb2cf25a8384ffc140b154f49ea5766 \
OPENCODE_RUBY_COMMIT=9277646a4bb2cf25a8384ffc140b154f49ea5766 \
OPENCODE_IMAGE='ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a' \ OPENCODE_IMAGE='ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a' \
bundle exec scripts/run_image_contract.sh scripts/run_image_contract.sh
``` ```
The live contract starts a deterministic local OpenAI-compatible model stub and The live contract starts a deterministic local OpenAI-compatible model stub and
@@ -79,54 +76,18 @@ an isolated OpenCode container. It creates a session, subscribes, submits an
async prompt, observes terminal SSE, fetches the authoritative exchange, and async prompt, observes terminal SSE, fetches the authoritative exchange, and
deletes the session. Passing requires the authoritative final text to equal the deletes the session. Passing requires the authoritative final text to equal the
expected text byte-for-byte and the deterministic model stub to observe exactly expected text byte-for-byte and the deterministic model stub to observe exactly
one request and exactly one authoritative assistant message. It never calls an one request. It never calls an external model provider.
external model provider.
On GitHub, candidate CI uploads machine-readable fixture, lockstep-client, and
exact-image evidence for 30 days. These artifacts are review inputs, not
certification by themselves. Gitea runs the same repository, fixture, and
lockstep contracts and executes every exact-image manifest entry sequentially,
because its runner cannot expand a matrix from a dependency job's JSON output.
The installed Gitea artifact service does not support the pinned GitHub upload
action, so Gitea deliberately uploads nothing: its generated JSON is transient
and must not be described as review evidence. A person must review a passing
GitHub artifact and commit the durable certification document under `evidence/`;
no workflow commits or promotes its own result.
`manifests/image-matrix.json` binds the certified shared-client matrix to both
exact, unpublished alpha8 commits. Each public row is explicitly scoped to the
shared Ruby/SSE contract; its separate `required_consumer_profiles` are not
implied by that result. The host rows bind the Ajent, Travelwolf, and Mushu
application canaries to their exact current alpha8 and previous alpha7 consumer
commits and durable evidence documents.
The checked-in alpha8 local exact-image result is explicitly marked
`not-certified` and `shared-client-contract-only`. It proves the Ruby/SSE probe
on the recorded image IDs but does not claim Rails, plugin, application, or
deployment canary coverage; exact-head CI still supplies reviewable workflow
provenance.
## Adding an upstream release ## Adding an upstream release
The scheduled watcher compares both the latest upstream GitHub release tag and The scheduled watcher compares the latest upstream GitHub release with the
its resolved OCI digest with the manifest. A new release or a changed digest manifest. When a new release appears, it resolves the tag to an OCI digest and
behind an existing tag gets a digest-specific branch and compatibility PR. The opens a compatibility PR. The PR adds a pending image-matrix entry, which runs
PR adds a pending image-matrix entry, which runs the candidate suite after the the candidate suite. A person still decides whether to promote it and update
normal GitHub workflow approval boundary. A person still decides whether to consumers.
promote it and update consumers. The watcher never merges, dispatches consumer
workflows, deploys, or force-pushes.
`public_ci` is the active compatibility set, not an append-only release log.
Keep images still referenced by a consumer's current, candidate, or previous
runtime tuple plus the newest pending upstream candidate. When a watcher PR
supersedes an unreferenced pending target, remove that older row during review;
durable certification documents and Git history are the archive. This keeps a
fast-moving upstream from making every future run retest every old release.
See [docs/certification.md](docs/certification.md) for promotion, canary, and See [docs/certification.md](docs/certification.md) for promotion, canary, and
rollback evidence requirements, and [docs/maintenance.md](docs/maintenance.md) rollback evidence requirements.
for consumer ownership, release order, runner boundaries, and expected failure
modes.
## License ## License

View File

@@ -6,44 +6,41 @@ 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;
2. exact `opencode-ruby` and `opencode-rails` candidate checkouts, all Rails 2. the public exact-image matrix against the deterministic model stub;
candidate tests on Ruby 3.2 through 4.0, an exact Rails-to-Ruby runtime 3. an isolated custom-image canary for Ajent Rails and Mushu;
dependency, and loaded Bundler provenance for the Ruby commit; 4. the consumer's own focused and full required tests in its devcontainer;
3. the public exact-image matrix against the deterministic model stub; 5. a user-visible canary turn for stream consumers;
4. an isolated custom-image canary for Ajent Rails and Mushu; 6. captured evidence including image ID/digest, server version, source commit,
5. the consumer's own focused and full required tests in its devcontainer;
6. a user-visible canary turn for stream consumers;
7. 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.
### Client source provenance
Before publication, `manifests/client-candidate.json` must use
`publication_state: unpublished` and `kind: commit` for both clients. Each
provenance commit must equal its full 40-character checkout ref, and the Rails
Gemfile/runtime dependency must resolve the same exact Ruby commit and version.
Commit-only evidence is candidate evidence; it is not proof of a gem release.
The lockstep checkout fetches tags and fails unpublished mode if the matching
`vVERSION` tag appears, forcing the manifest through the published gate.
After publication, change the state to `published` and replace both commit
provenance objects with `kind: annotated-tag`, the exact tag, annotated tag
object ID, and peeled commit. The repository validator rejects a published
commit-only candidate. The lockstep job additionally verifies that each local
tag ref resolves to the declared annotated tag object and that both the object
and ref peel to the tested checkout. This transition is a reviewed manifest
change; CI does not create or publish tags.
The image matrix's active candidate coordinates must equal the client manifest.
Changing either commit resets the active matrix to `pending`. A
`previous_certification` preserves historical evidence but does not certify the
new candidate.
The shared live probe is intentionally strict: `full_text` must equal the The shared live probe is intentionally strict: `full_text` must equal the
expected text byte-for-byte and the deterministic model must receive exactly expected text byte-for-byte and the deterministic model must receive exactly
one request. A response that merely contains the expected text, including a one request. A response that merely contains the expected text, including a
@@ -52,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
@@ -85,13 +91,6 @@ must explicitly contain values matching the promotion:
} }
``` ```
The promotion command stores each accepted document as a hash-bound reference,
`{"path":"evidence/...json","sha256":"sha256:..."}`. Later transitions
re-read the bytes, verify that digest, and cross-check any detailed consumer
tree, client, and runtime coordinates against the tuple. Editing an evidence
document therefore invalidates the certification until it is reviewed and
promoted again; a copied `tuple_sha256` alone is not sufficient.
Preview the exact manifest transition with `--dry-run`, then repeat without it Preview the exact manifest transition with `--dry-run`, then repeat without it
to write the manifest: to write the manifest:
@@ -122,27 +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
The companion lockstep job proves that the exact Rails candidate loads and need executable evidence from their owning consumer before a full tuple can
tests against the exact Ruby candidate on every supported Ruby version. That pass. A prose review or a successful shared Ruby probe is not that evidence.
still does not certify a consumer's ActiveRecord schema, persistence callbacks,
container adapter, or application canary; those remain consumer-owned profile
evidence.
GitHub CI writes canonical JSON artifacts for the shared fixture, lockstep
client, and each exact-image target. Artifact retention is 30 days and supplies
reviewable workflow provenance; it is not the long-term ledger. Gitea executes
the same contracts, with its exact-image targets run sequentially from the full
generated manifest, but its installed artifact service cannot accept the
reviewed GitHub upload action. Gitea therefore keeps generated JSON transient
and makes no artifact-evidence claim. After reviewing a GitHub artifact, copy
the relevant facts into a repository evidence document bound to the complete
tuple fingerprint. Automated workflows never update certified evidence or
promote a tuple.
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
@@ -155,40 +140,40 @@ 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
non-certified (`bootstrap-current-only` or `candidate`) and the candidate PR
must not be treated as deploy authorization.
Use the explicit degraded-bootstrap transition for the first passing rollback ```text
tuple. It requires passing evidence bound to the complete candidate fingerprint accept-degraded-rollback-with-failed-emergency-provenance
and a literal acknowledgement; it preserves the failed baseline as uncertified ```
`emergency_provenance`, leaves `previous` null, and records the temporary
`bootstrap-current-only` state: 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 ```sh
ruby scripts/promote_runtime_tuple.rb bootstrap-current \ ruby scripts/promote_runtime_tuple.rb bootstrap-current \
--consumer travelwolf \ --consumer travelwolf \
--consumer-commit FULL_40_CHARACTER_ROLLBACK_COMMIT \ --consumer-commit FULL_MAIN_OR_ATTESTED_PR_COMMIT \
--status pass \ --status pass \
--certified-at 2026-07-20T12:00:00Z \ --certified-at 2026-07-18T12:00:00Z \
--evidence evidence/travelwolf-rollback.json \ --evidence evidence/travelwolf-post-merge.json \
--acknowledge-degraded-rollback \ --acknowledge-degraded-rollback \
accept-degraded-rollback-with-failed-emergency-provenance \ accept-degraded-rollback-with-failed-emergency-provenance \
--dry-run --dry-run
``` ```
Review the dry run, repeat without `--dry-run`, and then treat that passing
tuple as the base for a normal promotion. Normal promotion moves it into
`previous`, removes the degraded rollback marker, and keeps the original failed
baseline only as historical emergency provenance. The repository becomes
`certified` only when every consumer has exact passing `current` and `previous`
tuples. Never use bootstrap to replace an existing previous tuple or to certify
a baseline already known to fail.
## Rollback ## Rollback
Rollback restores the whole `previous` tuple. Do not roll back only the gem or Rollback restores the whole `previous` tuple. Do not roll back only the gem or
@@ -202,17 +187,11 @@ the source commit used to build it. A source tag alone is insufficient.
Run the shared live contract on a host that can pull the exact image: Run the shared live contract on a host that can pull the exact image:
```sh ```sh
BUNDLE_GEMFILE=/path/to/opencode-ruby/Gemfile \
OPENCODE_RUBY_PATH=/path/to/opencode-ruby \ OPENCODE_RUBY_PATH=/path/to/opencode-ruby \
OPENCODE_RUBY_COMMIT=FULL_40_HEX_COMMIT \
OPENCODE_IMAGE='registry.example/image@sha256:...' \ OPENCODE_IMAGE='registry.example/image@sha256:...' \
bundle exec scripts/run_image_contract.sh scripts/run_image_contract.sh
``` ```
The Ruby checkout must be clean and its `HEAD` must equal
`OPENCODE_RUBY_COMMIT`. Install Bundler dependencies outside that checkout when
the normal cache path would create untracked files.
If an older private registry cannot expose an OCI repository digest, set If an older private registry cannot expose an OCI repository digest, set
`ALLOW_EXACT_IMAGE_ID=1` and pass the locally present `sha256:...` image ID. `ALLOW_EXACT_IMAGE_ID=1` and pass the locally present `sha256:...` image ID.
Record the registry tag, image ID, source commit, and reason a repository Record the registry tag, image ID, source commit, and reason a repository

View File

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

View File

@@ -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"
]
} }
} }

View File

@@ -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"
]
} }
} }

View File

@@ -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."
} }

View File

@@ -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"
} }

View File

@@ -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"
]
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,103 +0,0 @@
{
"schema_version": 1,
"kind": "pre-release-client-image-review-input",
"status": "pass",
"certification_status": "not-certified",
"publication_state": "unpublished",
"coverage_scope": "shared-client-contract-only",
"clients": {
"opencode_ruby": {
"version": "0.0.1.alpha8",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"executed": true
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"executed_by_image_contract": false
}
},
"checks": [
{
"id": "upstream-1.16.1",
"checked_at": "2026-07-20T05:38:34Z",
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"docker_image_id": "sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"platform": "linux/amd64",
"reported_version": "1.16.1",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn"]
},
{
"id": "upstream-1.17.18",
"checked_at": "2026-07-20T05:39:06Z",
"image": "ghcr.io/anomalyco/opencode@sha256:bf9d0e84b7cedef436a8f57db4d48767cd5d8fc6604f666335c1cc916b199a97",
"docker_image_id": "sha256:bf9d0e84b7cedef436a8f57db4d48767cd5d8fc6604f666335c1cc916b199a97",
"platform": "linux/amd64",
"reported_version": "1.17.18",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["plugin-ledger"]
},
{
"id": "upstream-1.18.3",
"checked_at": "2026-07-20T05:39:18Z",
"image": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
"docker_image_id": "sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e",
"platform": "linux/amd64",
"reported_version": "1.18.3",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"]
},
{
"id": "ajent-blackline-alpha8-shared-contract",
"checked_at": "2026-07-20T05:39:57Z",
"image": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
"docker_image_id": "sha256:239e3dc6cd2958251cda636fb20bd7cd7c46f596bd4c61cb81863ebc49d824e8",
"platform": "linux/amd64",
"reported_version": "1.18.3",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"]
},
{
"id": "mushu-permission-shortcuts-alpha8-shared-contract",
"checked_at": "2026-07-20T05:40:08Z",
"image": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"platform": "linux/amd64",
"reported_version": "0.0.0-permission-shortcuts-202606300721",
"status": "pass",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"authoritative_assistant_message_count": 1,
"llm_request_count": 1,
"executed_profiles": ["ruby-rest-sse"],
"required_consumer_profiles": []
}
],
"limitations": [
"This local evidence has no GitHub workflow run provenance and is review input only.",
"The exact-image contract executed opencode-ruby but did not execute opencode-rails.",
"Rails persistence, plugin-ledger, consumer application, deployment, and user-visible canary profiles remain unverified.",
"No runtime tuple was promoted and no consumer was deployed."
]
}

View File

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

View File

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

View File

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

View File

@@ -1,154 +0,0 @@
# frozen_string_literal: true
require "json"
require "rubygems"
module OpenCodeCompat
class ClientCandidate
class ValidationError < StandardError; end
SCHEMA_VERSION = 3
CLIENT_NAMES = %w[opencode-rails opencode-ruby].freeze
PUBLICATION_PROVENANCE = {
"published" => "annotated-tag",
"unpublished" => "commit"
}.freeze
SHA_PATTERN = /\A[0-9a-f]{40}\z/
def self.load(path)
new(JSON.parse(File.binread(path)))
rescue JSON::ParserError => error
raise ValidationError, "client candidate is not valid JSON: #{error.message}"
end
attr_reader :document
def initialize(document)
@document = document
end
def verify!
assert_exact_keys!(document,
%w[clients publication_state release_train schema_version status], "client candidate")
assert_equal!(SCHEMA_VERSION, document.fetch("schema_version"), "schema_version")
assert_equal!("candidate", document.fetch("status"), "status")
release_train = exact_version!(document.fetch("release_train"), "release_train")
publication_state = document.fetch("publication_state")
expected_kind = PUBLICATION_PROVENANCE.fetch(publication_state) do
raise ValidationError,
"publication_state must be one of #{PUBLICATION_PROVENANCE.keys.join(', ')}, got #{publication_state.inspect}"
end
clients = document.fetch("clients")
assert_equal!(CLIENT_NAMES, clients.keys.sort, "client names")
clients.each do |name, client|
validate_client!(name, client, release_train, expected_kind)
end
validate_runtime_dependency!(clients, release_train)
self
rescue KeyError => error
raise ValidationError, "client candidate is missing required field #{error.key.inspect}"
end
def github_outputs
verify!
clients = document.fetch("clients")
{
"publication_state" => document.fetch("publication_state"),
"ruby_provenance_kind" => provenance(clients, "opencode-ruby").fetch("kind"),
"ruby_ref" => clients.dig("opencode-ruby", "ref"),
"ruby_tag" => provenance(clients, "opencode-ruby")["tag"].to_s,
"ruby_tag_object" => provenance(clients, "opencode-ruby")["annotated_tag_object"].to_s,
"ruby_version" => clients.dig("opencode-ruby", "version"),
"rails_provenance_kind" => provenance(clients, "opencode-rails").fetch("kind"),
"rails_ref" => clients.dig("opencode-rails", "ref"),
"rails_tag" => provenance(clients, "opencode-rails")["tag"].to_s,
"rails_tag_object" => provenance(clients, "opencode-rails")["annotated_tag_object"].to_s,
"rails_version" => clients.dig("opencode-rails", "version")
}
end
private
def validate_client!(name, client, release_train, expected_kind)
expected_keys = %w[provenance ref repository version]
expected_keys << "runtime_dependencies" if name == "opencode-rails"
assert_exact_keys!(client, expected_keys.sort, "#{name} candidate")
assert_equal!("ajaynomics/#{name}", client.fetch("repository"), "#{name} repository")
assert_equal!(release_train, exact_version!(client.fetch("version"), "#{name} version"), "#{name} version")
ref = exact_commit!(client.fetch("ref"), "#{name} ref")
source = client.fetch("provenance")
assert_equal!(expected_kind, source.fetch("kind"), "#{name} provenance kind")
case expected_kind
when "commit"
assert_exact_keys!(source, %w[commit kind], "#{name} commit provenance")
assert_equal!(ref, exact_commit!(source.fetch("commit"), "#{name} provenance commit"),
"#{name} provenance commit")
when "annotated-tag"
assert_exact_keys!(source, %w[annotated_tag_object kind peeled_commit tag],
"#{name} annotated-tag provenance")
assert_equal!("v#{release_train}", exact_tag!(source.fetch("tag"), "#{name} tag"), "#{name} tag")
exact_commit!(source.fetch("annotated_tag_object"), "#{name} annotated tag object")
assert_equal!(ref, exact_commit!(source.fetch("peeled_commit"), "#{name} peeled commit"),
"#{name} peeled commit")
end
end
def validate_runtime_dependency!(clients, release_train)
dependency = clients.fetch("opencode-rails")
.fetch("runtime_dependencies")
.fetch("opencode-ruby")
assert_exact_keys!(dependency, %w[ref requirement], "opencode-rails runtime dependency")
assert_equal!("= #{release_train}", dependency.fetch("requirement"),
"opencode-rails runtime dependency requirement")
assert_equal!(clients.dig("opencode-ruby", "ref"),
exact_commit!(dependency.fetch("ref"), "opencode-rails runtime dependency ref"),
"opencode-rails runtime dependency ref")
end
def provenance(clients, name)
clients.fetch(name).fetch("provenance")
end
def exact_commit!(value, label)
return value if value.is_a?(String) && value.match?(SHA_PATTERN)
raise ValidationError, "#{label} must be a full lowercase 40-character Git commit, got #{value.inspect}"
end
def exact_tag!(value, label)
if value.is_a?(String) && value.match?(/\A[A-Za-z0-9][A-Za-z0-9._-]*\z/) &&
!value.include?("..") && !value.end_with?(".")
return value
end
raise ValidationError, "#{label} must be a safe exact Git tag, got #{value.inspect}"
end
def exact_version!(value, label)
if value.is_a?(String) && value.match?(/\A[0-9A-Za-z][0-9A-Za-z.-]*\z/) && Gem::Version.correct?(value)
return value
end
raise ValidationError, "#{label} must be an exact RubyGems version, got #{value.inspect}"
end
def assert_exact_keys!(value, expected, label)
actual = value.keys.sort
return if actual == expected
raise ValidationError, "#{label} keys must equal #{expected.inspect}, got #{actual.inspect}"
end
def assert_equal!(expected, actual, label)
return if expected == actual
raise ValidationError, "#{label} must equal #{expected.inspect}, got #{actual.inspect}"
end
end
end

View File

@@ -10,6 +10,7 @@ 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/
@@ -32,26 +33,6 @@ module OpenCodeCompat
source_image source_image
upstream_base upstream_base
].freeze ].freeze
EVIDENCE_REFERENCE_KEYS = %w[path sha256].freeze
NULLABLE_COMMIT_KEYS = %w[build_source_commit].freeze
EVIDENCE_RUNTIME_KEY_MAP = {
"custom_opencode_source_commit" => "source_commit"
}.freeze
EVIDENCE_RUNTIME_COORDINATE_KEYS = %w[
application_image
application_image_id
base_image
build_source_commit
docker_image_id
image
provenance_strength
registry_ref
reported_version
source_commit
source_image
tag_provenance
upstream_base
].freeze
def initialize(root:, manifest_path: File.join(root, "manifests/runtime-tuples.json")) def initialize(root:, manifest_path: File.join(root, "manifests/runtime-tuples.json"))
@root = File.realpath(root) @root = File.realpath(root)
@@ -63,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
@@ -146,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
@@ -178,14 +159,14 @@ module OpenCodeCompat
consumer_entry["current"] = certified_candidate consumer_entry["current"] = certified_candidate
consumer_entry["candidate"] = nil consumer_entry["candidate"] = nil
consumer_entry.delete("rollback_state") consumer_entry.delete("rollback_state")
refresh_promotion_state!(manifest) manifest["migration_state"] = migration_state_for(manifest)
manifest manifest
end end
def bootstrap_current_manifest(manifest, consumer:, consumer_commit:, certification:, acknowledgement:) def bootstrap_current_manifest(manifest, consumer:, consumer_commit:, certification:, acknowledgement:)
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 retain as emergency provenance" raise PromotionError, "#{consumer} has no current tuple to retain as emergency provenance"
end end
@@ -193,12 +174,6 @@ module OpenCodeCompat
unless NON_CERTIFIABLE_TUPLE_STATUSES.include?(current["status"]) unless NON_CERTIFIABLE_TUPLE_STATUSES.include?(current["status"])
raise PromotionError, "degraded bootstrap is only for a current tuple known to fail the contract" raise PromotionError, "degraded bootstrap is only for a current tuple known to fail the contract"
end end
unless consumer_entry["previous"].nil?
raise PromotionError, "degraded bootstrap cannot replace an existing previous tuple"
end
if consumer_entry.key?("emergency_provenance") || consumer_entry.key?("rollback_state")
raise PromotionError, "degraded bootstrap has already been recorded for this consumer"
end
candidate_fingerprint = tuple_fingerprint(candidate, consumer: consumer, profile: profile) candidate_fingerprint = tuple_fingerprint(candidate, consumer: consumer, profile: profile)
current_fingerprint = tuple_fingerprint(current, consumer: consumer, profile: profile) current_fingerprint = tuple_fingerprint(current, consumer: consumer, profile: profile)
@@ -225,13 +200,18 @@ module OpenCodeCompat
"recorded_at" => certification.fetch("certified_at"), "recorded_at" => certification.fetch("certified_at"),
"emergency_provenance_status" => current.fetch("status") "emergency_provenance_status" => current.fetch("status")
} }
refresh_promotion_state!(manifest) 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
@@ -239,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"
@@ -256,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/)
@@ -337,23 +459,21 @@ module OpenCodeCompat
end end
validate_timestamp!(certified_at, "#{label} certification timestamp") validate_timestamp!(certified_at, "#{label} certification timestamp")
raise PromotionError, "#{label} certification requires at least one evidence file" if evidence.empty? raise PromotionError, "#{label} certification requires at least one evidence file" if evidence.empty?
raise PromotionError, "#{label} certification evidence files must be unique" unless evidence.uniq == evidence
consumer_commit = tuple.fetch("consumer_commit")
normalized_evidence = evidence.map do |reference| normalized_evidence = evidence.map do |reference|
validate_evidence!( validate_evidence!(
reference, reference,
consumer: consumer, consumer: consumer,
profile: profile, profile: profile,
tuple: tuple, consumer_commit: consumer_commit,
status: status, status: status,
certified_at: certified_at, certified_at: certified_at,
tuple_fingerprint: expected_fingerprint, tuple_fingerprint: expected_fingerprint,
label: label label: label
) )
end end
evidence_paths = normalized_evidence.map { |reference| reference.fetch("path") }
unless evidence_paths.uniq == evidence_paths
raise PromotionError, "#{label} certification evidence files must be unique"
end
{ {
"status" => status, "status" => status,
@@ -369,13 +489,7 @@ module OpenCodeCompat
raise PromotionError, "#{label} tuple has invalid or stale certification metadata" raise PromotionError, "#{label} tuple has invalid or stale certification metadata"
end end
references = Array(certification["evidence"]) validate_supplied_certification!(
unless references.all? { |reference| structured_evidence_reference?(reference) }
raise PromotionError,
"#{label} recorded certification evidence must use hash-bound {path, sha256} references"
end
validated = validate_supplied_certification!(
certification, certification,
consumer: consumer, consumer: consumer,
profile: profile, profile: profile,
@@ -383,24 +497,23 @@ module OpenCodeCompat
expected_fingerprint: expected_fingerprint, expected_fingerprint: expected_fingerprint,
label: label label: label
) )
unless certification["evidence"] == validated["evidence"]
raise PromotionError, "#{label} recorded certification evidence references are not canonical"
end
end end
def validate_evidence!( def validate_evidence!(
reference, reference,
consumer:, consumer:,
profile:, profile:,
tuple:, consumer_commit:,
status:, status:,
certified_at:, certified_at:,
tuple_fingerprint:, tuple_fingerprint:,
label: label:
) )
reference_path, expected_sha256 = evidence_reference_parts!(reference, label) unless reference.is_a?(String) && !reference.empty?
raise PromotionError, "#{label} evidence references must be non-empty strings"
end
relative = Pathname.new(reference_path).cleanpath relative = Pathname.new(reference).cleanpath
if relative.absolute? || relative.each_filename.first != "evidence" if relative.absolute? || relative.each_filename.first != "evidence"
raise PromotionError, "#{label} evidence must be a repository-relative path under evidence/" raise PromotionError, "#{label} evidence must be a repository-relative path under evidence/"
end end
@@ -412,23 +525,14 @@ module OpenCodeCompat
raise PromotionError, "#{label} evidence resolves outside evidence/" raise PromotionError, "#{label} evidence resolves outside evidence/"
end end
contents = File.binread(real_path) document = parse_json(File.read(real_path), relative.to_s)
actual_sha256 = "sha256:#{Digest::SHA256.hexdigest(contents)}"
if expected_sha256 && expected_sha256 != actual_sha256
raise PromotionError,
"#{label} evidence #{relative} has sha256=#{actual_sha256.inspect}; " \
"expected #{expected_sha256.inspect}"
end
document = parse_json(contents, relative.to_s)
unless document.is_a?(Hash) && document["schema_version"] == 1 unless document.is_a?(Hash) && document["schema_version"] == 1
raise PromotionError, "#{label} evidence #{relative} must use schema_version 1" raise PromotionError, "#{label} evidence #{relative} must use schema_version 1"
end end
validate_immutable_fields!(document, "#{label} evidence #{relative}")
expected = { expected = {
"consumer" => consumer, "consumer" => consumer,
"profile" => profile, "profile" => profile,
"consumer_commit" => tuple.fetch("consumer_commit"), "consumer_commit" => consumer_commit,
"status" => status, "status" => status,
"certified_at" => certified_at, "certified_at" => certified_at,
"tuple_sha256" => tuple_fingerprint "tuple_sha256" => tuple_fingerprint
@@ -440,20 +544,14 @@ module OpenCodeCompat
"#{label} evidence #{relative} has #{key}=#{document[key].inspect}; expected #{value.inspect}" "#{label} evidence #{relative} has #{key}=#{document[key].inspect}; expected #{value.inspect}"
end end
validate_evidence_tuple_details!(document, tuple, "#{label} evidence #{relative}") relative.to_s
{
"path" => relative.to_s,
"sha256" => actual_sha256
}
rescue Errno::ENOENT rescue Errno::ENOENT
raise PromotionError, "#{label} evidence does not exist: #{reference_path || reference}" raise PromotionError, "#{label} evidence does not exist: #{reference}"
end end
def validate_tuple!(tuple, label) def validate_tuple!(tuple, label)
raise PromotionError, "#{label} tuple must be an object" unless tuple.is_a?(Hash) raise PromotionError, "#{label} tuple must be an object" unless tuple.is_a?(Hash)
validate_immutable_fields!(tuple, label)
validate_full_commit!(tuple["consumer_commit"], "#{label} consumer commit") validate_full_commit!(tuple["consumer_commit"], "#{label} consumer commit")
validate_client!(tuple["opencode_ruby"], "#{label} opencode_ruby", required: true) validate_client!(tuple["opencode_ruby"], "#{label} opencode_ruby", required: true)
validate_client!(tuple["opencode_rails"], "#{label} opencode_rails", required: false) validate_client!(tuple["opencode_rails"], "#{label} opencode_rails", required: false)
@@ -471,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)
@@ -480,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)
@@ -491,10 +670,8 @@ module OpenCodeCompat
raise PromotionError, "#{label} #{key} must be an immutable image@sha256 digest, not a tag" raise PromotionError, "#{label} #{key} must be an immutable image@sha256 digest, not a tag"
end end
exact_execution_coordinate = true if %w[image registry_ref].include?(key) exact_execution_coordinate = true if %w[image registry_ref].include?(key)
elsif key.end_with?("_commit") elsif key == "source_commit"
next if value.nil? && NULLABLE_COMMIT_KEYS.include?(key) validate_full_commit!(value, "#{label} source commit")
validate_full_commit!(value, "#{label} #{key.tr('_', ' ')}")
end end
end end
@@ -509,154 +686,6 @@ module OpenCodeCompat
(key.end_with?("_ref") && key != "tag_provenance") (key.end_with?("_ref") && key != "tag_provenance")
end end
def structured_evidence_reference?(reference)
reference.is_a?(Hash) && reference.keys.sort == EVIDENCE_REFERENCE_KEYS
end
def evidence_reference_parts!(reference, label)
if reference.is_a?(String)
unless !reference.empty?
raise PromotionError, "#{label} evidence references must contain a non-empty path"
end
return [reference, nil]
end
unless structured_evidence_reference?(reference)
raise PromotionError,
"#{label} evidence references must be a path string or an object containing only path and sha256"
end
path = reference["path"]
sha256 = reference["sha256"]
unless path.is_a?(String) && !path.empty?
raise PromotionError, "#{label} evidence references must contain a non-empty path"
end
unless sha256.is_a?(String) && sha256.match?(DIGEST)
raise PromotionError, "#{label} evidence reference sha256 must be an exact sha256 digest"
end
[path, sha256]
end
def validate_evidence_tuple_details!(document, tuple, label)
if document.key?("consumer_tree")
expected_tree = tuple["consumer_tree"]
unless expected_tree && document["consumer_tree"] == expected_tree
raise PromotionError,
"#{label} has consumer_tree=#{document['consumer_tree'].inspect}; " \
"expected #{expected_tree.inspect}"
end
end
validate_evidence_clients!(document["clients"], tuple, label) if document.key?("clients")
validate_evidence_runtime!(document["runtime"], tuple.fetch("runtime"), label) if document.key?("runtime")
end
def validate_evidence_clients!(clients, tuple, label)
unless clients.is_a?(Hash)
raise PromotionError, "#{label} clients must be an object"
end
%w[opencode_ruby opencode_rails].each do |client_name|
next unless clients.key?(client_name)
evidence_client = clients[client_name]
tuple_client = tuple[client_name]
if evidence_client.nil?
unless tuple_client.nil?
raise PromotionError, "#{label} #{client_name} is null but the tuple declares a client"
end
next
end
unless evidence_client.is_a?(Hash) && tuple_client.is_a?(Hash)
raise PromotionError, "#{label} #{client_name} must match the tuple client"
end
unless evidence_client["version"].is_a?(String) && !evidence_client["version"].empty?
raise PromotionError, "#{label} #{client_name} must include a non-empty version"
end
commits = %w[commit git_commit].filter_map do |key|
evidence_client[key] if evidence_client.key?(key)
end
if commits.empty?
raise PromotionError, "#{label} #{client_name} must include commit or git_commit"
end
compare_evidence_coordinate!(
evidence_client["version"],
tuple_client["version"],
"#{label} #{client_name}.version"
)
commits.each do |commit|
compare_evidence_coordinate!(commit, tuple_client["git_commit"], "#{label} #{client_name}.commit")
end
end
end
def validate_evidence_runtime!(runtime, tuple_runtime, label)
unless runtime.is_a?(Hash)
raise PromotionError, "#{label} runtime must be an object"
end
runtime.each do |key, value|
tuple_key = EVIDENCE_RUNTIME_KEY_MAP.fetch(key, key)
next unless EVIDENCE_RUNTIME_COORDINATE_KEYS.include?(tuple_key)
unless tuple_runtime.key?(tuple_key)
raise PromotionError, "#{label} runtime.#{key} is not present in the certified tuple"
end
compare_evidence_coordinate!(value, tuple_runtime[tuple_key], "#{label} runtime.#{key}")
end
end
def compare_evidence_coordinate!(actual, expected, label)
return if actual == expected
raise PromotionError, "#{label}=#{actual.inspect}; expected #{expected.inspect}"
end
def validate_immutable_fields!(value, label, path = [])
case value
when Hash
value.each do |key, child|
field_path = path + [key]
field_label = "#{label} #{field_path.join('.')}"
if key == "commit" || key.end_with?("_commit") || key == "head_sha" || key.end_with?("_head_sha")
unless child.nil? && NULLABLE_COMMIT_KEYS.include?(key)
validate_full_commit!(child, field_label)
end
elsif key == "tree" || key.end_with?("_tree")
validate_full_oid!(child, field_label)
elsif key == "annotated_tag_object" || key.end_with?("_tag_object")
validate_full_oid!(child, field_label)
elsif digest_field?(key)
unless child.is_a?(String) && child.match?(DIGEST)
raise PromotionError, "#{field_label} must be an exact sha256 digest"
end
end
validate_immutable_fields!(child, label, field_path)
end
when Array
value.each_with_index { |child, index| validate_immutable_fields!(child, label, path + [index]) }
end
end
def digest_field?(key)
key == "sha256" || key.end_with?("_sha256") ||
key == "digest" || key.end_with?("_digest") ||
key == "docker_image_id" || key.end_with?("_image_id")
end
def validate_full_oid!(value, label)
return if value.is_a?(String) && value.match?(FULL_COMMIT)
raise PromotionError, "#{label} must be a full 40-character lowercase Git object ID"
end
def validate_full_commit!(value, label) def validate_full_commit!(value, label)
return if value.is_a?(String) && value.match?(FULL_COMMIT) return if value.is_a?(String) && value.match?(FULL_COMMIT)
@@ -730,59 +759,13 @@ module OpenCodeCompat
end end
end end
def all_consumers_bootstrapped?(manifest)
manifest.fetch("consumers").all? do |consumer, entry|
next false unless entry["candidate"].nil? && entry["previous"].nil?
next false unless entry.dig("rollback_state", "status") == "degraded-no-certified-previous"
next false unless NON_CERTIFIABLE_TUPLE_STATUSES.include?(entry.dig("emergency_provenance", "status"))
profile = fetch_profile!(entry, consumer)
tuple = entry["current"]
next false unless tuple.is_a?(Hash) && tuple["status"] == "certified"
validate_tuple!(tuple, "#{consumer} current")
validate_recorded_certification!(
tuple,
consumer: consumer,
profile: profile,
expected_fingerprint: tuple_fingerprint(tuple, consumer: consumer, profile: profile),
label: "#{consumer} current"
)
true
rescue PromotionError
false
end
end
def migration_state_for(manifest) def migration_state_for(manifest)
return "certified" if all_consumers_certified?(manifest) return "certified" if all_consumers_certified?(manifest)
return "bootstrap-current-only" if all_consumers_bootstrapped?(manifest)
"candidate" degraded = manifest.fetch("consumers").any? do |_consumer, entry|
end entry.dig("rollback_state", "status") == "degraded-no-certified-previous"
def refresh_promotion_state!(manifest)
manifest["migration_state"] = migration_state_for(manifest)
manifest["promotion_readiness"] = case manifest.fetch("migration_state")
when "certified"
{
"status" => "certified",
"reason" => "Every consumer has exact current and previous passing tuples.",
"required_action" => "Promote only a newly certified, materially changed tuple."
}
when "bootstrap-current-only"
{
"status" => "bootstrap-current-only",
"reason" => "Every current tuple is certified, but no independently passing previous tuple exists yet.",
"required_action" => "Certify the next meaningful release so normal promotion retains the current tuple as previous."
}
else
{
"status" => "candidate",
"reason" => "At least one consumer transition remains incomplete.",
"required_action" => "Finish exact tuple certification without treating failed emergency provenance as rollback evidence."
}
end end
degraded ? "bootstrap-current-only" : "candidate"
end end
def with_current_manifest_lock def with_current_manifest_lock

View File

@@ -1,32 +1,11 @@
{ {
"schema_version": 3,
"release_train": "0.0.1.alpha8",
"publication_state": "unpublished",
"clients": {
"opencode-ruby": {
"repository": "ajaynomics/opencode-ruby", "repository": "ajaynomics/opencode-ruby",
"ref": "9277646a4bb2cf25a8384ffc140b154f49ea5766", "ref": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"version": "0.0.1.alpha8", "version": "0.0.1.alpha7",
"provenance": { "tag_provenance": {
"kind": "commit", "tag": "v0.0.1.alpha7",
"commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766" "annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645",
} "peeled_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798"
},
"opencode-rails": {
"repository": "ajaynomics/opencode-rails",
"ref": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"version": "0.0.1.alpha8",
"provenance": {
"kind": "commit",
"commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f"
},
"runtime_dependencies": {
"opencode-ruby": {
"requirement": "= 0.0.1.alpha8",
"ref": "9277646a4bb2cf25a8384ffc140b154f49ea5766"
}
}
}
}, },
"status": "candidate" "status": "candidate"
} }

View File

@@ -1,14 +1,5 @@
{ {
"schema_version": 2, "schema_version": 1,
"client_candidate": {
"release_train": "0.0.1.alpha8",
"publication_state": "unpublished",
"opencode_ruby_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"opencode_rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"certification_status": "certified",
"certified_at": "2026-07-20T07:36:06Z",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"public_ci": [ "public_ci": [
{ {
"id": "upstream-1.16.1", "id": "upstream-1.16.1",
@@ -18,28 +9,12 @@
"consumers": ["travelwolf"], "consumers": ["travelwolf"],
"profiles": ["ruby-rest-sse"], "profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn"], "required_consumer_profiles": ["rails-persisted-turn"],
"certification_scope": "shared-client-contract-only",
"certification_status": "certified", "certification_status": "certified",
"current_certification": { "certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"status": "certified", "certified_at": "2026-07-18T23:35:57Z",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:50Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"certified_at": "2026-07-18T22:03:12Z",
"expected_text": "compat-ok", "expected_text": "compat-ok",
"full_text": "compat-ok", "full_text": "compat-ok",
"llm_request_count": 1 "llm_request_count": 1
}
}, },
{ {
"id": "upstream-1.17.18", "id": "upstream-1.17.18",
@@ -49,28 +24,12 @@
"consumers": ["opencode-ajent"], "consumers": ["opencode-ajent"],
"profiles": ["ruby-rest-sse"], "profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["plugin-ledger"], "required_consumer_profiles": ["plugin-ledger"],
"certification_scope": "shared-client-contract-only",
"certification_status": "certified", "certification_status": "certified",
"current_certification": { "certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"status": "certified", "certified_at": "2026-07-18T23:35:58Z",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:55Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"certified_at": "2026-07-18T22:03:13Z",
"expected_text": "compat-ok", "expected_text": "compat-ok",
"full_text": "compat-ok", "full_text": "compat-ok",
"llm_request_count": 1 "llm_request_count": 1
}
}, },
{ {
"id": "upstream-1.18.3", "id": "upstream-1.18.3",
@@ -80,114 +39,55 @@
"consumers": ["ajent-rails"], "consumers": ["ajent-rails"],
"profiles": ["ruby-rest-sse"], "profiles": ["ruby-rest-sse"],
"required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"], "required_consumer_profiles": ["rails-persisted-turn", "plugin-ledger"],
"certification_scope": "shared-client-contract-only",
"certification_status": "certified", "certification_status": "certified",
"current_certification": { "certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"status": "certified", "certified_at": "2026-07-18T23:35:58Z",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"checked_at": "2026-07-20T07:34:48Z",
"expected_text": "compat-ok",
"full_text": "compat-ok",
"llm_request_count": 1,
"workflow_run_id": "29725021192",
"workflow_head_sha": "328fa39c557d598ad09b270ad1ceab19901031cd",
"evidence": "evidence/2026-07-20-opencode-alpha8-shared-client-ci.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"certified_at": "2026-07-18T22:03:14Z",
"expected_text": "compat-ok", "expected_text": "compat-ok",
"full_text": "compat-ok", "full_text": "compat-ok",
"llm_request_count": 1 "llm_request_count": 1
} }
}
], ],
"host_canary": [ "host_canary": [
{ {
"id": "ajent-rails-alpha8-production", "id": "ajent-blackline-alpha7-canary",
"consumer": "ajent-rails", "consumer": "ajent-rails",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264", "consumer_commit": "01cdf9da8e1b3f9a205d681e448aefefd9704db2",
"application_image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350", "consumer_ref_kind": "pull-request-head",
"product_images": { "certification_scope": "pre-merge-pr-head-candidate-only",
"aigl": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7", "promotion_eligible": false,
"blackline": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8", "tag_provenance": "ajent-opencode-blackline-canary:01cdf9da8e1b3f9a205d681e448aefefd9704db2",
"raven": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3" "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", "plugin-ledger"],
"certification_status": "certified",
"current_certification": {
"status": "certified",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"certified_at": "2026-07-20T08:15:17Z",
"evidence": "evidence/2026-07-20-ajent-rails-alpha8-production.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"consumer_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"certified_at": "2026-07-20T08:14:53Z",
"evidence": "evidence/2026-07-20-ajent-rails-alpha7-rollback.json"
}
},
{
"id": "travelwolf-alpha8-production",
"consumer": "travelwolf",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:031371e20f34e1014712ddc668b4e182483664bb70f116b683c0ec83d6668338",
"reported_version": "1.16.1",
"profiles": ["rails-persisted-turn"], "profiles": ["rails-persisted-turn"],
"certification_status": "certified", "certification_status": "candidate-certified",
"current_certification": { "certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"status": "certified", "certified_rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766", "certified_at": "2026-07-18T23:28:16.435369Z",
"rails_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f", "persisted_final_text": "compat-ok",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7", "authoritative_final_text": "compat-ok",
"certified_at": "2026-07-20T06:51:48Z", "model_request_count": 1,
"evidence": "evidence/2026-07-20-travelwolf-alpha8-production.json" "product_artifacts_missing_before_promotion": ["aigl", "raven"]
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"rails_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"certified_at": "2026-07-20T06:31:42Z",
"evidence": "evidence/2026-07-20-travelwolf-alpha7-rollback.json"
}
}, },
{ {
"id": "mushu-permission-shortcuts-alpha8-production", "id": "mushu-permission-shortcuts-alpha7-canary",
"consumer": "mushu", "consumer": "mushu",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047", "consumer_commit": "7cff22788b36ae703f6b1b8fa39b52d6dbafe7e0",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:a16bf0c4cd17698eba02244f6ba0e429b2238b6455e1415ff69fb08965c863a7", "consumer_ref_kind": "pull-request-head",
"application_image": "docker-registry:5000/mushu@sha256:30f204184520d504c7c384c7f6e503fa3547217aaaef2f1d0d65884533bdeacd", "certification_scope": "pre-merge-pr-head-candidate-only",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16", "promotion_eligible": false,
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9", "source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": "521185613a864a16a76a21c7a60597ab3973bae9",
"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": "certified", "certification_status": "candidate-certified",
"current_certification": { "certified_client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"status": "certified", "certified_at": "2026-07-18T23:26:06.865190Z",
"client_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766", "authoritative_final_text": "compat-ok",
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047", "provider_request_count": 1
"certified_at": "2026-07-20T07:13:51Z",
"evidence": "evidence/2026-07-20-mushu-alpha8-production.json"
},
"previous_certification": {
"status": "certified",
"client_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"certified_at": "2026-07-20T06:39:46.921805Z",
"evidence": "evidence/2026-07-20-mushu-alpha7-rollback.json"
}
} }
] ]
} }

View File

@@ -1,202 +1,19 @@
{ {
"schema_version": 1, "$schema": "../schemas/runtime-tuples.schema.json",
"migration_state": "certified", "schema_version": 2,
"migration_state": "candidate",
"promotion_readiness": { "promotion_readiness": {
"status": "certified", "status": "blocked",
"reason": "Every consumer has exact current and previous passing tuples.", "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.",
"required_action": "Promote only a newly certified, materially changed tuple." "evidence": "evidence/2026-07-18-opencode-ruby-alpha2-regression.json",
"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": "certified",
"consumer_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"consumer_tree": "a1e044f3a4f5c5ac0510a1d998c4afb55286da5a",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"git_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"application_image": "docker-registry:5000/ajent_rails@sha256:c7b2c6f3c3ffb6af62069c3142a2d76e606b0794077b81c5464add2266b7c350",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"build_source_commit": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"application_canary": {
"checked_at": "2026-07-20T08:15:17Z",
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"forge_ci": {
"pr_run_id": "2718",
"main_build_run_id": "2719",
"protected_main_run_id": "2720",
"deploy_job_id": "12065",
"status": "pass"
},
"production": {
"checked_at": "2026-07-20T08:15:17Z",
"application_health_status": 200,
"blackline_health_status": 200,
"ajent_public_status": 308,
"voice_contract_status": "pass",
"scotus_contract_status": "pass",
"status": "pass"
},
"source_availability": {
"origin_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"backup_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"github_main": "2ec8b879c5a34bb683fa5aaf26b998041d0e1264",
"status": "pass"
},
"compatibility_certified_at": "2026-07-20T08:15:17Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T08:15:17Z",
"tuple_sha256": "sha256:326a87cdb67061bdd36dfcee989ae149511a0c627e857488d59e0f2b3c796720",
"evidence": [
{
"path": "evidence/2026-07-20-ajent-rails-alpha8-production.json",
"sha256": "sha256:1db57a6df214d03a3466ce8668c773723e733f00d08d072dfa55206c3e8b09a1"
}
]
}
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"consumer_tree": "29e6cd35313a06cef6f9c1f40a36e3ee582fd035",
"opencode_ruby": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "abfce3ec8cb1ee468107d0845521769502251645"
}
},
"opencode_rails": {
"version": "0.0.1.alpha7",
"git_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"annotated_tag_object": "61e128084b2b665c0ba20b332987e41794948d7c"
}
},
"runtime": {
"image": "docker-registry:5000/ajent_rails@sha256:fe6fdc2325d304ddb276df7d35807c183e7e574876a0d76a6c6de6014619c76e",
"application_image": "docker-registry:5000/ajent_rails@sha256:fe6fdc2325d304ddb276df7d35807c183e7e574876a0d76a6c6de6014619c76e",
"aigl_image": "docker-registry:5000/opencode-aigl@sha256:f4064cb8b7a0f680f076dba9c72ba8e63b1457227c9fad5aa1407f0e0aac12e7",
"blackline_image": "docker-registry:5000/opencode-blackline@sha256:7b4ea4d70ff5fd35e3b17c571865dd6bb489217c7c00942e8569132fe2f749c8",
"raven_image": "docker-registry:5000/opencode-raven@sha256:9fe37ee88d8b10165e17f5f2a5c47b2396256464e732b4ef49138fb92a1234e3",
"build_source_commit": "6caa0132e4b172777126ed537de99cb7e9758e0b",
"reported_version": "1.18.3",
"upstream_base": "ghcr.io/anomalyco/opencode@sha256:c2d5d6398df72aac85cb1bdc8f900c71a9b75a33fb7c0a76dc1484e4b126e41e"
},
"application_canary": {
"checked_at": "2026-07-20T08:14:53Z",
"products": [
{
"id": "aigl",
"agent": "travel-agent",
"model": "openai-gpt-5.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "blackline",
"agent": "employment-lawyer",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
},
{
"id": "raven",
"agent": "raven-legal",
"model": "moonshot-kimi-k2.5",
"final_text": "CANARY_OK",
"reported_version": "1.18.3",
"status": "pass"
}
],
"session_cleanup_status": "pass",
"status": "pass"
},
"consumer_verification": {
"test_runs": 4247,
"assertions": 15740,
"failures": 0,
"errors": 0,
"skips": 4,
"rubocop_files": 1430,
"status": "pass"
},
"source_availability": {
"origin_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"backup_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720",
"github_ref": "refs/heads/opencode/opencode-alpha7-rollback-certified-20260720"
},
"rollback_operability": {
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": true,
"application_rebuild_required": false,
"deployed": false
},
"compatibility_certified_at": "2026-07-20T08:14:53Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T08:14:53Z",
"tuple_sha256": "sha256:ae48333ba453f442d14263a916418a384f576db34d09a503111fc28c340eb408",
"evidence": [
{
"path": "evidence/2026-07-20-ajent-rails-alpha7-rollback.json",
"sha256": "sha256:c3554a346a6585c7f98fde2aee2ad4f06ef4db7b621c98160183274e9e6ddc5c"
}
]
}
},
"emergency_provenance": {
"status": "observed-production-contract-failed", "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"
@@ -206,106 +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,
"travelwolf": {
"profile": "rails-persisted-turn",
"current": {
"status": "certified",
"consumer_commit": "02aed70eb4de201c6510e3be583699f8eb984ce7",
"consumer_tree": "f436a7248d90e5ae9e84c45f264acd913a4162aa",
"opencode_ruby": {
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"opencode_rails": {
"version": "0.0.1.alpha8",
"git_commit": "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:031371e20f34e1014712ddc668b4e182483664bb70f116b683c0ec83d6668338",
"reported_version": "1.16.1"
},
"application_canary": {
"checked_at": "2026-07-20T06:31:13.506Z",
"final_text": "TRAVELWOLF_ALPHA8_CANARY_OK",
"application_prompt_count": 1,
"user_turn_count": 1,
"authoritative_assistant_turn_count": 1,
"child_turn_count": 0,
"model_request_count_observed": false,
"cleanup_status": "pass",
"status": "pass"
},
"forge_ci": {
"status": "pass", "status": "pass",
"pr_run_id": "2702", "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."
"main_run_id": "2704"
}, },
"production": { "deployed_product_selection": {
"checked_at": "2026-07-20T06:51:48Z", "strategy": "mutable-latest",
"application_image_created_at": "2026-07-20T06:42:55.929825054Z", "status": "observed-configuration-drift-failure",
"internal_health_status": 200, "references": {
"authenticated_public_health_status": 200, "aigl": "docker-registry:5000/opencode-aigl:latest",
"container_status": "running", "blackline": "docker-registry:5000/opencode-blackline:latest",
"deployment_mode": "manual-isolated-kamal", "raven": "docker-registry:5000/opencode-raven:latest"
"status": "pass"
}, },
"rollback_operability": { "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."
"certified_previous_consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "8494c52d11bf2995c378398e977d82d06a09e046"
}, },
"compatibility_certified_at": "2026-07-20T06:51:48Z", "ci_built_product_images": {
"certification": { "aigl": {
"status": "pass", "tag_provenance": "docker-registry:5000/opencode-aigl:8d8550265dca0ac06a41d5427c585ede56bbb808",
"certified_at": "2026-07-20T06:51:48Z", "docker_image_id": "sha256:72c454f39d423618b75439122c9073dbba7c9071421e5f3abd12cf288dc1f898",
"tuple_sha256": "sha256:0321c4246bed6ab62587924a83204a9e2ca7517b1dec5e4aef35dcac400fcc8e", "source_commit": "8d8550265dca0ac06a41d5427c585ede56bbb808"
"evidence": [ },
{ "blackline": {
"path": "evidence/2026-07-20-travelwolf-alpha8-production.json", "tag_provenance": "docker-registry:5000/opencode-blackline:8d8550265dca0ac06a41d5427c585ede56bbb808",
"sha256": "sha256:ab8d8de768d415b3a68090507fb6ec40bdfad439fcaf0471beea4f52450e9d2f" "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"
}
} }
]
} }
}, },
"immediate_platform_rollback": { "candidate": {
"status": "uncertified-known-contract-mismatch", "status": "compatibility-certified",
"consumer_commit": "8494c52d11bf2995c378398e977d82d06a09e046", "certification_scope": "pre-merge-pr-head-candidate-only",
"opencode_ruby": { "promotion_eligible": false,
"version": "0.0.1.alpha6", "promotion_blockers": [
"git_commit": "9356268fb339269cd53763435af7ce88df1eeec7" "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_rails": {
"version": "0.0.1.alpha6",
"git_commit": "8d1eb7701fe9e24baa64c16d6af03c6ed1f423eb"
},
"runtime": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"application_image_id": "sha256:4291b256b37eeb694435093655db17154c837e53335b8d0eb8f1b07a744e856e",
"reported_version": "1.16.1"
},
"reason": "This retained application image is the platform's one-click target, but its alpha6 clients are not the certified alpha7 previous tuple."
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "531011defe4acc45a15ffe44a8e9da9cb610aaac",
"consumer_tree": "d0e47b2d90ace87c6cc9c4b94b2f22987441c1f6",
"opencode_ruby": { "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": {
@@ -314,47 +108,67 @@
"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": {
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a", "reported_version": "1.18.3",
"reported_version": "1.16.1" "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"
}
}
},
"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": { "application_canary": {
"checked_at": "2026-07-20T06:22:36.958Z", "checked_at": "2026-07-18T23:28:16.435369Z",
"final_text": "TRAVELWOLF_ALPHA7_CANARY_OK", "path": "Blackline::GenerateResponseJob",
"application_prompt_count": 1, "persisted_final_text": "compat-ok",
"user_turn_count": 1, "authoritative_final_text": "compat-ok",
"authoritative_assistant_turn_count": 1, "authoritative_assistant_count": 1,
"child_turn_count": 0, "model_request_count": 1,
"model_request_count_observed": false,
"cleanup_status": "pass", "cleanup_status": "pass",
"status": "pass" "status": "pass"
}, },
"forge_ci": { "certified_at": "2026-07-18T23:28:16.435369Z"
"status": "pass",
"pr_run_id": "2696",
"main_run_id": "2698"
}, },
"rollback_operability": { "previous": null
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true
}, },
"compatibility_certified_at": "2026-07-20T06:31:42Z", "travelwolf": {
"certification": { "profile": "rails-persisted-turn",
"status": "pass", "current": {
"certified_at": "2026-07-20T06:31:42Z",
"tuple_sha256": "sha256:20ecec3d2c3743efcd8575e71ec46dd7b86c2865e472511d16a96d57ddb8b40c",
"evidence": [
{
"path": "evidence/2026-07-20-travelwolf-alpha7-rollback.json",
"sha256": "sha256:d5e36ffc40f44ff28cbd7eee7bf209fa595ce57619081a76f8d049499b5d30a4"
}
]
}
},
"emergency_provenance": {
"status": "observed-production-contract-failed", "status": "observed-production-contract-failed",
"consumer_commit": "1ca756d64f058c9e0379f69fc4a3d53330cfcd15", "consumer_commit": "1ca756d64f058c9e0379f69fc4a3d53330cfcd15",
"opencode_ruby": { "opencode_ruby": {
@@ -369,168 +183,104 @@
"image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a", "image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"reported_version": "1.16.1" "reported_version": "1.16.1"
} }
}
}, },
"mushu": { "candidate": {
"profile": "ruby-rest-sse", "status": "compatibility-certified",
"current": { "certification_scope": "pre-merge-pr-head-candidate-only",
"status": "certified", "promotion_eligible": false,
"consumer_commit": "3f6754caab3f66537a5834f44ee63c1f05ecb047", "promotion_blockers": [
"consumer_tree": "726c3878e71f7a4cf8c0b8a3fa8fc15382bbab45", "consumer-ref-is-an-unmerged-pull-request-head",
"opencode_ruby": { "no-certified-previous-tuple"
"version": "0.0.1.alpha8",
"git_commit": "9277646a4bb2cf25a8384ffc140b154f49ea5766",
"source_provenance": "unpublished-exact-commit"
},
"runtime": {
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-521185613a864a16a76a21c7a60597ab3973bae9",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:a16bf0c4cd17698eba02244f6ba0e429b2238b6455e1415ff69fb08965c863a7",
"application_image": "docker-registry:5000/mushu@sha256:30f204184520d504c7c384c7f6e503fa3547217aaaef2f1d0d65884533bdeacd",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": "521185613a864a16a76a21c7a60597ab3973bae9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"preproduction_canary": {
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 2,
"mcp_context_count": 2,
"database_sha256": "sha256:7fbe32ff9621196a2504aeba5eca78cb0c9c670edd7a1168b1f81f667872e2b6",
"log_sha256": "sha256:51b698fcc8b7bce68e79cbae10997e19eb47a88985cbe24871d10740dda26225",
"status": "pass"
},
"application_canary": {
"started_at": "2026-07-20T07:03:17Z",
"checked_at": "2026-07-20T07:04:51.376351Z",
"successful_turn_count": 13,
"turn_count": 13,
"successful_conversation_count": 6,
"conversation_count": 6,
"successful_mcp_context_count": 1,
"mcp_context_count": 1,
"status": "pass"
},
"forge_ci": {
"status": "pass",
"pr_run_id": "2700",
"main_run_id": "2705"
},
"production": {
"checked_at": "2026-07-20T07:13:20Z",
"web_health_status": 200,
"status_health_status": 200,
"runtime_readiness": "ready",
"containers": [
"web",
"calendar",
"telegram",
"opencode"
], ],
"deployment_mode": "manual", "consumer_commit": "b9a2c2dd361a6b2ea547cc67ee817b8eb4ce31e8",
"status": "pass" "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"
}, },
"rollback_operability": {
"certified_previous_consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"immediate_platform_rollback_is_certified_tuple": false,
"immediate_platform_rollback_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922"
},
"compatibility_certified_at": "2026-07-20T07:13:51Z",
"certification": {
"status": "pass",
"certified_at": "2026-07-20T07:13:51Z",
"tuple_sha256": "sha256:804410a63cf2b6e4056d6dd9ce289ac72464f441c9785b6fc8dd50016263a1a8",
"evidence": [
{
"path": "evidence/2026-07-20-mushu-alpha8-production.json",
"sha256": "sha256:786bf92a02588b951b5b7f7a21ed915b7ac1120538c5f894af027c190379170f"
}
]
}
},
"immediate_platform_rollback": {
"status": "uncertified-known-contract-failure",
"consumer_commit": "82ca30b18e1f38b55e2bed91cbd46f1eaf4a5922",
"opencode_ruby": {
"version": "0.0.1.alpha2",
"git_commit": "889d38332f98f5f7b76d16952b7204d8a5b9a662"
},
"opencode_rails": null,
"runtime": {
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"application_image": null,
"application_image_provenance": "not-retained",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"reason": "The one-click application image is not retained and the alpha2 client failed the contract; use the certified alpha7 previous source tuple instead."
},
"candidate": null,
"previous": {
"status": "certified",
"consumer_commit": "af2e9b0ce7ccacf49500450595a67c765a9d617a",
"consumer_tree": "f43f48972ab444310508e9067831dfc6977f1a24",
"opencode_ruby": { "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": {
"version": "0.0.1.alpha7",
"git_commit": "2a391ccad1d098e4bb51eb19b6ca52adaa79e5cb",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"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": {
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1", "image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"registry_ref": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a", "reported_version": "1.16.1"
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"build_source_commit": null,
"provenance_strength": "one-time-tag-source-attestation",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
}, },
"application_canary": { "deterministic_contract": {
"started_at": "2026-07-20T06:37:55Z", "evidence": "evidence/2026-07-18-opencode-ruby-alpha7.json",
"checked_at": "2026-07-20T06:39:46.921805Z", "checked_at": "2026-07-18T23:23:03Z",
"successful_turn_count": 13, "image": "ghcr.io/anomalyco/opencode@sha256:e975a0647576016dfdf77d54b979ca30d32b4750472c10263e9894aad6628c2a",
"turn_count": 13, "opencode_ruby_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"successful_conversation_count": 6, "expected_text": "compat-ok",
"conversation_count": 6, "full_text": "compat-ok",
"successful_mcp_context_count": 7, "authoritative_assistant_count": 1,
"mcp_context_count": 7, "model_request_count": 1,
"database_sha256": "sha256:a567d2ff9a906c9b78831bb6fa43153af539f1e76ee63bf35961472f55651d74",
"log_sha256": "sha256:a35a83b003a1f082b8a721b3faad8b35cb71dc9d1ceaedfc3cec3b26916e85b9",
"status": "pass" "status": "pass"
}, },
"forge_ci": { "application_canary": {
"status": "not_run", "run_id": "travelwolf-alpha7-ref-20260718T232211Z-48eadf",
"run_ids": [], "checked_at": "2026-07-18T23:22:26.216140Z",
"reason": "Frozen exact head predates the runner-only system-test workflow repair; clean-worktree application canary is authoritative." "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"
}, },
"source_availability": { "certified_at": "2026-07-18T23:35:57Z"
"origin_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719",
"backup_ref": "refs/heads/review/mushu-opencode-alpha7-consumer-final-20260719"
}, },
"rollback_operability": { "previous": null
"source_archived_on_all_remotes": true,
"prebuilt_application_image_retained": false,
"application_rebuild_required": true
}, },
"compatibility_certified_at": "2026-07-20T06:39:46.921805Z", "mushu": {
"certification": { "profile": "ruby-rest-sse",
"status": "pass", "current": {
"certified_at": "2026-07-20T06:39:46.921805Z",
"tuple_sha256": "sha256:8d7d3cb2d3e2d920c900a9c1ada365026872904588cff0b166903d1d6d32183f",
"evidence": [
{
"path": "evidence/2026-07-20-mushu-alpha7-rollback.json",
"sha256": "sha256:143deb6b400a05305991c956eaf80f07fdee76a9cce98af96f8eee395ed9847d"
}
]
}
},
"emergency_provenance": {
"status": "observed-production-contract-failed", "status": "observed-production-contract-failed",
"consumer_commit": "fe16e5bab028e3c92cd5508ef2cebd9bfa23386f", "consumer_commit": "fe16e5bab028e3c92cd5508ef2cebd9bfa23386f",
"opencode_ruby": { "opencode_ruby": {
@@ -539,11 +289,89 @@
}, },
"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": {
"status": "compatibility-certified",
"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": {
"version": "0.0.1.alpha7",
"git_commit": "78b6f9c9e9c7d58b699af1c3c17764acd33de798",
"tag_provenance": {
"tag": "v0.0.1.alpha7",
"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": {
"tag_provenance": "docker-registry:5000/mushu-opencode:sha-99d6328b1",
"image": "docker-registry:5000/mushu-opencode@sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"docker_image_id": "sha256:ed6293ca87e6db1dc07870d3ed6da21960743e6e2e904533b3c566d334b58d6a",
"source_commit": "99d6328b18ff8a340a928449a2aa8bc184a063f9",
"base_image": "docker-registry:5000/mushu-opencode-base@sha256:de8a6adeefb30ce1c474cde23fe173dfe059434a13df0a54d14c7473edb33e16",
"reported_version": "0.0.0-permission-shortcuts-202606300721"
},
"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"
},
"certified_at": "2026-07-18T23:26:06.865190Z"
},
"previous": null
} }
} }
} }

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -13,22 +13,13 @@ module OpenCodeCompat
end end
def assert_model_request_count!(value) def assert_model_request_count!(value)
count = Integer(value.to_s, 10) count = Integer(value, 10)
return if count == 1 return if count == 1
raise ContractError, "Expected exactly one deterministic model request, got #{count}" raise ContractError, "Expected exactly one deterministic model request, got #{count}"
rescue ArgumentError, TypeError rescue ArgumentError, TypeError
raise ContractError, "Deterministic model request count is not an integer: #{value.inspect}" raise ContractError, "Deterministic model request count is not an integer: #{value.inspect}"
end end
def assert_authoritative_assistant_count!(value)
count = Integer(value.to_s, 10)
return if count == 1
raise ContractError, "Expected exactly one authoritative assistant message, got #{count}"
rescue ArgumentError, TypeError
raise ContractError, "Authoritative assistant message count is not an integer: #{value.inspect}"
end
end end
end end

View File

@@ -37,7 +37,7 @@ begin
messages = client.get_messages(session_id) messages = client.get_messages(session_id)
assistant_messages = Array(messages).select { |message| message.dig(:info, :role) == "assistant" } assistant_messages = Array(messages).select { |message| message.dig(:info, :role) == "assistant" }
OpenCodeCompat::ExactLiveContract.assert_authoritative_assistant_count!(assistant_messages.length) raise "No authoritative assistant exchange returned" if assistant_messages.empty?
puts JSON.generate( puts JSON.generate(
status: "pass", status: "pass",

View File

@@ -1,422 +0,0 @@
# frozen_string_literal: true
require "fileutils"
require "json"
require "open3"
require "rubygems"
module OpenCodeCompat
class LockstepClientContract
class ContractError < StandardError; end
class GitCommandError < ContractError
attr_reader :exitstatus
def initialize(message, exitstatus:)
@exitstatus = exitstatus
super(message)
end
end
CONTRACT_NAME = "opencode-client-lockstep"
OPENCODE_RUBY_GIT_URI = "https://github.com/ajaynomics/opencode-ruby.git"
PUBLICATION_PROVENANCE = {
"published" => "annotated-tag",
"unpublished" => "commit"
}.freeze
REQUIRED_ENV = %w[
OPENCODE_CLIENT_PUBLICATION_STATE
OPENCODE_RUBY_PATH
OPENCODE_RUBY_COMMIT
OPENCODE_RUBY_PROVENANCE_KIND
OPENCODE_RUBY_VERSION
OPENCODE_RAILS_PATH
OPENCODE_RAILS_COMMIT
OPENCODE_RAILS_PROVENANCE_KIND
OPENCODE_RAILS_VERSION
].freeze
def self.from_environment(env: ENV, git_resolver: nil, git_head_resolver: nil)
require "bundler"
require "opencode-rails"
new(
env: env,
git_resolver: git_resolver,
git_head_resolver: git_head_resolver,
loaded_specs: Gem.loaded_specs,
bundle_specs: Bundler.load.specs,
runtime_ruby_version: RUBY_VERSION,
ruby_version: Opencode::VERSION,
rails_version: Opencode::RAILS_VERSION
)
end
def self.canonical_json(value)
JSON.generate(canonicalize(value))
end
def self.canonicalize(value)
case value
when Hash
value.keys.map(&:to_s).sort.to_h do |key|
source_key = value.key?(key) ? key : value.keys.find { |candidate| candidate.to_s == key }
[key, canonicalize(value.fetch(source_key))]
end
when Array
value.map { |entry| canonicalize(entry) }
else
value
end
end
private_class_method :canonicalize
def initialize(
env:,
loaded_specs:,
bundle_specs:,
ruby_version:,
rails_version:,
runtime_ruby_version: RUBY_VERSION,
git_resolver: nil,
git_head_resolver: nil
)
@env = env
@loaded_specs = loaded_specs
@bundle_specs = bundle_specs
@ruby_version = ruby_version.to_s
@rails_version = rails_version.to_s
@runtime_ruby_version = runtime_ruby_version.to_s
@git_resolver = git_resolver || method(:resolve_git)
@git_head_resolver = git_head_resolver
end
def verify
ensure_required_environment!
ruby_path = realpath("OPENCODE_RUBY_PATH")
rails_path = realpath("OPENCODE_RAILS_PATH")
ruby_commit = exact_commit("OPENCODE_RUBY_COMMIT")
rails_commit = exact_commit("OPENCODE_RAILS_COMMIT")
expected_ruby_version = exact_version("OPENCODE_RUBY_VERSION")
expected_rails_version = exact_version("OPENCODE_RAILS_VERSION")
publication_state = exact_publication_state
assert_equal!(ruby_commit, checkout_head(ruby_path, "opencode-ruby"), "opencode-ruby checkout commit")
assert_equal!(rails_commit, checkout_head(rails_path, "opencode-rails"), "opencode-rails checkout commit")
ruby_source_provenance = source_provenance(
path: ruby_path,
label: "opencode-ruby",
prefix: "OPENCODE_RUBY",
publication_state: publication_state,
expected_commit: ruby_commit,
expected_version: expected_ruby_version
)
rails_source_provenance = source_provenance(
path: rails_path,
label: "opencode-rails",
prefix: "OPENCODE_RAILS",
publication_state: publication_state,
expected_commit: rails_commit,
expected_version: expected_rails_version
)
assert_equal!(expected_ruby_version, @ruby_version, "loaded Opencode::VERSION")
assert_equal!(expected_rails_version, @rails_version, "loaded Opencode::RAILS_VERSION")
assert_equal!(expected_ruby_version, expected_rails_version, "lockstep client version")
ruby_spec = loaded_spec!("opencode-ruby")
rails_spec = loaded_spec!("opencode-rails")
assert_equal!(expected_ruby_version, ruby_spec.version.to_s, "loaded opencode-ruby gem version")
assert_equal!(expected_rails_version, rails_spec.version.to_s, "loaded opencode-rails gem version")
assert_equal!(rails_path, File.realpath(rails_spec.full_gem_path), "loaded opencode-rails checkout")
dependency = exact_runtime_dependency!(rails_spec, expected_ruby_version)
bundle_spec = bundled_ruby_spec!
source = bundle_spec.source
unless source && source.respond_to?(:revision) && source.respond_to?(:ref) && source.respond_to?(:uri) &&
source.class.name.match?(/(?:\A|::)Git\z/)
raise ContractError, "Bundler opencode-ruby source must be a Git source"
end
bundler_revision = source.revision.to_s
bundler_ref = source.ref.to_s
bundler_uri = source.uri.to_s
assert_exact_commit!(bundler_revision, "Bundler opencode-ruby revision")
assert_exact_commit!(bundler_ref, "Bundler opencode-ruby ref")
assert_equal!(ruby_commit, bundler_revision, "Bundler opencode-ruby revision")
assert_equal!(ruby_commit, bundler_ref, "Bundler opencode-ruby ref")
assert_equal!(OPENCODE_RUBY_GIT_URI, bundler_uri, "Bundler opencode-ruby URI")
{
"contract" => CONTRACT_NAME,
"publication_state" => publication_state,
"opencode_rails" => {
"checkout_commit" => rails_commit,
"gem_version" => rails_spec.version.to_s,
"loaded_version" => @rails_version,
"source_provenance" => rails_source_provenance,
"runtime_dependency" => {
"name" => dependency.name,
"requirement" => dependency.requirement.to_s
}
},
"opencode_ruby" => {
"bundler_git_ref" => bundler_ref,
"bundler_git_revision" => bundler_revision,
"bundler_git_uri" => bundler_uri,
"bundler_source" => source.class.name,
"checkout_commit" => ruby_commit,
"gem_version" => ruby_spec.version.to_s,
"loaded_version" => @ruby_version,
"source_provenance" => ruby_source_provenance
},
"ruby_runtime_version" => @runtime_ruby_version,
"schema_version" => 2,
"status" => "pass",
"workflow" => {
"head_sha" => @env["OPENCODE_COMPAT_HEAD_SHA"],
"repository" => @env["OPENCODE_COMPAT_REPOSITORY"],
"run_attempt" => @env["OPENCODE_COMPAT_RUN_ATTEMPT"],
"run_id" => @env["OPENCODE_COMPAT_RUN_ID"],
"run_url" => @env["OPENCODE_COMPAT_RUN_URL"]
}
}
rescue Errno::ENOENT, Errno::EACCES => error
raise ContractError, error.message
end
def emit(io: $stdout)
document = verify
json = self.class.canonical_json(document)
evidence_path = @env["OPENCODE_COMPAT_EVIDENCE_PATH"].to_s
unless evidence_path.empty?
expanded_path = File.expand_path(evidence_path)
FileUtils.mkdir_p(File.dirname(expanded_path))
File.binwrite(expanded_path, "#{json}\n")
end
io.puts(json)
document
end
private
def ensure_required_environment!
missing = REQUIRED_ENV.select { |name| @env[name].to_s.empty? }
return if missing.empty?
raise ContractError, "missing required environment: #{missing.join(', ')}"
end
def realpath(name)
File.realpath(@env.fetch(name))
end
def exact_commit(name)
value = @env.fetch(name)
assert_exact_commit!(value, name)
value
end
def exact_publication_state
value = @env.fetch("OPENCODE_CLIENT_PUBLICATION_STATE")
return value if PUBLICATION_PROVENANCE.key?(value)
raise ContractError,
"OPENCODE_CLIENT_PUBLICATION_STATE must be one of #{PUBLICATION_PROVENANCE.keys.join(', ')}, got #{value.inspect}"
end
def exact_tag(name)
exact_tag_value(@env.fetch(name), name)
end
def exact_tag_value(value, label)
if value.match?(/\A[A-Za-z0-9][A-Za-z0-9._-]*\z/) && !value.include?("..") && !value.end_with?(".")
return value
end
raise ContractError, "#{label} must be a safe exact Git tag name, got #{value.inspect}"
end
def exact_tag_object(name)
value = @env.fetch(name)
return value if value.match?(/\A[0-9a-f]{40}\z/)
raise ContractError,
"#{name} must be a full lowercase 40-character Git object ID, got #{value.inspect}"
end
def assert_exact_commit!(value, label)
return if value.match?(/\A[0-9a-f]{40}\z/)
raise ContractError, "#{label} must be a full lowercase 40-character Git commit, got #{value.inspect}"
end
def exact_version(name)
value = @env.fetch(name)
return value if Gem::Version.correct?(value)
raise ContractError, "#{name} must be an exact RubyGems version, got #{value.inspect}"
end
def checkout_head(path, label)
head = if @git_head_resolver
@git_head_resolver.call(path).to_s.strip
else
git_value(path, "#{label} checkout HEAD", "rev-parse", "--verify", "HEAD^{commit}")
end
assert_exact_commit!(head, "#{label} checkout HEAD")
head
rescue ContractError
raise
rescue StandardError => error
raise ContractError, "could not resolve #{label} checkout HEAD: #{error.message}"
end
def source_provenance(path:, label:, prefix:, publication_state:, expected_commit:, expected_version:)
kind_name = "#{prefix}_PROVENANCE_KIND"
kind = @env.fetch(kind_name)
expected_kind = PUBLICATION_PROVENANCE.fetch(publication_state)
assert_equal!(expected_kind, kind, "#{label} provenance kind for #{publication_state} candidate")
case kind
when "commit"
ensure_unpublished_tag_absent!(path, label, expected_version)
supplied_tag_fields = %W[#{prefix}_TAG #{prefix}_TAG_OBJECT].select do |name|
!@env[name].to_s.empty?
end
unless supplied_tag_fields.empty?
raise ContractError,
"#{label} unpublished commit provenance must not supply #{supplied_tag_fields.join(', ')}"
end
{"commit" => expected_commit, "kind" => "commit"}
when "annotated-tag"
annotated_tag_provenance(
path: path,
label: label,
tag_name: "#{prefix}_TAG",
tag_object: "#{prefix}_TAG_OBJECT",
expected_commit: expected_commit
).merge("kind" => "annotated-tag")
else
raise ContractError, "#{kind_name} has unsupported value #{kind.inspect}"
end
end
def ensure_unpublished_tag_absent!(path, label, expected_version)
tag = exact_tag_value("v#{expected_version}", "#{label} expected release tag")
tag_ref = "refs/tags/#{tag}"
resolved = begin
@git_resolver.call(path, "rev-parse", "--verify", "--quiet", tag_ref).to_s.strip
rescue GitCommandError => error
raise ContractError, "could not check #{label} release tag: #{error.message}" unless error.exitstatus == 1
nil
rescue KeyError
nil
end
return if resolved.nil? || resolved.empty?
raise ContractError,
"#{label} release tag #{tag} already exists; publication_state must be published with annotated-tag provenance"
end
def annotated_tag_provenance(path:, label:, tag_name:, tag_object:, expected_commit:)
tag = exact_tag(tag_name)
object = exact_tag_object(tag_object)
tag_ref = "refs/tags/#{tag}"
object_type = git_value(path, "#{label} annotated tag object type", "cat-file", "-t", object)
assert_equal!("tag", object_type, "#{label} annotated tag object type")
resolved_object = git_value(path, "#{label} local tag ref", "rev-parse", "--verify", tag_ref)
assert_exact_commit!(resolved_object, "#{label} local tag ref object")
assert_equal!(object, resolved_object, "#{label} local tag ref object")
object_commit = git_value(path, "#{label} annotated tag peel", "rev-parse", "--verify", "#{object}^{commit}")
assert_exact_commit!(object_commit, "#{label} annotated tag peeled commit")
assert_equal!(expected_commit, object_commit, "#{label} annotated tag peeled commit")
ref_commit = git_value(path, "#{label} local tag ref peel", "rev-parse", "--verify", "#{tag_ref}^{commit}")
assert_exact_commit!(ref_commit, "#{label} local tag ref peeled commit")
assert_equal!(expected_commit, ref_commit, "#{label} local tag ref peeled commit")
{
"annotated_tag_object" => object,
"peeled_commit" => object_commit,
"tag" => tag
}
end
def git_value(path, label, *arguments)
@git_resolver.call(path, *arguments).to_s.strip
rescue StandardError => error
raise ContractError, "could not resolve #{label}: #{error.message}"
end
def resolve_git(path, *arguments)
stdout, stderr, status = Open3.capture3("git", "-C", path, *arguments)
return stdout.strip if status.success?
detail = stderr.strip
detail = "git exited #{status.exitstatus}" if detail.empty?
raise GitCommandError.new(detail, exitstatus: status.exitstatus)
end
def loaded_spec!(name)
@loaded_specs.fetch(name)
rescue KeyError
raise ContractError, "#{name} is not present in Gem.loaded_specs"
end
def exact_runtime_dependency!(rails_spec, ruby_version)
dependencies = rails_spec.runtime_dependencies.select { |dependency| dependency.name == "opencode-ruby" }
unless dependencies.length == 1
raise ContractError, "opencode-rails must declare exactly one runtime dependency on opencode-ruby"
end
dependency = dependencies.first
expected = [["=", Gem::Version.new(ruby_version)]]
unless dependency.requirement.requirements == expected
raise ContractError,
"opencode-rails must require opencode-ruby = #{ruby_version}, got #{dependency.requirement}"
end
dependency
end
def bundled_ruby_spec!
specs = @bundle_specs.select { |spec| spec.name == "opencode-ruby" }
return specs.first if specs.length == 1
raise ContractError, "Bundler must resolve exactly one opencode-ruby spec, found #{specs.length}"
end
def assert_equal!(expected, actual, label)
return if expected == actual
raise ContractError, "#{label} must equal #{expected.inspect}, got #{actual.inspect}"
end
end
end
if $PROGRAM_NAME == __FILE__
begin
OpenCodeCompat::LockstepClientContract.from_environment.emit
rescue StandardError => error
failure = OpenCodeCompat::LockstepClientContract.canonical_json(
"contract" => OpenCodeCompat::LockstepClientContract::CONTRACT_NAME,
"error" => "#{error.class}: #{error.message}",
"schema_version" => 2,
"status" => "fail"
)
if (evidence_path = ENV["OPENCODE_COMPAT_EVIDENCE_PATH"]) && !evidence_path.empty?
expanded_path = File.expand_path(evidence_path)
FileUtils.mkdir_p(File.dirname(expanded_path))
File.binwrite(expanded_path, "#{failure}\n")
end
warn failure
exit 1
end
end

View File

@@ -1,8 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
require "json" require "json"
require "fileutils"
require "time"
root = File.expand_path("..", __dir__) root = File.expand_path("..", __dir__)
gem_path = File.expand_path(ENV.fetch("OPENCODE_RUBY_PATH")) gem_path = File.expand_path(ENV.fetch("OPENCODE_RUBY_PATH"))
@@ -11,12 +9,6 @@ require "opencode-ruby"
manifest = JSON.parse(File.read(File.join(root, "fixtures/manifest.json"))) manifest = JSON.parse(File.read(File.join(root, "fixtures/manifest.json")))
failures = [] failures = []
write_evidence = lambda do |document|
next unless (evidence_path = ENV["OPENCODE_COMPAT_EVIDENCE_PATH"])
FileUtils.mkdir_p(File.dirname(evidence_path))
File.write(evidence_path, JSON.pretty_generate(document) + "\n")
end
manifest.fetch("fixtures").each do |entry| manifest.fetch("fixtures").each do |entry|
reply = Opencode::Reply.new reply = Opencode::Reply.new
@@ -64,45 +56,14 @@ manifest.fetch("fixtures").each do |entry|
end end
unless failures.empty? unless failures.empty?
evidence = {
schema_version: 1,
kind: "shared-ruby-fixture-contract",
status: "fail",
checked_at: Time.now.utc.iso8601,
adapter: "opencode-ruby",
adapter_version: Opencode::VERSION,
adapter_commit: ENV["OPENCODE_RUBY_COMMIT"],
fixture_count: manifest.fetch("fixtures").length,
failures: failures,
workflow: {
run_id: ENV["OPENCODE_COMPAT_RUN_ID"],
run_attempt: ENV["OPENCODE_COMPAT_RUN_ATTEMPT"],
head_sha: ENV["OPENCODE_COMPAT_HEAD_SHA"],
repository: ENV["OPENCODE_COMPAT_REPOSITORY"],
run_url: ENV["OPENCODE_COMPAT_RUN_URL"]
}
}
write_evidence.call(evidence)
warn failures.join("\n") warn failures.join("\n")
exit 1 exit 1
end end
evidence = { puts JSON.generate(
schema_version: 1,
kind: "shared-ruby-fixture-contract",
status: "pass", status: "pass",
checked_at: Time.now.utc.iso8601,
adapter: "opencode-ruby", adapter: "opencode-ruby",
adapter_version: Opencode::VERSION, adapter_version: Opencode::VERSION,
adapter_commit: ENV["OPENCODE_RUBY_COMMIT"], adapter_commit: ENV["OPENCODE_RUBY_COMMIT"],
fixture_count: manifest.fetch("fixtures").length, fixture_count: manifest.fetch("fixtures").length
workflow: { )
run_id: ENV["OPENCODE_COMPAT_RUN_ID"],
run_attempt: ENV["OPENCODE_COMPAT_RUN_ATTEMPT"],
head_sha: ENV["OPENCODE_COMPAT_HEAD_SHA"],
repository: ENV["OPENCODE_COMPAT_REPOSITORY"],
run_url: ENV["OPENCODE_COMPAT_RUN_URL"]
}
}
puts JSON.generate(evidence)
write_evidence.call(evidence)

View 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"
}
}
}
}
]
}
}
}

View File

@@ -1,9 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative "../lib/opencode_compat/client_candidate"
root = File.expand_path("..", __dir__)
path = ARGV.fetch(0, File.join(root, "manifests/client-candidate.json"))
candidate = OpenCodeCompat::ClientCandidate.load(path)
candidate.github_outputs.each { |key, value| puts "#{key}=#{value}" }

View File

@@ -1,47 +1,14 @@
# frozen_string_literal: true # frozen_string_literal: true
require "json" require "json"
require_relative "../lib/opencode_compat/client_candidate"
root = File.expand_path("..", __dir__) root = File.expand_path("..", __dir__)
manifest = JSON.parse(File.read(File.join(root, "manifests/image-matrix.json"))) manifest = JSON.parse(File.read(File.join(root, "manifests/image-matrix.json")))
candidate = OpenCodeCompat::ClientCandidate.load(File.join(root, "manifests/client-candidate.json"))
candidate.verify!
clients = candidate.document.fetch("clients")
expected_candidate = {
"release_train" => candidate.document.fetch("release_train"),
"publication_state" => candidate.document.fetch("publication_state"),
"opencode_ruby_commit" => clients.dig("opencode-ruby", "ref"),
"opencode_rails_commit" => clients.dig("opencode-rails", "ref")
}
matrix_candidate = manifest.fetch("client_candidate")
unless expected_candidate.all? { |key, value| matrix_candidate[key] == value }
abort "image matrix client_candidate must equal the exact client candidate"
end
unless %w[pending certified].include?(matrix_candidate.fetch("certification_status"))
abort "image matrix client_candidate certification_status must be pending or certified"
end
matrix = manifest.fetch("public_ci").map do |target| matrix = manifest.fetch("public_ci").map do |target|
id = target.fetch("id")
profiles = target.fetch("profiles")
certification_scope = target.fetch("certification_scope", "shared-client-contract-only")
unless profiles == ["ruby-rest-sse"]
abort "#{id}: public CI executes exactly ruby-rest-sse; got profiles=#{profiles.inspect}"
end
unless certification_scope == "shared-client-contract-only"
abort "#{id}: certification_scope must be shared-client-contract-only; got #{certification_scope.inspect}"
end
{ {
"id" => id, "id" => target.fetch("id"),
"version" => target.fetch("version"), "version" => target.fetch("version"),
"image" => target.fetch("image"), "image" => target.fetch("image")
"consumers" => target.fetch("consumers"),
"profiles" => profiles,
"required_consumer_profiles" => target.fetch("required_consumer_profiles"),
"certification_scope" => certification_scope,
"certification_status" => target.fetch("certification_status")
} }
end end

View File

@@ -1,28 +0,0 @@
#!/usr/bin/env ruby
# frozen_string_literal: true
require "ipaddr"
route_path = ARGV.fetch(0, "/proc/net/route")
routes = File.foreach(route_path).drop(1).filter_map do |line|
fields = line.split
next unless fields.length >= 8
next unless fields.fetch(1) == "00000000"
next unless (fields.fetch(3).to_i(16) & 0x2) == 0x2
fields
end
routes.sort_by { |fields| fields.fetch(6).to_i }.each do |fields|
gateway_hex = fields.fetch(2)
next unless gateway_hex.match?(/\A[0-9A-Fa-f]{8}\z/)
gateway = gateway_hex.scan(/../).reverse.map { |octet| octet.to_i(16) }.join(".")
address = IPAddr.new(gateway)
next unless address.ipv4? && address.private?
puts address
exit 0
end
abort "no private IPv4 default-route gateway found in #{route_path}"

View File

@@ -8,13 +8,6 @@ tag, published_at, release_url, digest = ARGV
abort "usage: record_upstream_candidate.rb TAG PUBLISHED_AT RELEASE_URL DIGEST" unless digest abort "usage: record_upstream_candidate.rb TAG PUBLISHED_AT RELEASE_URL DIGEST" unless digest
abort "invalid upstream release tag: #{tag.inspect}" unless tag.match?(/\Av\d+\.\d+\.\d+\z/) abort "invalid upstream release tag: #{tag.inspect}" unless tag.match?(/\Av\d+\.\d+\.\d+\z/)
abort "invalid OCI digest: #{digest.inspect}" unless digest.match?(/\Asha256:[0-9a-f]{64}\z/) abort "invalid OCI digest: #{digest.inspect}" unless digest.match?(/\Asha256:[0-9a-f]{64}\z/)
begin
Time.iso8601(published_at)
rescue ArgumentError, TypeError
abort "invalid upstream publication timestamp: #{published_at.inspect}"
end
expected_release_url = "https://github.com/anomalyco/opencode/releases/tag/#{tag}"
abort "invalid upstream release URL: #{release_url.inspect}" unless release_url == expected_release_url
version = tag.delete_prefix("v") version = tag.delete_prefix("v")
image = "ghcr.io/anomalyco/opencode@#{digest}" image = "ghcr.io/anomalyco/opencode@#{digest}"
@@ -22,37 +15,9 @@ upstream_path = File.join(root, "manifests/upstream.json")
matrix_path = File.join(root, "manifests/image-matrix.json") matrix_path = File.join(root, "manifests/image-matrix.json")
upstream = JSON.parse(File.read(upstream_path)) upstream = JSON.parse(File.read(upstream_path))
matrix = JSON.parse(File.read(matrix_path)) matrix = JSON.parse(File.read(matrix_path))
public_ci = matrix.fetch("public_ci")
abort "public_ci must be an array" unless public_ci.is_a?(Array)
matching_targets = public_ci.select { |target| target.fetch("image") == image } exit 0 if upstream.fetch("release_tag") == tag
if matching_targets.length > 1
abort "image #{image} must appear exactly once in public_ci; found #{matching_targets.length}"
end
matrix_changed = false
if matching_targets.empty?
target_id = "upstream-#{version}-#{digest.delete_prefix('sha256:')}"
if public_ci.any? { |target| target.fetch("id") == target_id }
abort "cannot add #{image}: public_ci id #{target_id.inspect} is already used by another image"
end
public_ci << {
"id" => target_id,
"version" => version,
"image" => image,
"tag_provenance" => "ghcr.io/anomalyco/opencode:#{version}",
"consumers" => [],
"profiles" => ["ruby-rest-sse"],
"required_consumer_profiles" => [],
"certification_scope" => "shared-client-contract-only",
"certification_status" => "pending"
}
matrix_changed = true
end
upstream_changed = upstream.fetch("release_tag") != tag || upstream.fetch("image") != image
if upstream_changed
upstream.merge!( upstream.merge!(
"release_tag" => tag, "release_tag" => tag,
"version" => version, "version" => version,
@@ -61,7 +26,19 @@ if upstream_changed
"image" => image, "image" => image,
"observed_at" => Time.now.utc.iso8601 "observed_at" => Time.now.utc.iso8601
) )
unless matrix.fetch("public_ci").any? { |target| target.fetch("image") == image }
matrix.fetch("public_ci") << {
"id" => "upstream-#{version}",
"version" => version,
"image" => image,
"tag_provenance" => "ghcr.io/anomalyco/opencode:#{version}",
"consumers" => ["upstream-candidate"],
"profiles" => ["ruby-rest-sse"],
"required_consumer_profiles" => ["rails-persisted-turn", "voice-stream", "strict-v2", "plugin-ledger", "provider-hooks"],
"certification_status" => "pending"
}
end end
File.write(upstream_path, JSON.pretty_generate(upstream) + "\n") if upstream_changed File.write(upstream_path, JSON.pretty_generate(upstream) + "\n")
File.write(matrix_path, JSON.pretty_generate(matrix) + "\n") if matrix_changed File.write(matrix_path, JSON.pretty_generate(matrix) + "\n")

View File

@@ -2,146 +2,46 @@
set -euo pipefail set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
image="${OPENCODE_IMAGE:-}" image="${OPENCODE_IMAGE:?set OPENCODE_IMAGE to an immutable image digest}"
gem_path="${OPENCODE_RUBY_PATH:-}" gem_path="${OPENCODE_RUBY_PATH:?set OPENCODE_RUBY_PATH to the candidate checkout}"
expected_gem_commit="${OPENCODE_RUBY_COMMIT:-}"
evidence_path="${OPENCODE_COMPAT_EVIDENCE_PATH:-}"
probe_host="${OPENCODE_PROBE_HOST:-127.0.0.1}"
container_name="opencode-compat-${RANDOM}-$$"
llm_container_name="opencode-compat-llm-${RANDOM}-$$"
network_name="opencode-compat-net-${RANDOM}-$$"
python_image="python@sha256:399babc8b49529dabfd9c922f2b5eea81d611e4512e3ed250d75bd2e7683f4b0"
network_created=0
llm_container_started=0
opencode_container_started=0
gem_commit=""
write_failure_evidence() { if [[ "$image" != *@sha256:* ]]; then
local exit_status="$1" if [[ "${ALLOW_EXACT_IMAGE_ID:-0}" != "1" || "$image" != sha256:* ]]; then
[[ -n "$evidence_path" ]] || return 0
mkdir -p "$(dirname "$evidence_path")"
jq -n \
--argjson schema_version 1 \
--arg kind shared-client-image-contract \
--arg status fail \
--arg checked_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--arg matrix_target "${OPENCODE_MATRIX_ID:-local}" \
--arg image "$image" \
--arg ruby_commit "${gem_commit:-}" \
--arg rails_commit "${OPENCODE_RAILS_COMMIT:-}" \
--arg run_id "${OPENCODE_COMPAT_RUN_ID:-}" \
--arg run_attempt "${OPENCODE_COMPAT_RUN_ATTEMPT:-}" \
--arg head_sha "${OPENCODE_COMPAT_HEAD_SHA:-}" \
--arg repository "${OPENCODE_COMPAT_REPOSITORY:-}" \
--arg run_url "${OPENCODE_COMPAT_RUN_URL:-}" \
--argjson exit_status "$exit_status" \
'{
schema_version:$schema_version,
kind:$kind,
status:$status,
checked_at:$checked_at,
matrix_target:$matrix_target,
certification_scope:"shared-client-contract-only",
executed_profiles:[],
image:{requested:$image},
clients:{opencode_ruby:{commit:$ruby_commit},opencode_rails:{commit:$rails_commit,executed:false}},
failure:{exit_status:$exit_status},
workflow:{run_id:$run_id,run_attempt:$run_attempt,head_sha:$head_sha,repository:$repository,run_url:$run_url}
}' >"$evidence_path"
}
cleanup() {
local status=$?
trap - EXIT
if [[ "$status" != "0" ]]; then
echo "OpenCode compatibility probe failed; container log follows" >&2
if [[ "$opencode_container_started" == "1" ]]; then
docker logs "$container_name" >&2 2>/dev/null || true
fi
echo "Deterministic model request summary follows" >&2
if [[ "$llm_container_started" == "1" ]]; then
docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/stats >&2 2>/dev/null || true
fi
echo >&2
write_failure_evidence "$status" || true
fi
if [[ "$opencode_container_started" == "1" ]]; then
docker rm -f "$container_name" >/dev/null 2>&1 || true
fi
if [[ "$llm_container_started" == "1" ]]; then
docker rm -f "$llm_container_name" >/dev/null 2>&1 || true
fi
if [[ "$network_created" == "1" ]]; then
docker network rm "$network_name" >/dev/null 2>&1 || true
fi
exit "$status"
}
trap cleanup EXIT
if [[ -n "$evidence_path" ]]; then
mkdir -p "$(dirname "$evidence_path")"
: >"$evidence_path"
fi
if [[ -z "$image" ]]; then
echo "set OPENCODE_IMAGE to an immutable image digest" >&2
exit 2
fi
if [[ -z "$gem_path" ]]; then
echo "set OPENCODE_RUBY_PATH to the candidate checkout" >&2
exit 2
fi
if [[ ! "$image" =~ ^[^[:space:]@]+@sha256:[0-9a-f]{64}$ ]]; then
if [[ "${ALLOW_EXACT_IMAGE_ID:-0}" != "1" || ! "$image" =~ ^sha256:[0-9a-f]{64}$ ]]; then
echo "OPENCODE_IMAGE must be an OCI digest (or an exact local image ID with ALLOW_EXACT_IMAGE_ID=1)" >&2 echo "OPENCODE_IMAGE must be an OCI digest (or an exact local image ID with ALLOW_EXACT_IMAGE_ID=1)" >&2
exit 2 exit 2
fi fi
fi fi
if ! ruby -ripaddr -e '
begin
address = IPAddr.new(ARGV.fetch(0))
valid = address.ipv4? && (address.loopback? || address.private?)
rescue IPAddr::InvalidAddressError
valid = false
end
exit(valid ? 0 : 1)
' "$probe_host"; then
echo "OPENCODE_PROBE_HOST must be a loopback or private IPv4 address" >&2
exit 2
fi
if [[ ! "$expected_gem_commit" =~ ^[0-9a-f]{40}$ ]]; then container_name="opencode-compat-${RANDOM}-$$"
echo "OPENCODE_RUBY_COMMIT must be a full lowercase 40-character Git commit" >&2 llm_container_name="opencode-compat-llm-${RANDOM}-$$"
exit 2 network_name="opencode-compat-net-${RANDOM}-$$"
fi python_image="python@sha256:399babc8b49529dabfd9c922f2b5eea81d611e4512e3ed250d75bd2e7683f4b0"
if ! gem_commit="$(git -C "$gem_path" rev-parse --verify 'HEAD^{commit}' 2>/dev/null)"; then
echo "OPENCODE_RUBY_PATH must be a readable Git checkout" >&2 cleanup() {
exit 2 status=$?
fi if [[ "$status" != "0" ]]; then
if [[ "$gem_commit" != "$expected_gem_commit" ]]; then echo "OpenCode compatibility probe failed; container log follows" >&2
echo "opencode-ruby checkout HEAD $gem_commit does not match expected commit $expected_gem_commit" >&2 docker logs "$container_name" >&2 2>/dev/null || true
exit 2 echo "Deterministic model request summary follows" >&2
fi docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/stats >&2 2>/dev/null || true
if [[ -n "$(git -C "$gem_path" status --porcelain --untracked-files=all)" ]]; then echo >&2
echo "opencode-ruby checkout must be clean before certification" >&2
exit 2
fi fi
docker rm -f "$container_name" >/dev/null 2>&1 || true
docker rm -f "$llm_container_name" >/dev/null 2>&1 || true
docker network rm "$network_name" >/dev/null 2>&1 || true
return "$status"
}
trap cleanup EXIT
docker network create "$network_name" >/dev/null docker network create "$network_name" >/dev/null
network_created=1
docker run --detach \ docker run --detach \
--name "$llm_container_name" \ --name "$llm_container_name" \
--network "$network_name" \ --network "$network_name" \
--network-alias compat-llm \ --network-alias compat-llm \
--volume "$repo_root/scripts:/compat:ro" \
"$python_image" \ "$python_image" \
python -c 'import time; time.sleep(3600)' \ python /compat/fake_llm.py --port 8080 --port-file /tmp/compat-port \
>/dev/null >/dev/null
llm_container_started=1
docker cp "$repo_root/scripts/fake_llm.py" "$llm_container_name:/tmp/fake_llm.py"
docker exec --detach \
"$llm_container_name" \
python /tmp/fake_llm.py --port 8080 --port-file /tmp/compat-port
for _ in $(seq 1 100); do for _ in $(seq 1 100); do
if docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/health >/dev/null 2>&1; then if docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/health >/dev/null 2>&1; then
@@ -184,7 +84,7 @@ config_json="$(jq -cn --arg url "http://compat-llm:8080/v1" '{
docker run --detach \ docker run --detach \
--name "$container_name" \ --name "$container_name" \
--network "$network_name" \ --network "$network_name" \
--publish "${probe_host}::4096" \ --publish 127.0.0.1::4096 \
--env "OPENCODE_CONFIG_CONTENT=$config_json" \ --env "OPENCODE_CONFIG_CONTENT=$config_json" \
--env OPENCODE_DISABLE_AUTOUPDATE=1 \ --env OPENCODE_DISABLE_AUTOUPDATE=1 \
--env OPENCODE_DISABLE_AUTOCOMPACT=1 \ --env OPENCODE_DISABLE_AUTOCOMPACT=1 \
@@ -193,10 +93,9 @@ docker run --detach \
--env OPENCODE_PURE=1 \ --env OPENCODE_PURE=1 \
"$image" serve --hostname 0.0.0.0 --port 4096 \ "$image" serve --hostname 0.0.0.0 --port 4096 \
>/dev/null >/dev/null
opencode_container_started=1
host_port="$(docker port "$container_name" 4096/tcp | sed -E 's/.*:([0-9]+)$/\1/' | head -1)" host_port="$(docker port "$container_name" 4096/tcp | sed -E 's/.*:([0-9]+)$/\1/' | head -1)"
base_url="http://${probe_host}:${host_port}" base_url="http://127.0.0.1:${host_port}"
ready=0 ready=0
for _ in $(seq 1 120); do for _ in $(seq 1 120); do
@@ -219,75 +118,22 @@ fi
server_version="$(docker exec "$container_name" opencode --version)" server_version="$(docker exec "$container_name" opencode --version)"
image_id="$(docker inspect "$container_name" --format '{{.Image}}')" image_id="$(docker inspect "$container_name" --format '{{.Image}}')"
image_os="$(docker image inspect "$image_id" --format '{{.Os}}')" gem_commit="$(git -C "$gem_path" rev-parse HEAD 2>/dev/null || true)"
image_architecture="$(docker image inspect "$image_id" --format '{{.Architecture}}')"
image_platform="${image_os}/${image_architecture}"
expected_version="${OPENCODE_EXPECTED_VERSION:-}"
if [[ -n "$expected_version" && "$server_version" != "$expected_version" ]]; then
echo "OpenCode reported version $server_version; expected $expected_version" >&2
exit 1
fi
live_contract="$(
OPENCODE_BASE_URL="$base_url" \ OPENCODE_BASE_URL="$base_url" \
OPENCODE_RUBY_PATH="$gem_path" \ OPENCODE_RUBY_PATH="$gem_path" \
OPENCODE_RUBY_COMMIT="$gem_commit" \ OPENCODE_RUBY_COMMIT="$gem_commit" \
ruby "$repo_root/ruby/live_probe.rb" ruby "$repo_root/ruby/live_probe.rb"
)"
printf '%s\n' "$live_contract"
llm_stats="$(docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/stats)" llm_stats="$(docker exec "$llm_container_name" wget -qO- http://127.0.0.1:8080/stats)"
request_count="$(jq -r '.request_count' <<<"$llm_stats")" request_count="$(jq -r '.request_count' <<<"$llm_stats")"
ruby "$repo_root/ruby/exact_live_contract.rb" "$request_count" ruby "$repo_root/ruby/exact_live_contract.rb" "$request_count"
evidence="$(jq -cn \ jq -cn \
--argjson schema_version 1 \
--arg status pass \ --arg status pass \
--arg kind shared-client-image-contract \
--arg matrix_target "${OPENCODE_MATRIX_ID:-local}" \
--arg certification_scope "shared-client-contract-only" \
--argjson executed_profiles '["ruby-rest-sse"]' \
--argjson required_consumer_profiles "${OPENCODE_REQUIRED_CONSUMER_PROFILES:-[]}" \
--arg image "$image" \ --arg image "$image" \
--arg image_id "$image_id" \ --arg image_id "$image_id" \
--arg image_platform "$image_platform" \
--arg server_version "$server_version" \ --arg server_version "$server_version" \
--arg gem_commit "$gem_commit" \ --arg gem_commit "$gem_commit" \
--arg companion_rails_commit "${OPENCODE_RAILS_COMMIT:-}" \
--arg run_id "${OPENCODE_COMPAT_RUN_ID:-}" \
--arg run_attempt "${OPENCODE_COMPAT_RUN_ATTEMPT:-}" \
--arg head_sha "${OPENCODE_COMPAT_HEAD_SHA:-}" \
--arg repository "${OPENCODE_COMPAT_REPOSITORY:-}" \
--arg run_url "${OPENCODE_COMPAT_RUN_URL:-}" \
--argjson live_contract "$live_contract" \
--argjson llm_request_count "$request_count" \ --argjson llm_request_count "$request_count" \
'{ '{status:$status,image:$image,image_id:$image_id,server_version:$server_version,opencode_ruby_commit:$gem_commit,llm_request_count:$llm_request_count}'
schema_version:$schema_version,
kind:$kind,
status:$status,
checked_at:$live_contract.checked_at,
matrix_target:$matrix_target,
certification_scope:$certification_scope,
executed_profiles:$executed_profiles,
required_consumer_profiles:$required_consumer_profiles,
image:{requested:$image,docker_image_id:$image_id,platform:$image_platform,reported_version:$server_version},
clients:{
opencode_ruby:{version:$live_contract.opencode_ruby_version,commit:$gem_commit,executed:true},
opencode_rails:{commit:$companion_rails_commit,executed:false}
},
contract:{
expected_text:$live_contract.expected_text,
full_text:$live_contract.full_text,
observed_part_count:$live_contract.observed_part_count,
authoritative_assistant_message_count:$live_contract.authoritative_assistant_message_count,
llm_request_count:$llm_request_count
},
workflow:{run_id:$run_id,run_attempt:$run_attempt,head_sha:$head_sha,repository:$repository,run_url:$run_url}
}'
)"
printf '%s\n' "$evidence"
if [[ -n "$evidence_path" ]]; then
printf '%s\n' "$evidence" >"$evidence_path"
fi

View File

@@ -1,44 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
evidence_dir="${OPENCODE_COMPAT_EVIDENCE_DIR:-$repo_root/.compat-evidence}"
matrix_json="$(ruby "$repo_root/scripts/matrix_json.rb")"
entry_count="$(jq -er '.include | length' <<<"$matrix_json")"
if [[ ! "$entry_count" =~ ^[1-9][0-9]*$ ]]; then
echo "exact image matrix must contain at least one entry" >&2
exit 2
fi
mkdir -p "$evidence_dir"
for ((index = 0; index < entry_count; index++)); do
entry="$(jq -ec --argjson index "$index" '.include[$index]' <<<"$matrix_json")"
matrix_id="$(jq -er '.id' <<<"$entry")"
image="$(jq -er '.image' <<<"$entry")"
expected_version="$(jq -er '.version' <<<"$entry")"
required_profiles="$(jq -ec '.required_consumer_profiles' <<<"$entry")"
evidence_path="$evidence_dir/$matrix_id.json"
printf 'Running exact image matrix entry %s (%s)\n' "$matrix_id" "$image"
OPENCODE_IMAGE="$image" \
OPENCODE_EXPECTED_VERSION="$expected_version" \
OPENCODE_MATRIX_ID="$matrix_id" \
OPENCODE_REQUIRED_CONSUMER_PROFILES="$required_profiles" \
OPENCODE_COMPAT_EVIDENCE_PATH="$evidence_path" \
bundle exec "$repo_root/scripts/run_image_contract.sh"
jq -e \
--arg matrix_id "$matrix_id" \
--arg image "$image" \
--arg version "$expected_version" \
'.status == "pass" and
.matrix_target == $matrix_id and
.image.requested == $image and
.image.reported_version == $version' \
"$evidence_path" >/dev/null
printf 'Exact image matrix entry %s passed; JSON remains transient on this forge.\n' "$matrix_id"
done
printf 'Full exact image matrix passed (%s entries).\n' "$entry_count"

View File

@@ -1,21 +0,0 @@
# frozen_string_literal: true
tag = ARGV.fetch(0)
digest = ARGV.fetch(1)
disambiguator = ARGV[2]
abort "release tag must be vMAJOR.MINOR.PATCH" unless tag.match?(/\Av\d+\.\d+\.\d+\z/)
abort "digest must be an exact sha256" unless digest.match?(/\Asha256:[0-9a-f]{64}\z/)
branch = "compat/upstream-#{tag.delete_prefix('v')}-#{digest.delete_prefix('sha256:')}"
if disambiguator
unless disambiguator.match?(/\A[A-Za-z0-9][A-Za-z0-9-]*\z/)
abort "disambiguator must contain only letters, digits, and hyphens"
end
branch = "#{branch}-r#{disambiguator}"
end
abort "generated branch is longer than 128 characters" if branch.length > 128
puts branch

View File

@@ -1,114 +0,0 @@
# frozen_string_literal: true
require "fileutils"
require "json"
require "minitest/autorun"
require "open3"
require "rbconfig"
require "tmpdir"
require_relative "../lib/opencode_compat/client_candidate"
class ClientCandidateTest < Minitest::Test
ROOT = File.expand_path("..", __dir__)
RUBY_COMMIT = "9277646a4bb2cf25a8384ffc140b154f49ea5766"
RAILS_COMMIT = "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f"
def setup
@document = JSON.parse(File.binread(File.join(ROOT, "manifests/client-candidate.json")))
end
def test_accepts_exact_unpublished_commit_candidate_and_emits_safe_outputs
outputs = OpenCodeCompat::ClientCandidate.new(@document).github_outputs
assert_equal "unpublished", outputs.fetch("publication_state")
assert_equal "commit", outputs.fetch("ruby_provenance_kind")
assert_equal RUBY_COMMIT, outputs.fetch("ruby_ref")
assert_equal "", outputs.fetch("ruby_tag")
assert_equal "commit", outputs.fetch("rails_provenance_kind")
assert_equal RAILS_COMMIT, outputs.fetch("rails_ref")
assert_equal "", outputs.fetch("rails_tag_object")
end
def test_unpublished_candidate_rejects_mixed_or_mismatched_provenance
@document.dig("clients", "opencode-ruby", "provenance")["tag"] = "v0.0.1.alpha8"
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/commit provenance keys/, error.message)
@document.dig("clients", "opencode-ruby", "provenance").delete("tag")
@document.dig("clients", "opencode-ruby", "provenance")["commit"] = "f" * 40
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/provenance commit/, error.message)
end
def test_published_candidate_requires_annotated_tag_provenance_for_both_clients
publish_document!
candidate.verify!
@document.dig("clients", "opencode-ruby")["provenance"] = {
"kind" => "commit",
"commit" => RUBY_COMMIT
}
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/provenance kind must equal "annotated-tag"/, error.message)
end
def test_published_candidate_rejects_wrong_tag_object_shape_or_peel
publish_document!
ruby_provenance = @document.dig("clients", "opencode-ruby", "provenance")
ruby_provenance["annotated_tag_object"] = "short"
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/annotated tag object must be a full lowercase 40-character/, error.message)
ruby_provenance["annotated_tag_object"] = "3" * 40
ruby_provenance["peeled_commit"] = "f" * 40
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/peeled commit/, error.message)
end
def test_rejects_non_lockstep_runtime_dependency
dependency = @document.dig("clients", "opencode-rails", "runtime_dependencies", "opencode-ruby")
dependency["requirement"] = "~> 0.0.1.alpha8"
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/runtime dependency requirement/, error.message)
dependency["requirement"] = "= 0.0.1.alpha8"
dependency["ref"] = "f" * 40
error = assert_raises(OpenCodeCompat::ClientCandidate::ValidationError) { candidate.verify! }
assert_match(/runtime dependency ref/, error.message)
end
def test_output_script_validates_the_repository_manifest
output, error, status = Open3.capture3(
RbConfig.ruby,
File.join(ROOT, "scripts/client_candidate_outputs.rb")
)
assert status.success?, error
assert_includes output.lines, "ruby_ref=#{RUBY_COMMIT}\n"
assert_includes output.lines, "rails_ref=#{RAILS_COMMIT}\n"
assert_includes output.lines, "publication_state=unpublished\n"
end
private
def candidate
OpenCodeCompat::ClientCandidate.new(@document)
end
def publish_document!
@document["publication_state"] = "published"
{
"opencode-ruby" => "3" * 40,
"opencode-rails" => "4" * 40
}.each do |name, tag_object|
client = @document.fetch("clients").fetch(name)
client["provenance"] = {
"kind" => "annotated-tag",
"tag" => "v#{client.fetch('version')}",
"annotated_tag_object" => tag_object,
"peeled_commit" => client.fetch("ref")
}
end
end
end

View File

@@ -26,14 +26,4 @@ class ExactLiveContractTest < Minitest::Test
end end
end end
end end
def test_accepts_only_one_authoritative_assistant_message
assert_nil OpenCodeCompat::ExactLiveContract.assert_authoritative_assistant_count!(1)
[0, 2, "not-a-number"].each do |count|
assert_raises(OpenCodeCompat::ExactLiveContract::ContractError) do
OpenCodeCompat::ExactLiveContract.assert_authoritative_assistant_count!(count)
end
end
end
end end

View File

@@ -1,198 +0,0 @@
# frozen_string_literal: true
require "fileutils"
require "json"
require "minitest/autorun"
require "open3"
require "rbconfig"
require "tmpdir"
class ImageContractEvidenceTest < Minitest::Test
ROOT = File.expand_path("..", __dir__)
RUNNER = File.join(ROOT, "scripts/run_image_contract.sh")
GATEWAY_RESOLVER = File.join(ROOT, "scripts/private_default_gateway.rb")
VALID_IMAGE = "ghcr.io/anomalyco/opencode@sha256:#{'a' * 64}"
def setup
@tmp = Dir.mktmpdir("opencode-image-evidence")
@bin = File.join(@tmp, "bin")
@docker_marker = File.join(@tmp, "docker-called")
FileUtils.mkdir_p(@bin)
docker = File.join(@bin, "docker")
File.write(docker, <<~SH)
#!/usr/bin/env bash
: >"$DOCKER_CALLED_MARKER"
exit 99
SH
FileUtils.chmod(0o755, docker)
end
def teardown
FileUtils.remove_entry(@tmp)
end
def test_invalid_input_overwrites_stale_pass_without_touching_docker
evidence_path = File.join(@tmp, "evidence.json")
File.write(evidence_path, JSON.generate("status" => "pass"))
_output, _error, status = run_contract(
"OPENCODE_IMAGE" => "latest",
"OPENCODE_RUBY_PATH" => ROOT,
"OPENCODE_COMPAT_EVIDENCE_PATH" => evidence_path
)
assert_equal 2, status.exitstatus
refute_path_exists @docker_marker
evidence = JSON.parse(File.read(evidence_path))
assert_equal "fail", evidence.fetch("status")
assert_equal 2, evidence.dig("failure", "exit_status")
assert_equal "latest", evidence.dig("image", "requested")
assert_equal [], evidence.fetch("executed_profiles")
end
def test_rejects_truncated_registry_and_local_image_ids_before_docker
{
"ghcr.io/anomalyco/opencode@sha256:#{'a' * 12}" => {},
"sha256:#{'b' * 12}" => {"ALLOW_EXACT_IMAGE_ID" => "1"}
}.each_with_index do |(image, extra_environment), index|
evidence_path = File.join(@tmp, "truncated-#{index}.json")
_output, error, status = run_contract(
{
"OPENCODE_IMAGE" => image,
"OPENCODE_RUBY_PATH" => ROOT,
"OPENCODE_COMPAT_EVIDENCE_PATH" => evidence_path
}.merge(extra_environment)
)
assert_equal 2, status.exitstatus
assert_match(/must be an OCI digest/, error)
assert_equal image, JSON.parse(File.read(evidence_path)).dig("image", "requested")
end
refute_path_exists @docker_marker
end
def test_rejects_a_checkout_at_a_different_commit_before_docker
checkout, _commit = git_checkout
evidence_path = File.join(@tmp, "mismatch.json")
_output, error, status = run_contract(
base_environment(checkout, "f" * 40, evidence_path)
)
assert_equal 2, status.exitstatus
assert_match(/does not match expected commit/, error)
assert_failed_without_docker(evidence_path)
end
def test_rejects_a_dirty_checkout_before_docker
checkout, commit = git_checkout
File.write(File.join(checkout, "dirty.txt"), "not certified\n")
evidence_path = File.join(@tmp, "dirty.json")
_output, error, status = run_contract(base_environment(checkout, commit, evidence_path))
assert_equal 2, status.exitstatus
assert_match(/checkout must be clean/, error)
assert_failed_without_docker(evidence_path)
end
def test_rejects_a_non_git_path_before_docker
checkout = File.join(@tmp, "not-a-checkout")
FileUtils.mkdir_p(checkout)
evidence_path = File.join(@tmp, "not-git.json")
_output, error, status = run_contract(
base_environment(checkout, "f" * 40, evidence_path)
)
assert_equal 2, status.exitstatus
assert_match(/readable Git checkout/, error)
assert_failed_without_docker(evidence_path)
end
def test_rejects_malformed_ipv6_and_public_probe_hosts_before_docker
checkout, commit = git_checkout
["not-an-address", "::1", "8.8.8.8"].each_with_index do |probe_host, index|
evidence_path = File.join(@tmp, "probe-host-#{index}.json")
environment = base_environment(checkout, commit, evidence_path).merge(
"OPENCODE_PROBE_HOST" => probe_host
)
_output, error, status = run_contract(environment)
assert_equal 2, status.exitstatus
assert_match(/loopback or private IPv4 address/, error)
assert_failed_without_docker(evidence_path)
end
end
def test_resolves_a_private_default_gateway_from_linux_route_hex
route_path = File.join(@tmp, "route")
File.write(route_path, <<~ROUTES)
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 010011AC 0003 0 0 10 00000000 0 0 0
ROUTES
output, error, status = Open3.capture3(RbConfig.ruby, GATEWAY_RESOLVER, route_path)
assert status.success?, error
assert_equal "172.17.0.1\n", output
end
def test_gateway_resolver_rejects_a_public_default_gateway
route_path = File.join(@tmp, "public-route")
File.write(route_path, <<~ROUTES)
Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 08080808 0003 0 0 10 00000000 0 0 0
ROUTES
_output, error, status = Open3.capture3(RbConfig.ruby, GATEWAY_RESOLVER, route_path)
refute status.success?
assert_match(/no private IPv4 default-route gateway found/, error)
end
private
def base_environment(checkout, commit, evidence_path)
{
"OPENCODE_IMAGE" => VALID_IMAGE,
"OPENCODE_RUBY_PATH" => checkout,
"OPENCODE_RUBY_COMMIT" => commit,
"OPENCODE_COMPAT_EVIDENCE_PATH" => evidence_path
}
end
def run_contract(environment)
env = {
"PATH" => "#{@bin}:#{ENV.fetch('PATH')}",
"DOCKER_CALLED_MARKER" => @docker_marker
}.merge(environment)
Open3.capture3(env, "bash", RUNNER)
end
def git_checkout
path = File.join(@tmp, "checkout-#{rand(1_000_000)}")
FileUtils.mkdir_p(path)
run_git!(path, "init", "--quiet")
run_git!(path, "config", "user.name", "Compatibility Test")
run_git!(path, "config", "user.email", "compat@example.test")
File.write(File.join(path, "tracked.txt"), "clean\n")
run_git!(path, "add", "tracked.txt")
run_git!(path, "commit", "--quiet", "-m", "fixture")
commit = run_git!(path, "rev-parse", "HEAD").strip
[path, commit]
end
def run_git!(path, *arguments)
output, error, status = Open3.capture3("git", "-C", path, *arguments)
assert status.success?, error
output
end
def assert_failed_without_docker(evidence_path)
refute_path_exists @docker_marker
assert_equal "fail", JSON.parse(File.read(evidence_path)).fetch("status")
end
end

View File

@@ -1,415 +0,0 @@
# frozen_string_literal: true
require "minitest/autorun"
require "rbconfig"
require "stringio"
require "tmpdir"
require_relative "../ruby/lockstep_client_contract"
module LockstepClientContractTestSources
class Git
attr_reader :ref, :revision, :uri
def initialize(revision, ref: revision, uri: OpenCodeCompat::LockstepClientContract::OPENCODE_RUBY_GIT_URI)
@revision = revision
@ref = ref
@uri = uri
end
end
class Path; end
end
class LockstepClientContractTest < Minitest::Test
RUBY_COMMIT = "1" * 40
RAILS_COMMIT = "2" * 40
RUBY_TAG_OBJECT = "3" * 40
RAILS_TAG_OBJECT = "4" * 40
RUBY_TAG = "v0.0.1.alpha7"
RAILS_TAG = "v0.0.1.alpha7"
VERSION = "0.0.1.alpha7"
FakeSpec = Struct.new(:name, :version, :runtime_dependencies, :source, :full_gem_path, keyword_init: true)
def setup
@root = Dir.mktmpdir("opencode-lockstep-contract")
@ruby_path = File.join(@root, "opencode-ruby")
@rails_path = File.join(@root, "opencode-rails")
Dir.mkdir(@ruby_path)
Dir.mkdir(@rails_path)
@env = {
"OPENCODE_CLIENT_PUBLICATION_STATE" => "published",
"OPENCODE_RUBY_PATH" => @ruby_path,
"OPENCODE_RUBY_COMMIT" => RUBY_COMMIT,
"OPENCODE_RUBY_PROVENANCE_KIND" => "annotated-tag",
"OPENCODE_RUBY_TAG" => RUBY_TAG,
"OPENCODE_RUBY_TAG_OBJECT" => RUBY_TAG_OBJECT,
"OPENCODE_RUBY_VERSION" => VERSION,
"OPENCODE_RAILS_PATH" => @rails_path,
"OPENCODE_RAILS_COMMIT" => RAILS_COMMIT,
"OPENCODE_RAILS_PROVENANCE_KIND" => "annotated-tag",
"OPENCODE_RAILS_TAG" => RAILS_TAG,
"OPENCODE_RAILS_TAG_OBJECT" => RAILS_TAG_OBJECT,
"OPENCODE_RAILS_VERSION" => VERSION
}
@heads = {@ruby_path => RUBY_COMMIT, @rails_path => RAILS_COMMIT}
@git_results = {}
seed_annotated_tag(@ruby_path, RUBY_TAG, RUBY_TAG_OBJECT, RUBY_COMMIT)
seed_annotated_tag(@rails_path, RAILS_TAG, RAILS_TAG_OBJECT, RAILS_COMMIT)
@ruby_spec = FakeSpec.new(name: "opencode-ruby", version: Gem::Version.new(VERSION))
@rails_spec = FakeSpec.new(
name: "opencode-rails",
version: Gem::Version.new(VERSION),
runtime_dependencies: [Gem::Dependency.new("opencode-ruby", "= #{VERSION}")],
full_gem_path: @rails_path
)
@bundle_ruby_spec = FakeSpec.new(
name: "opencode-ruby",
source: LockstepClientContractTestSources::Git.new(RUBY_COMMIT)
)
end
def teardown
FileUtils.remove_entry(@root)
end
def test_verifies_and_emits_canonical_lockstep_evidence
evidence_path = File.join(@root, "evidence", "lockstep.json")
@env["OPENCODE_COMPAT_EVIDENCE_PATH"] = evidence_path
output = StringIO.new
document = contract.emit(io: output)
expected_json = OpenCodeCompat::LockstepClientContract.canonical_json(document)
assert_equal "#{expected_json}\n", output.string
assert_equal "#{expected_json}\n", File.binread(evidence_path)
assert_equal "pass", document.fetch("status")
assert_equal 2, document.fetch("schema_version")
assert_equal "published", document.fetch("publication_state")
assert_equal RUBY_COMMIT, document.dig("opencode_ruby", "checkout_commit")
assert_equal RUBY_COMMIT, document.dig("opencode_ruby", "bundler_git_ref")
assert_equal RUBY_COMMIT, document.dig("opencode_ruby", "bundler_git_revision")
assert_equal OpenCodeCompat::LockstepClientContract::OPENCODE_RUBY_GIT_URI,
document.dig("opencode_ruby", "bundler_git_uri")
assert_equal "annotated-tag", document.dig("opencode_ruby", "source_provenance", "kind")
assert_equal RUBY_TAG_OBJECT, document.dig("opencode_ruby", "source_provenance", "annotated_tag_object")
assert_equal RUBY_COMMIT, document.dig("opencode_ruby", "source_provenance", "peeled_commit")
assert_equal RUBY_TAG, document.dig("opencode_ruby", "source_provenance", "tag")
assert_equal RAILS_COMMIT, document.dig("opencode_rails", "checkout_commit")
assert_equal RAILS_TAG_OBJECT, document.dig("opencode_rails", "source_provenance", "annotated_tag_object")
assert_equal RAILS_COMMIT, document.dig("opencode_rails", "source_provenance", "peeled_commit")
assert_equal "= #{VERSION}", document.dig("opencode_rails", "runtime_dependency", "requirement")
assert_equal RUBY_VERSION, document.fetch("ruby_runtime_version")
assert_nil document.dig("workflow", "run_id")
assert_equal expected_json, JSON.generate(JSON.parse(expected_json).sort.to_h)
end
def test_accepts_unpublished_commit_only_provenance_without_tag_lookup
use_commit_only_provenance!
document = contract.verify
assert_equal "unpublished", document.fetch("publication_state")
assert_equal({"commit" => RUBY_COMMIT, "kind" => "commit"},
document.dig("opencode_ruby", "source_provenance"))
assert_equal({"commit" => RAILS_COMMIT, "kind" => "commit"},
document.dig("opencode_rails", "source_provenance"))
end
def test_publication_state_cannot_weaken_published_tag_binding
@env["OPENCODE_RUBY_PROVENANCE_KIND"] = "commit"
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/published candidate/, error.message)
use_commit_only_provenance!
@env["OPENCODE_RAILS_PROVENANCE_KIND"] = "annotated-tag"
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/unpublished candidate/, error.message)
end
def test_commit_only_provenance_rejects_residual_tag_coordinates
use_commit_only_provenance!
@env["OPENCODE_RUBY_TAG"] = RUBY_TAG
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/must not supply OPENCODE_RUBY_TAG/, error.message)
end
def test_commit_only_provenance_stops_when_the_release_tag_appears
use_commit_only_provenance!
set_git_result(
@ruby_path,
["rev-parse", "--verify", "--quiet", "refs/tags/#{RUBY_TAG}"],
RUBY_TAG_OBJECT
)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/release tag #{Regexp.escape(RUBY_TAG)} already exists/, error.message)
assert_match(/publication_state must be published/, error.message)
end
def test_commit_only_provenance_does_not_treat_git_errors_as_an_absent_tag
use_commit_only_provenance!
@git_results[[@ruby_path, ["rev-parse", "--verify", "--quiet", "refs/tags/#{RUBY_TAG}"]]] =
OpenCodeCompat::LockstepClientContract::GitCommandError.new("corrupt ref", exitstatus: 128)
resolver = lambda do |path, *arguments|
result = resolve_git(path, *arguments)
raise result if result.is_a?(Exception)
result
end
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) do
contract(git_resolver: resolver).verify
end
assert_match(/could not check opencode-ruby release tag: corrupt ref/, error.message)
end
def test_rejects_checkout_commit_mismatch_for_either_client
{
@ruby_path => "opencode-ruby checkout commit",
@rails_path => "opencode-rails checkout commit"
}.each do |path, message|
original = @heads.fetch(path)
@heads[path] = "f" * 40
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match message, error.message
ensure
@heads[path] = original
end
end
def test_rejects_non_annotated_tag_object_for_either_client
{
@ruby_path => RUBY_TAG_OBJECT,
@rails_path => RAILS_TAG_OBJECT
}.each do |path, object|
set_git_result(path, ["cat-file", "-t", object], "commit")
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/annotated tag object type must equal "tag"/, error.message)
ensure
set_git_result(path, ["cat-file", "-t", object], "tag")
end
end
def test_rejects_tampered_annotated_tag_object_coordinate
tampered_object = "f" * 40
@env["OPENCODE_RUBY_TAG_OBJECT"] = tampered_object
set_git_result(@ruby_path, ["cat-file", "-t", tampered_object], "tag")
set_git_result(@ruby_path, ["rev-parse", "--verify", "#{tampered_object}^{commit}"], RUBY_COMMIT)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/opencode-ruby local tag ref object/, error.message)
assert_match(/#{RUBY_TAG_OBJECT}/, error.message)
end
def test_rejects_local_tag_ref_resolving_to_another_object
set_git_result(
@rails_path,
["rev-parse", "--verify", "refs/tags/#{RAILS_TAG}"],
"e" * 40
)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/opencode-rails local tag ref object/, error.message)
end
def test_rejects_wrong_object_or_ref_peel
object_peel_key = ["rev-parse", "--verify", "#{RUBY_TAG_OBJECT}^{commit}"]
set_git_result(@ruby_path, object_peel_key, "e" * 40)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/opencode-ruby annotated tag peeled commit/, error.message)
set_git_result(@ruby_path, object_peel_key, RUBY_COMMIT)
set_git_result(
@ruby_path,
["rev-parse", "--verify", "refs/tags/#{RUBY_TAG}^{commit}"],
"e" * 40
)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/opencode-ruby local tag ref peeled commit/, error.message)
end
def test_rejects_missing_local_tag
@git_results.delete([@ruby_path, ["rev-parse", "--verify", "refs/tags/#{RUBY_TAG}"]])
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/could not resolve opencode-ruby local tag ref/, error.message)
end
def test_rejects_loaded_or_gem_version_drift
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) do
contract(ruby_version: "0.0.1.alpha8").verify
end
assert_match(/loaded Opencode::VERSION/, error.message)
@rails_spec.version = Gem::Version.new("0.0.1.alpha8")
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/loaded opencode-rails gem version/, error.message)
end
def test_rejects_non_lockstep_expected_versions
@env["OPENCODE_RAILS_VERSION"] = "0.0.1.alpha8"
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) do
contract(rails_version: "0.0.1.alpha8").verify
end
assert_match(/lockstep client version/, error.message)
end
def test_rejects_non_exact_or_wrong_rails_runtime_dependency
["~> #{VERSION}", "= 0.0.1.alpha6"].each do |requirement|
@rails_spec.runtime_dependencies = [Gem::Dependency.new("opencode-ruby", requirement)]
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/must require opencode-ruby = #{Regexp.escape(VERSION)}/, error.message)
end
end
def test_rejects_missing_or_duplicate_rails_runtime_dependency
@rails_spec.runtime_dependencies = []
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/exactly one runtime dependency/, error.message)
@rails_spec.runtime_dependencies = [
Gem::Dependency.new("opencode-ruby", "= #{VERSION}"),
Gem::Dependency.new("opencode-ruby", "= #{VERSION}")
]
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/exactly one runtime dependency/, error.message)
end
def test_rejects_non_git_or_mismatched_bundler_source
@bundle_ruby_spec.source = LockstepClientContractTestSources::Path.new
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/must be a Git source/, error.message)
@bundle_ruby_spec.source = LockstepClientContractTestSources::Git.new("f" * 40)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/Bundler opencode-ruby revision/, error.message)
@bundle_ruby_spec.source = LockstepClientContractTestSources::Git.new(RUBY_COMMIT, ref: "main")
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/Bundler opencode-ruby ref/, error.message)
@bundle_ruby_spec.source = LockstepClientContractTestSources::Git.new(
RUBY_COMMIT,
uri: "https://example.test/opencode-ruby.git"
)
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/Bundler opencode-ruby URI/, error.message)
end
def test_rejects_loaded_rails_gem_from_another_checkout
other_path = File.join(@root, "other-opencode-rails")
Dir.mkdir(other_path)
@rails_spec.full_gem_path = other_path
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/loaded opencode-rails checkout/, error.message)
end
def test_requires_full_exact_candidate_coordinates
@env.delete("OPENCODE_RUBY_COMMIT")
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/missing required environment: OPENCODE_RUBY_COMMIT/, error.message)
@env["OPENCODE_RUBY_COMMIT"] = "main"
error = assert_raises(OpenCodeCompat::LockstepClientContract::ContractError) { contract.verify }
assert_match(/full lowercase 40-character Git commit/, error.message)
end
def test_cli_failure_writes_failure_evidence
stub_path = File.join(@root, "cli-stubs")
evidence_path = File.join(@root, "cli-evidence", "lockstep.json")
FileUtils.mkdir_p(stub_path)
File.binwrite(File.join(stub_path, "bundler.rb"), <<~RUBY)
module Bundler
LoadedDefinition = Struct.new(:specs)
def self.load
LoadedDefinition.new([])
end
end
RUBY
File.binwrite(File.join(stub_path, "opencode-rails.rb"), <<~RUBY)
module Opencode
VERSION = #{VERSION.inspect}
RAILS_VERSION = #{VERSION.inspect}
end
RUBY
_stdout, stderr, status = Open3.capture3(
{"OPENCODE_COMPAT_EVIDENCE_PATH" => evidence_path},
RbConfig.ruby,
"-I",
stub_path,
File.expand_path("../ruby/lockstep_client_contract.rb", __dir__),
unsetenv_others: true
)
refute status.success?
assert_equal 1, status.exitstatus
failure = JSON.parse(File.binread(evidence_path))
assert_equal "opencode-client-lockstep", failure.fetch("contract")
assert_equal 2, failure.fetch("schema_version")
assert_equal "fail", failure.fetch("status")
assert_match(/missing required environment/, failure.fetch("error"))
assert_equal failure, JSON.parse(stderr)
end
private
def contract(ruby_version: VERSION, rails_version: VERSION, git_resolver: method(:resolve_git))
OpenCodeCompat::LockstepClientContract.new(
env: @env,
git_resolver: git_resolver,
loaded_specs: {
"opencode-ruby" => @ruby_spec,
"opencode-rails" => @rails_spec
},
bundle_specs: [@bundle_ruby_spec],
ruby_version: ruby_version,
rails_version: rails_version
)
end
def resolve_git(path, *arguments)
return @heads.fetch(path) if arguments == ["rev-parse", "--verify", "HEAD^{commit}"]
@git_results.fetch([path, arguments])
end
def seed_annotated_tag(path, tag, object, commit)
set_git_result(path, ["cat-file", "-t", object], "tag")
set_git_result(path, ["rev-parse", "--verify", "refs/tags/#{tag}"], object)
set_git_result(path, ["rev-parse", "--verify", "#{object}^{commit}"], commit)
set_git_result(path, ["rev-parse", "--verify", "refs/tags/#{tag}^{commit}"], commit)
end
def set_git_result(path, arguments, result)
@git_results[[path, arguments]] = result
end
def use_commit_only_provenance!
@env["OPENCODE_CLIENT_PUBLICATION_STATE"] = "unpublished"
@env["OPENCODE_RUBY_PROVENANCE_KIND"] = "commit"
@env["OPENCODE_RAILS_PROVENANCE_KIND"] = "commit"
@env.delete("OPENCODE_RUBY_TAG")
@env.delete("OPENCODE_RUBY_TAG_OBJECT")
@env.delete("OPENCODE_RAILS_TAG")
@env.delete("OPENCODE_RAILS_TAG_OBJECT")
end
end

View File

@@ -1,97 +0,0 @@
# frozen_string_literal: true
require "fileutils"
require "json"
require "minitest/autorun"
require "open3"
require "rbconfig"
require "tmpdir"
class MatrixJsonTest < Minitest::Test
ROOT = File.expand_path("..", __dir__)
def setup
@tmp = Dir.mktmpdir("opencode-matrix-json")
FileUtils.mkdir_p(File.join(@tmp, "lib/opencode_compat"))
FileUtils.mkdir_p(File.join(@tmp, "scripts"))
FileUtils.mkdir_p(File.join(@tmp, "manifests"))
FileUtils.cp(File.join(ROOT, "scripts/matrix_json.rb"), File.join(@tmp, "scripts"))
FileUtils.cp(
File.join(ROOT, "lib/opencode_compat/client_candidate.rb"),
File.join(@tmp, "lib/opencode_compat")
)
FileUtils.cp(
File.join(ROOT, "manifests/client-candidate.json"),
File.join(@tmp, "manifests")
)
@manifest = JSON.parse(File.read(File.join(ROOT, "manifests/image-matrix.json")))
end
def teardown
FileUtils.remove_entry(@tmp)
end
def test_emits_only_the_actual_shared_client_contract_profile
output, error, status = run_matrix(@manifest)
assert status.success?, error
JSON.parse(output).fetch("include").each do |target|
assert_equal ["ruby-rest-sse"], target.fetch("profiles")
assert_equal "shared-client-contract-only", target.fetch("certification_scope")
end
end
def test_rejects_a_manifest_that_overstates_the_executed_profile
@manifest.fetch("public_ci").first["profiles"] = ["rails-persisted-turn"]
_output, error, status = run_matrix(@manifest)
refute status.success?
assert_match(/executes exactly ruby-rest-sse/, error)
end
def test_rejects_a_manifest_that_overstates_the_certification_scope
@manifest.fetch("public_ci").first["certification_scope"] = "consumer-runtime"
_output, error, status = run_matrix(@manifest)
refute status.success?
assert_match(/certification_scope must be shared-client-contract-only/, error)
end
def test_rejects_a_matrix_bound_to_another_client_candidate
@manifest.fetch("client_candidate")["opencode_ruby_commit"] = "f" * 40
_output, error, status = run_matrix(@manifest)
refute status.success?
assert_match(/must equal the exact client candidate/, error)
end
def test_allows_reviewed_candidate_certification_without_changing_coordinates
@manifest.fetch("client_candidate")["certification_status"] = "certified"
_output, error, status = run_matrix(@manifest)
assert status.success?, error
end
def test_rejects_unknown_candidate_certification_status
@manifest.fetch("client_candidate")["certification_status"] = "deployed"
_output, error, status = run_matrix(@manifest)
refute status.success?
assert_match(/certification_status must be pending or certified/, error)
end
private
def run_matrix(manifest)
File.write(
File.join(@tmp, "manifests/image-matrix.json"),
JSON.pretty_generate(manifest) + "\n"
)
Open3.capture3(RbConfig.ruby, File.join(@tmp, "scripts/matrix_json.rb"))
end
end

View File

@@ -1,9 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
require "json" require "json"
require "digest"
require "minitest/autorun" require "minitest/autorun"
require_relative "../lib/opencode_compat/client_candidate"
require_relative "../lib/opencode_compat/runtime_tuple_promoter" require_relative "../lib/opencode_compat/runtime_tuple_promoter"
class RepositoryTest < Minitest::Test class RepositoryTest < Minitest::Test
@@ -14,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
@@ -38,136 +36,42 @@ 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
manifest = json("manifests/image-matrix.json") targets = json("manifests/image-matrix.json").fetch("public_ci")
assert_equal 2, manifest.fetch("schema_version")
targets = manifest.fetch("public_ci")
refute_empty targets refute_empty targets
targets.each do |target| targets.each do |target|
assert_match %r{\Aghcr\.io/anomalyco/opencode@sha256:[0-9a-f]{64}\z}, target.fetch("image") assert_match %r{\Aghcr\.io/anomalyco/opencode@sha256:[0-9a-f]{64}\z}, target.fetch("image")
refute_includes target.fetch("image"), ":latest" refute_includes target.fetch("image"), ":latest"
assert_equal ["ruby-rest-sse"], target.fetch("profiles") refute_empty target.fetch("profiles")
assert_equal "shared-client-contract-only", next unless target.fetch("certification_status") == "certified"
target.fetch("certification_scope", "shared-client-contract-only")
assert_equal "certified", target.fetch("certification_status")
current = target.fetch("current_certification")
assert_equal "certified", current.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("client_commit"))
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("rails_commit"))
assert_equal current.fetch("expected_text"), current.fetch("full_text")
assert_equal 1, current.fetch("llm_request_count")
assert_path_exists File.join(ROOT, current.fetch("evidence"))
previous = target["previous_certification"]
next unless previous
assert_equal "certified", previous.fetch("status") assert_match(/\A[0-9a-f]{40}\z/, target.fetch("certified_client_commit"))
assert_match(/\A[0-9a-f]{40}\z/, previous.fetch("client_commit")) assert_equal target.fetch("expected_text"), target.fetch("full_text")
assert_equal previous.fetch("expected_text"), previous.fetch("full_text") assert_equal 1, target.fetch("llm_request_count")
assert_equal 1, previous.fetch("llm_request_count")
end end
end end
def test_candidate_client_train_is_lockstep_and_bound_to_unpublished_commits def test_candidate_is_bound_to_an_annotated_tag
candidate = json("manifests/client-candidate.json") candidate = json("manifests/client-candidate.json")
OpenCodeCompat::ClientCandidate.new(candidate).verify! provenance = candidate.fetch("tag_provenance")
clients = candidate.fetch("clients")
release_train = candidate.fetch("release_train")
assert_equal 3, candidate.fetch("schema_version") assert_match(/\A[0-9a-f]{40}\z/, candidate.fetch("ref"))
assert_equal "candidate", candidate.fetch("status") assert_match(/\Av\d+\.\d+\.\d+\.alpha\d+\z/, provenance.fetch("tag"))
assert_equal "unpublished", candidate.fetch("publication_state") assert_match(/\A[0-9a-f]{40}\z/, provenance.fetch("annotated_tag_object"))
assert_equal "0.0.1.alpha8", release_train assert_equal candidate.fetch("ref"), provenance.fetch("peeled_commit")
assert_equal %w[opencode-rails opencode-ruby], clients.keys.sort
clients.each do |name, client|
provenance = client.fetch("provenance")
assert_equal "ajaynomics/#{name}", client.fetch("repository")
assert_equal release_train, client.fetch("version")
assert_match(/\A[0-9a-f]{40}\z/, client.fetch("ref"))
assert_equal "commit", provenance.fetch("kind")
assert_equal client.fetch("ref"), provenance.fetch("commit")
refute provenance.key?("tag")
end
assert_equal "9277646a4bb2cf25a8384ffc140b154f49ea5766", clients.dig("opencode-ruby", "ref")
assert_equal "a9add2a7c1dd3eb978aa8b4ebf9ef7e111d1057f", clients.dig("opencode-rails", "ref")
dependency = clients.fetch("opencode-rails").fetch("runtime_dependencies").fetch("opencode-ruby")
assert_equal "= #{release_train}", dependency.fetch("requirement")
assert_equal clients.fetch("opencode-ruby").fetch("ref"), dependency.fetch("ref")
end
def test_image_matrix_is_certified_and_bound_to_the_exact_unpublished_client_candidate
candidate = json("manifests/client-candidate.json")
clients = candidate.fetch("clients")
matrix_candidate = json("manifests/image-matrix.json").fetch("client_candidate")
assert_equal "certified", matrix_candidate.fetch("certification_status")
assert_equal "unpublished", matrix_candidate.fetch("publication_state")
assert_path_exists File.join(ROOT, matrix_candidate.fetch("evidence"))
assert_equal candidate.fetch("release_train"), matrix_candidate.fetch("release_train")
assert_equal candidate.fetch("publication_state"), matrix_candidate.fetch("publication_state")
assert_equal clients.dig("opencode-ruby", "ref"), matrix_candidate.fetch("opencode_ruby_commit")
assert_equal clients.dig("opencode-rails", "ref"), matrix_candidate.fetch("opencode_rails_commit")
json("manifests/image-matrix.json").fetch("host_canary").each do |target|
assert_equal "certified", target.fetch("certification_status")
current = target.fetch("current_certification")
assert_equal "certified", current.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, current.fetch("client_commit"))
assert_path_exists File.join(ROOT, current.fetch("evidence"))
previous = target.fetch("previous_certification")
assert_equal "certified", previous.fetch("status")
assert_match(/\A[0-9a-f]{40}\z/, previous.fetch("client_commit"))
assert_path_exists File.join(ROOT, previous.fetch("evidence"))
end
end
def test_alpha8_local_image_evidence_is_review_input_not_app_certification
evidence = json("evidence/2026-07-20-opencode-alpha8-pre-release-shared-client.json")
candidate = json("manifests/client-candidate.json").fetch("clients")
assert_equal "not-certified", evidence.fetch("certification_status")
assert_equal "shared-client-contract-only", evidence.fetch("coverage_scope")
assert_equal "unpublished", evidence.fetch("publication_state")
assert_equal candidate.dig("opencode-ruby", "ref"), evidence.dig("clients", "opencode_ruby", "commit")
assert_equal candidate.dig("opencode-rails", "ref"), evidence.dig("clients", "opencode_rails", "commit")
assert_equal true, evidence.dig("clients", "opencode_ruby", "executed")
assert_equal false, evidence.dig("clients", "opencode_rails", "executed_by_image_contract")
assert_equal 5, evidence.fetch("checks").length
evidence.fetch("checks").each do |check|
assert_equal "pass", check.fetch("status")
assert_equal ["ruby-rest-sse"], check.fetch("executed_profiles")
assert_equal check.fetch("expected_text"), check.fetch("full_text")
assert_equal 1, check.fetch("authoritative_assistant_message_count")
assert_equal 1, check.fetch("llm_request_count")
end
assert evidence.fetch("limitations").any? { |entry| entry.include?("remain unverified") }
end
def test_alpha8_ci_evidence_certifies_shared_and_lockstep_scopes_without_claiming_apps
evidence = json("evidence/2026-07-20-opencode-alpha8-shared-client-ci.json")
candidate = json("manifests/client-candidate.json").fetch("clients")
assert_equal "pass", evidence.fetch("status")
assert_equal "unpublished", evidence.fetch("publication_state")
assert_equal candidate.dig("opencode-ruby", "ref"), evidence.dig("clients", "opencode_ruby", "commit")
assert_equal candidate.dig("opencode-rails", "ref"), evidence.dig("clients", "opencode_rails", "commit")
assert_equal "29725021192", evidence.dig("github_workflow", "run_id")
assert_equal "2717", evidence.dig("gitea_workflow", "run_id")
assert_equal false, evidence.dig("gitea_workflow", "artifact_evidence_claimed")
assert_operator evidence.fetch("certified_at"), :>=, evidence.dig("github_workflow", "completed_at")
assert_operator evidence.fetch("certified_at"), :>=, evidence.dig("gitea_workflow", "completed_at")
assert_equal %w[3.2.11 3.3.12 3.4.10 4.0.6],
evidence.fetch("rails_lockstep").map { |entry| entry.fetch("ruby_runtime_version") }
assert evidence.fetch("rails_lockstep").all? { |entry| entry.fetch("status") == "pass" }
assert_equal 3, evidence.fetch("exact_image_contracts").length
evidence.fetch("exact_image_contracts").each do |contract|
assert_equal "pass", contract.fetch("status")
assert_equal contract.fetch("expected_text"), contract.fetch("full_text")
assert_equal 1, contract.fetch("authoritative_assistant_message_count")
assert_equal 1, contract.fetch("llm_request_count")
end
assert evidence.fetch("limitations").any? { |entry| entry.include?("application profile") }
end end
def test_certified_migration_keeps_previous_tuple def test_certified_migration_keeps_previous_tuple
@@ -187,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")
@@ -196,7 +104,7 @@ class RepositoryTest < Minitest::Test
def test_runtime_tuples_use_full_commits_and_no_mutable_registry_coordinate def test_runtime_tuples_use_full_commits_and_no_mutable_registry_coordinate
tuples = json("manifests/runtime-tuples.json") tuples = json("manifests/runtime-tuples.json")
tuples.fetch("consumers").each_value do |consumer| tuples.fetch("consumers").each_value do |consumer|
%w[current candidate previous emergency_provenance].each do |slot| %w[current candidate previous].each do |slot|
tuple = consumer[slot] tuple = consumer[slot]
next unless tuple next unless tuple
@@ -217,183 +125,137 @@ class RepositoryTest < Minitest::Test
end end
end end
def test_failed_alpha2_baselines_remain_uncertified_emergency_provenance def test_failed_alpha2_baselines_block_promotion_instead_of_becoming_previous
tuples = json("manifests/runtime-tuples.json") tuples = json("manifests/runtime-tuples.json")
readiness = tuples.fetch("promotion_readiness") readiness = tuples.fetch("promotion_readiness")
assert_equal "certified", tuples.fetch("migration_state") assert_equal "blocked", readiness.fetch("status")
assert_equal "certified", readiness.fetch("status") assert_path_exists File.join(ROOT, readiness.fetch("evidence"))
tuples.fetch("consumers").each_value do |consumer| tuples.fetch("consumers").each_value do |consumer|
emergency = consumer.fetch("emergency_provenance") assert_equal "observed-production-contract-failed", consumer.dig("current", "status")
assert_equal "observed-production-contract-failed", emergency.fetch("status") assert_nil consumer.fetch("previous")
refute emergency.key?("certification")
assert_equal "certified", consumer.dig("current", "status")
assert_equal "0.0.1.alpha8", consumer.dig("current", "opencode_ruby", "version")
assert_equal "certified", consumer.dig("previous", "status")
assert_equal "0.0.1.alpha7", consumer.dig("previous", "opencode_ruby", "version")
refute consumer.key?("rollback_state")
end end
end end
def test_platform_one_click_targets_are_explicitly_uncertified_and_separate def test_pull_request_heads_are_explicitly_candidate_only
consumers = json("manifests/runtime-tuples.json").fetch("consumers") tuples = json("manifests/runtime-tuples.json")
%w[travelwolf mushu].each do |name| tuples.fetch("consumers").each do |name, consumer|
rollback = consumers.fetch(name).fetch("immediate_platform_rollback") candidate = consumer.fetch("candidate")
assert_match(/\Auncertified-/, rollback.fetch("status")) reference = candidate.fetch("consumer_ref")
assert_match(/\A[0-9a-f]{40}\z/, rollback.fetch("consumer_commit"))
assert_match(/\A[0-9a-f]{40}\z/, rollback.dig("opencode_ruby", "git_commit")) assert_equal "pre-merge-pr-head-candidate-only", candidate.fetch("certification_scope"), name
refute rollback.key?("certification") assert_equal false, candidate.fetch("promotion_eligible"), name
refute_equal consumers.dig(name, "previous", "consumer_commit"), rollback.fetch("consumer_commit") 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 end
travelwolf_runtime = consumers.dig("travelwolf", "immediate_platform_rollback", "runtime") def test_candidate_gems_use_peeled_refs_and_loaded_source_proof
assert_match(/@sha256:[0-9a-f]{64}\z/, travelwolf_runtime.fetch("image")) tuples = json("manifests/runtime-tuples.json")
assert_match(/\Asha256:[0-9a-f]{64}\z/, travelwolf_runtime.fetch("application_image_id"))
mushu_runtime = consumers.dig("mushu", "immediate_platform_rollback", "runtime") tuples.fetch("consumers").each do |consumer_name, consumer|
assert_match(/@sha256:[0-9a-f]{64}\z/, mushu_runtime.fetch("registry_ref")) candidate = consumer.fetch("candidate")
assert_nil mushu_runtime.fetch("application_image") %w[opencode_ruby opencode_rails].each do |client_name|
assert_equal "not-retained", mushu_runtime.fetch("application_image_provenance") 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 end
def test_certified_tuple_evidence_is_bound_to_complete_tuple_fingerprints 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) promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
tuples = json("manifests/runtime-tuples.json")
json("manifests/runtime-tuples.json").fetch("consumers").each do |consumer, entry| tuples.fetch("consumers").each do |name, consumer|
profile = entry.fetch("profile") error = assert_raises(OpenCodeCompat::PromotionError) do
%w[current previous].each do |slot| promoter.promote(
tuple = entry.fetch(slot) consumer: name,
fingerprint = promoter.send(:tuple_fingerprint, tuple, consumer: consumer, profile: profile) consumer_commit: consumer.dig("candidate", "consumer_commit"),
certification = tuple.fetch("certification") certification: {},
promoter.send( dry_run: true
:validate_recorded_certification!, )
tuple, 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
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: ROOT)
evidence_paths = {
"ajent-rails" => "evidence/2026-07-18-ajent-rails-alpha7-candidate.json",
"travelwolf" => "evidence/2026-07-18-travelwolf-alpha7-candidate.json",
"mushu" => "evidence/2026-07-18-mushu-alpha7-candidate.json"
}
evidence_paths.each do |consumer, path|
evidence = json(path)
fingerprints = promoter.fingerprints(
consumer: consumer, consumer: consumer,
profile: profile, consumer_commit: evidence.fetch("consumer_commit")
expected_fingerprint: fingerprint,
label: "#{consumer} #{slot}"
) )
assert_equal "certified", tuple.fetch("status")
assert_equal "pass", certification.fetch("status")
assert_equal fingerprint, certification.fetch("tuple_sha256")
refute_empty certification.fetch("evidence")
certification.fetch("evidence").each do |reference|
assert_equal %w[path sha256], reference.keys.sort
path = reference.fetch("path")
bytes = File.binread(File.join(ROOT, path))
assert_equal "sha256:#{Digest::SHA256.hexdigest(bytes)}", reference.fetch("sha256")
evidence = json(path)
assert_equal consumer, evidence.fetch("consumer") assert_equal consumer, evidence.fetch("consumer")
assert_equal profile, evidence.fetch("profile") assert_equal fingerprints.fetch("profile"), evidence.fetch("profile")
assert_equal tuple.fetch("consumer_commit"), evidence.fetch("consumer_commit") assert_equal fingerprints.fetch("candidate_tuple_sha256"), evidence.fetch("tuple_sha256")
assert_equal certification.fetch("certified_at"), evidence.fetch("certified_at") assert_equal "pre-merge-pr-head-candidate-only", evidence.fetch("certification_scope")
assert_equal fingerprint, evidence.fetch("tuple_sha256") assert_equal false, evidence.fetch("promotion_eligible")
assert_equal "pass", evidence.fetch("status") assert_equal "pass", evidence.fetch("status")
end end
end end
end
end
def test_watcher_has_no_deployment_commands def test_watcher_has_no_deployment_commands
workflow = File.read(File.join(ROOT, ".github/workflows/watch-upstream.yml")) workflow = File.read(File.join(ROOT, ".github/workflows/watch-upstream.yml"))
refute_match(/\b(kamal|kubectl|helm|nomad|docker\s+service)\b/i, workflow) refute_match(/\b(kamal|kubectl|helm|nomad|docker\s+service)\b/i, workflow)
end end
def test_workflow_actions_are_pinned_to_exact_commits
workflows = Dir.glob(File.join(ROOT, ".github/workflows/*.{yml,yaml}"))
refute_empty workflows
workflows.each do |path|
File.foreach(path).with_index(1) do |line, number|
next unless (match = line.match(/\buses:\s+[^\s@]+@([^\s#]+)/))
assert_match(/\A[0-9a-f]{40}\z/, match[1], "#{path}:#{number} must pin an exact action commit")
end
end
end
def test_workflow_actions_use_reviewed_node24_compatible_pins
expected = {
"actions/checkout" => "9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0",
"actions/upload-artifact" => "043fb46d1a93c77aae656e7c1c64a875d1fc6a0a",
"ruby/setup-ruby" => "003a5c4d8d6321bd302e38f6f0ec593f77f06600"
}
observed = Hash.new { |hash, key| hash[key] = [] }
Dir.glob(File.join(ROOT, ".github/workflows/*.{yml,yaml}")).each do |path|
File.read(path).scan(/\buses:\s+([^\s@]+)@([0-9a-f]{40})/) do |action, commit|
observed[action] << commit if expected.key?(action)
end
end
expected.each do |action, commit|
refute_empty observed.fetch(action), "expected at least one #{action} use"
assert_equal [commit], observed.fetch(action).uniq, "#{action} must stay on the reviewed Node 24 pin"
end
end
def test_candidate_workflow_verifies_and_preserves_the_lockstep_client_tuple
workflow = File.read(File.join(ROOT, ".github/workflows/candidate.yml"))
refute_includes workflow, "inputs.opencode_ruby_ref"
refute_includes workflow, "inputs.opencode_rails_ref"
assert_includes workflow, "repository: ajaynomics/opencode-ruby"
assert_includes workflow, "repository: ajaynomics/opencode-rails"
assert_includes workflow, "path: opencode-rails"
assert_includes workflow, "ruby: [\"3.2\", \"3.3\", \"3.4\", \"4.0\"]"
assert_includes workflow, "scripts/client_candidate_outputs.rb"
assert_includes workflow, "OPENCODE_CLIENT_PUBLICATION_STATE"
assert_includes workflow, "OPENCODE_RUBY_PROVENANCE_KIND"
assert_includes workflow, "OPENCODE_RAILS_PROVENANCE_KIND"
assert_includes workflow, "ruby/lockstep_client_contract.rb"
assert_includes workflow, "OPENCODE_RUBY_TAG_OBJECT"
assert_includes workflow, "OPENCODE_RAILS_TAG_OBJECT"
assert_operator workflow.scan("fetch-tags: true").length, :>=, 2
assert_operator workflow.scan("actions/upload-artifact@").length, :>=, 3
assert_includes workflow, "OPENCODE_COMPAT_EVIDENCE_PATH"
assert_includes workflow, "OPENCODE_REQUIRED_CONSUMER_PROFILES"
assert_includes workflow, 'BUNDLE_PATH: ${{ runner.temp }}/opencode-ruby-bundle'
assert_includes workflow, "Install candidate dependencies outside the checkout"
refute_includes workflow, "OPENCODE_MATRIX_PROFILES"
refute_includes workflow, "OPENCODE_CERTIFICATION_SCOPE"
refute_match(/\b(kamal|kubectl|helm|nomad|docker\s+service|gh\s+pr\s+merge)\b/i, workflow)
end
def test_candidate_workflow_has_an_explicit_dual_forge_evidence_boundary
workflow = File.read(File.join(ROOT, ".github/workflows/candidate.yml"))
upload_conditions = workflow.scan(
/- name: Upload (?:fixture|lockstep|exact-image) evidence\n\s+if: ([^\n]+)\n\s+uses: actions\/upload-artifact@/
).flatten
assert_equal 3, upload_conditions.length
assert_equal ["always() && github.server_url == 'https://github.com'"], upload_conditions.uniq
assert_includes workflow, "bundler-cache: ${{ github.server_url == 'https://github.com' }}"
assert_includes workflow, "exact-image-contract-gitea:"
assert_includes workflow, "if: github.server_url != 'https://github.com'"
assert_includes workflow, "run: scripts/run_image_matrix_contract.sh"
assert_includes workflow, 'probe_host="$(ruby scripts/private_default_gateway.rb)"'
assert_includes workflow, 'echo "OPENCODE_PROBE_HOST=$probe_host" >> "$GITHUB_ENV"'
assert_operator workflow.scan('BUNDLE_GEMFILE: ${{ github.workspace }}/ruby-client/Gemfile').length, :>=, 2
assert_operator workflow.scan("generated JSON is transient and is not review evidence").length, :>=, 3
gitea_job = workflow.split(/^ exact-image-contract-gitea:\n/, 2).fetch(1)
refute_includes gitea_job, "actions/upload-artifact@"
end
def test_gitea_matrix_runner_uses_every_generated_entry_without_hardcoded_coordinates
runner = File.read(File.join(ROOT, "scripts/run_image_matrix_contract.sh"))
assert_includes runner, 'matrix_json="$(ruby "$repo_root/scripts/matrix_json.rb")"'
assert_includes runner, 'for ((index = 0; index < entry_count; index++))'
assert_includes runner, 'bundle exec "$repo_root/scripts/run_image_contract.sh"'
assert_includes runner, '.image.reported_version == $version'
refute_match(/upstream-[0-9]/, runner)
refute_match(/ghcr\.io\/anomalyco/, runner)
end
def test_tuple_promotion_has_no_command_execution_or_deployment_client def test_tuple_promotion_has_no_command_execution_or_deployment_client
paths = %w[ paths = %w[
lib/opencode_compat/runtime_tuple_promoter.rb lib/opencode_compat/runtime_tuple_promoter.rb
@@ -411,16 +273,8 @@ class RepositoryTest < Minitest::Test
runner = File.read(File.join(ROOT, "scripts/run_image_contract.sh")) runner = File.read(File.join(ROOT, "scripts/run_image_contract.sh"))
assert_includes probe, "ExactLiveContract.assert_final_text!" assert_includes probe, "ExactLiveContract.assert_final_text!"
assert_includes probe, "ExactLiveContract.assert_authoritative_assistant_count!"
refute_includes probe, "full_text.include?" refute_includes probe, "full_text.include?"
assert_includes runner, "exact_live_contract.rb" assert_includes runner, "exact_live_contract.rb"
assert_includes runner, 'docker cp "$repo_root/scripts/fake_llm.py"'
refute_includes runner, '--volume "$repo_root/scripts:/compat:ro"'
assert_includes runner, '--publish "${probe_host}::4096"'
assert_includes runner, 'base_url="http://${probe_host}:${host_port}"'
refute_includes runner, "--publish 0.0.0.0"
assert_includes runner, "OPENCODE_COMPAT_EVIDENCE_PATH"
assert_includes runner, "OPENCODE_EXPECTED_VERSION"
refute_match(/request_count.*-lt\s+1/, runner) refute_match(/request_count.*-lt\s+1/, runner)
end end
end end

View File

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

View File

@@ -1,238 +0,0 @@
# frozen_string_literal: true
require "fileutils"
require "json"
require "minitest/autorun"
require "open3"
require "rbconfig"
require "tmpdir"
class WatcherTest < Minitest::Test
ROOT = File.expand_path("..", __dir__)
RUBY = RbConfig.ruby
def setup
@tmp = Dir.mktmpdir("opencode-watcher")
FileUtils.mkdir_p(File.join(@tmp, "scripts"))
FileUtils.mkdir_p(File.join(@tmp, "manifests"))
FileUtils.cp(File.join(ROOT, "scripts/record_upstream_candidate.rb"), File.join(@tmp, "scripts"))
%w[upstream.json image-matrix.json].each do |name|
FileUtils.cp(File.join(ROOT, "manifests", name), File.join(@tmp, "manifests"))
end
end
def teardown
FileUtils.remove_entry(@tmp)
end
def test_same_tag_and_digest_is_an_exact_noop
upstream = read_json("manifests/upstream.json")
before = watched_bytes
run_recorder!(
upstream.fetch("release_tag"),
upstream.fetch("published_at"),
upstream.fetch("release_url"),
upstream.fetch("image").split("@").last
)
assert_equal before, watched_bytes
end
def test_same_tag_and_digest_repairs_a_missing_public_target_then_noops
upstream = read_json("manifests/upstream.json")
matrix = read_json("manifests/image-matrix.json")
image = upstream.fetch("image")
matrix.fetch("public_ci").reject! { |target| target.fetch("image") == image }
write_json("manifests/image-matrix.json", matrix)
upstream_before = File.binread(File.join(@tmp, "manifests/upstream.json"))
arguments = [
upstream.fetch("release_tag"),
upstream.fetch("published_at"),
upstream.fetch("release_url"),
image.split("@").last
]
run_recorder!(*arguments)
repaired = read_json("manifests/image-matrix.json")
targets = repaired.fetch("public_ci").select { |target| target.fetch("image") == image }
assert_equal 1, targets.length
assert_equal "pending", targets.first.fetch("certification_status")
assert_equal ["ruby-rest-sse"], targets.first.fetch("profiles")
assert_equal upstream_before, File.binread(File.join(@tmp, "manifests/upstream.json"))
after = watched_bytes
run_recorder!(*arguments)
assert_equal after, watched_bytes
end
def test_duplicate_public_targets_are_rejected_without_writing_either_manifest
upstream = read_json("manifests/upstream.json")
matrix = read_json("manifests/image-matrix.json")
target = matrix.fetch("public_ci").find { |entry| entry.fetch("image") == upstream.fetch("image") }
duplicate = target.merge("id" => "#{target.fetch('id')}-duplicate")
matrix.fetch("public_ci") << duplicate
write_json("manifests/image-matrix.json", matrix)
before = watched_bytes
_output, error, status = capture_recorder(
upstream.fetch("release_tag"),
upstream.fetch("published_at"),
upstream.fetch("release_url"),
upstream.fetch("image").split("@").last
)
refute status.success?
assert_match(/must appear exactly once in public_ci; found 2/, error)
assert_equal before, watched_bytes
end
def test_missing_public_target_with_conflicting_generated_id_is_rejected_without_writing
upstream = read_json("manifests/upstream.json")
matrix = read_json("manifests/image-matrix.json")
image = upstream.fetch("image")
removed = matrix.fetch("public_ci").find { |target| target.fetch("image") == image }
matrix.fetch("public_ci").reject! { |target| target.fetch("image") == image }
matrix.fetch("public_ci") << removed.merge(
"id" => "upstream-#{upstream.fetch('version')}-#{image.split('sha256:').last}",
"image" => "ghcr.io/anomalyco/opencode@sha256:#{'e' * 64}"
)
write_json("manifests/image-matrix.json", matrix)
before = watched_bytes
_output, error, status = capture_recorder(
upstream.fetch("release_tag"),
upstream.fetch("published_at"),
upstream.fetch("release_url"),
image.split("@").last
)
refute status.success?
assert_match(/public_ci id .* is already used by another image/, error)
assert_equal before, watched_bytes
end
def test_same_tag_with_new_digest_records_a_distinct_pending_target_once
upstream = read_json("manifests/upstream.json")
replacement = "sha256:#{'f' * 64}"
arguments = [
upstream.fetch("release_tag"),
upstream.fetch("published_at"),
upstream.fetch("release_url"),
replacement
]
run_recorder!(*arguments)
changed_upstream = read_json("manifests/upstream.json")
matrix = read_json("manifests/image-matrix.json")
target = matrix.fetch("public_ci").last
assert_equal "ghcr.io/anomalyco/opencode@#{replacement}", changed_upstream.fetch("image")
expected_version = upstream.fetch("release_tag").delete_prefix("v")
assert_equal "upstream-#{expected_version}-#{'f' * 64}", target.fetch("id")
assert_equal "ghcr.io/anomalyco/opencode@#{replacement}", target.fetch("image")
assert_equal "pending", target.fetch("certification_status")
assert_equal "shared-client-contract-only", target.fetch("certification_scope")
assert_empty target.fetch("consumers")
assert_empty target.fetch("required_consumer_profiles")
after = watched_bytes
run_recorder!(*arguments)
assert_equal after, watched_bytes
end
def test_recorder_rejects_untrusted_release_metadata
upstream = read_json("manifests/upstream.json")
digest = upstream.fetch("image").split("@").last
refute recorder_status("branch-name", upstream.fetch("published_at"), upstream.fetch("release_url"), digest).success?
refute recorder_status(upstream.fetch("release_tag"), "yesterday", upstream.fetch("release_url"), digest).success?
refute recorder_status(upstream.fetch("release_tag"), upstream.fetch("published_at"), "https://example.test/release", digest).success?
refute recorder_status(upstream.fetch("release_tag"), upstream.fetch("published_at"), upstream.fetch("release_url"), "sha256:nope").success?
end
def test_branch_names_bind_the_full_tag_and_digest_and_support_retries
script = File.join(ROOT, "scripts/upstream_branch_name.rb")
digest = "sha256:#{'a' * 64}"
replacement = "sha256:#{'b' * 64}"
base = run_script!(script, "v1.18.3", digest).strip
changed = run_script!(script, "v1.18.3", replacement).strip
retry_branch = run_script!(script, "v1.18.3", digest, "123-2").strip
assert_equal "compat/upstream-1.18.3-#{'a' * 64}", base
refute_equal base, changed
assert_equal "#{base}-r123-2", retry_branch
refute run_status(script, "v1.18.3", digest, "bad/value").success?
end
def test_workflow_can_only_update_manifests_and_open_a_pr
workflow = File.read(File.join(ROOT, ".github/workflows/watch-upstream.yml"))
assert_match(
/\n open-compatibility-pr:\n if: github\.server_url == 'https:\/\/github\.com'\n runs-on:/,
workflow
)
assert_includes workflow, "contents: write"
assert_includes workflow, "pull-requests: write"
assert_includes workflow, "git add manifests/image-matrix.json manifests/upstream.json"
assert_includes workflow, "gh pr create"
assert_includes workflow, "scripts/upstream_branch_name.rb"
assert_includes workflow, "git ls-remote --exit-code --heads"
assert_includes workflow, "--base main"
assert_includes workflow, ".baseRefName == \"main\""
assert_includes workflow, "isCrossRepository == false"
assert_includes workflow, "headRefOid"
assert_includes workflow, "application/vnd.github.raw+json"
assert_includes workflow, "current_image_count"
assert_includes workflow, '[.public_ci[]? | select(.image == $image)] | length'
assert_includes workflow, '[ "$current_image_count" -eq 1 ]'
assert_includes workflow, ".release_tag == $tag and .image == $image"
assert_includes workflow, "([.public_ci[]? | select(.image == $image)] | length) == 1"
content_check = workflow.index(".release_tag == $tag and .image == $image")
skip = workflow.index('echo "skip=true"')
assert_operator content_check, :<, skip
refute_match(/gh\s+pr\s+merge|repository_dispatch|workflow_dispatches|git\s+push\s+--force/i, workflow)
refute_match(/\b(kamal|kubectl|helm|nomad|docker\s+service)\b/i, workflow)
end
private
def run_recorder!(*arguments)
run_script!(File.join(@tmp, "scripts/record_upstream_candidate.rb"), *arguments)
end
def recorder_status(*arguments)
run_status(File.join(@tmp, "scripts/record_upstream_candidate.rb"), *arguments)
end
def capture_recorder(*arguments)
Open3.capture3(RUBY, File.join(@tmp, "scripts/record_upstream_candidate.rb"), *arguments)
end
def run_script!(script, *arguments)
output, error, status = Open3.capture3(RUBY, script, *arguments)
assert status.success?, error
output
end
def run_status(script, *arguments)
_output, _error, status = Open3.capture3(RUBY, script, *arguments)
status
end
def read_json(relative_path)
JSON.parse(File.read(File.join(@tmp, relative_path)))
end
def write_json(relative_path, value)
File.write(File.join(@tmp, relative_path), JSON.pretty_generate(value) + "\n")
end
def watched_bytes
%w[manifests/upstream.json manifests/image-matrix.json].map do |relative_path|
File.binread(File.join(@tmp, relative_path))
end
end
end