From 5d6743d33487233f1115e8239c2ff3623f6cfa43 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 10:28:55 -0400 Subject: [PATCH 1/6] Add Codex native review normalizer --- .../codex-native-reviewer/v1/normalize.jq | 215 ++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 adapters/codex-native-reviewer/v1/normalize.jq diff --git a/adapters/codex-native-reviewer/v1/normalize.jq b/adapters/codex-native-reviewer/v1/normalize.jq new file mode 100644 index 0000000..1a17e2f --- /dev/null +++ b/adapters/codex-native-reviewer/v1/normalize.jq @@ -0,0 +1,215 @@ +def exact_fields($required; $optional): + . as $value | + type == "object" and + ((keys_unsorted - ($required + $optional)) | length) == 0 and + all($required[]; . as $key | $value | has($key)); + +def id_ok: + type == "string" and test("\\A[a-z0-9][a-z0-9._:-]{0,127}\\z"); +def provider_id_ok: + type == "string" and test("\\A[1-9][0-9]{0,19}\\z"); +def finding_id_ok: + type == "string" and test("\\A[A-Za-z0-9][A-Za-z0-9._:-]{0,127}\\z"); +def sha256_ok: + type == "string" and test("\\A[0-9a-f]{64}\\z"); +def text_ok: + type == "string" and utf8bytelength >= 1 and utf8bytelength <= 8192; + +def time_ok: + type == "string" and + test("\\A[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z\\z") and + (capture("\\A(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})T(?[0-9]{2}):(?[0-9]{2}):(?[0-9]{2})Z\\z") as $parts | + ($parts.year | tonumber) as $year | + ($parts.month | tonumber) as $month | + ($parts.day | tonumber) as $day | + ($parts.hour | tonumber) as $hour | + ($parts.minute | tonumber) as $minute | + ($parts.second | tonumber) as $second | + ($year % 4 == 0 and ($year % 100 != 0 or $year % 400 == 0)) as $leap | + [31,(if $leap then 29 else 28 end),31,30,31,30,31,31,30,31,30,31] as $days | + $month >= 1 and $month <= 12 and + $day >= 1 and $day <= $days[$month - 1] and + $hour >= 0 and $hour <= 23 and + $minute >= 0 and $minute <= 59 and + $second >= 0 and $second <= 59); + +def revision_ok: + exact_fields(["repository_id","hash_algorithm","commit_id"];[]) and + (.repository_id | id_ok) and + (.hash_algorithm == "sha1" or .hash_algorithm == "sha256") and + (if .hash_algorithm == "sha1" + then (.commit_id | type == "string" and test("\\A[0-9a-f]{40}\\z")) + else (.commit_id | type == "string" and test("\\A[0-9a-f]{64}\\z")) + end); + +def content_ref_ok: + exact_fields(["content_id","media_type","sha256"];[]) and + (.content_id | id_ok) and + (.media_type | type == "string" and + test("\\A[a-z0-9][a-z0-9!#$&^_.+-]*/[a-z0-9][a-z0-9!#$&^_.+-]*\\z")) and + (.sha256 | sha256_ok); + +def trust_context_ok: + exact_fields( + ["expected_repository_id","expected_change_request_id","expected_review_id", + "expected_head","expected_base","expected_github_app_id","observation_time", + "instruction_ref","review_policy_ref","execution_boundary_id", + "invocation_kind"]; + []) and + (.expected_repository_id | provider_id_ok) and + (.expected_change_request_id | provider_id_ok) and + (.expected_review_id | provider_id_ok) and + (.expected_head | revision_ok) and + (.expected_base | revision_ok) and + .expected_head.repository_id == .expected_base.repository_id and + (.expected_github_app_id | provider_id_ok) and + (.observation_time | time_ok) and + (.instruction_ref | content_ref_ok) and + (.review_policy_ref | content_ref_ok) and + (.execution_boundary_id | id_ok) and + .invocation_kind == "native-review"; + +def path_ok: + type == "string" and length > 0 and utf8bytelength <= 4096 and + (test("[\\x{0000}-\\x{001f}\\x{007f}-\\x{009f}]") | not) and + (contains("\\") | not) and + (split("/") | all(.[]; . != "" and . != "." and . != "..")); + +def unavailable_ok: + exact_fields(["state","reason_id"];[]) and + .state == "unavailable" and (.reason_id | id_ok); + +def hidden_execution_ok: + exact_fields(["model","effort","tools","cost"];[]) and + all([.model,.effort,.tools,.cost][]; unavailable_ok); + +def top_finding_ok: + exact_fields(["finding_id","body","provider_severity","provider_metadata"];[]) and + (.finding_id | finding_id_ok) and + (.body | text_ok) and + (.provider_severity | text_ok) and + (.provider_metadata | type == "object"); + +def inline_finding_ok($head): + exact_fields( + ["finding_id","path","line","side","commit_id","body", + "provider_severity","provider_metadata"]; + []) and + (.finding_id | finding_id_ok) and + (.path | path_ok) and + (.line | type == "number" and . == floor and . >= 1 and . <= 2147483647) and + (.side == "LEFT" or .side == "RIGHT") and + .commit_id == $head.commit_id and + (.body | text_ok) and + (.provider_severity | text_ok) and + (.provider_metadata | type == "object"); + +def findings_ok: + . as $snapshot | + (.reported_top_level_count | type == "number" and + . == floor and . >= 0 and . <= 100000) and + (.reported_inline_count | type == "number" and + . == floor and . >= 0 and . <= 100000) and + (.top_level_findings | type == "array" and length <= 256 and + all(.[]; top_finding_ok) and + (map(.finding_id) as $ids | + $ids == ($ids | sort) and ($ids | length) == ($ids | unique | length))) and + (.inline_findings | type == "array" and length <= 256 and + all(.[]; inline_finding_ok($snapshot.head)) and + (map([.path,.line,.side,.finding_id]) as $keys | $keys == ($keys | sort))) and + ((.top_level_findings + .inline_findings) | map(.finding_id) | + length == (unique | length)) and + (if .complete then + (.top_level_findings | length) == .reported_top_level_count and + (.inline_findings | length) == .reported_inline_count + else + (.top_level_findings | length) <= .reported_top_level_count and + (.inline_findings | length) <= .reported_inline_count + end); + +def status_facts_ok: + if .status == "COMPLETED" then + (.terminal_at | time_ok) and .dismissed_at == null + elif .status == "DISMISSED" then + (.terminal_at | time_ok) and (.dismissed_at | time_ok) and + .terminal_at <= .dismissed_at + elif .status == "TIMED_OUT" or .status == "FAILED" then + (.terminal_at | time_ok) and .dismissed_at == null + elif .status == "IN_PROGRESS" or .status == "UNKNOWN" then + .terminal_at == null and .dismissed_at == null + else false + end; + +def timestamps_ok: + (.started_at | time_ok) and (.updated_at | time_ok) and (.observed_at | time_ok) and + .started_at <= .updated_at and .updated_at <= .observed_at and + (if .terminal_at == null then true else .terminal_at <= .updated_at end) and + (if .dismissed_at == null then true else .dismissed_at <= .updated_at end); + +def snapshot_ok: + exact_fields( + ["repository_id","change_request_id","review_id","head","base", + "github_app_id","observed_at","status","complete","started_at","updated_at", + "terminal_at","dismissed_at","reported_top_level_count", + "reported_inline_count","top_level_findings","inline_findings", + "hidden_execution","provider_metadata"]; + []) and + (.repository_id | provider_id_ok) and + (.change_request_id | provider_id_ok) and + (.review_id | provider_id_ok) and + (.head | revision_ok) and (.base | revision_ok) and + .head.repository_id == .base.repository_id and + (.github_app_id | provider_id_ok) and + (.observed_at | time_ok) and + (.status | IN("COMPLETED","DISMISSED","TIMED_OUT","FAILED","IN_PROGRESS","UNKNOWN")) and + (.complete | type == "boolean") and + (.hidden_execution | hidden_execution_ok) and + (.provider_metadata | type == "object") and + status_facts_ok and timestamps_ok and findings_ok; + +def stale_bindings($context; $snapshot): + [ + if $snapshot.github_app_id != $context.expected_github_app_id then "app" else empty end, + if $snapshot.base != $context.expected_base then "base" else empty end, + if $snapshot.change_request_id != $context.expected_change_request_id then "change-request" else empty end, + if $snapshot.head != $context.expected_head then "head" else empty end, + if $snapshot.observed_at != $context.observation_time then "observation-time" else empty end, + if $snapshot.repository_id != $context.expected_repository_id then "repository" else empty end, + if $snapshot.review_id != $context.expected_review_id then "review" else empty end + ]; + +def normalized_state($snapshot; $stale): + if ($stale | length) > 0 then ["stale","codex.review-binding-stale"] + elif $snapshot.status == "DISMISSED" then ["dismissed","codex.review-dismissed"] + elif $snapshot.status == "TIMED_OUT" then ["timeout","codex.review-timeout"] + elif $snapshot.status == "FAILED" then ["failed","codex.review-failed"] + elif $snapshot.complete == false then ["inconclusive","codex.review-incomplete"] + elif $snapshot.status != "COMPLETED" then ["inconclusive","codex.review-not-terminal"] + elif ($snapshot.reported_top_level_count + $snapshot.reported_inline_count) > 0 then + ["findings","codex.review-findings"] + else ["clean","codex.review-clean"] + end; + +if (exact_fields(["trust_context","snapshot"];[]) | not) then + error("codex-reviewer.invalid-envelope") +elif (.trust_context | trust_context_ok) == false then + error("codex-reviewer.invalid-trust-context") +elif (.snapshot | snapshot_ok) == false then + error("codex-reviewer.invalid-snapshot") +else + .trust_context as $context | + .snapshot as $snapshot | + stale_bindings($context;$snapshot) as $stale | + normalized_state($snapshot;$stale) as $normalized | + { + schema_version:1, + kind:"adapter_observation", + adapter:{id:"adapter.codex-native-reviewer.v1",version:"v1",status:"inactive"}, + state:$normalized[0],reason_id:$normalized[1],stale_bindings:$stale, + review_mode:"read-only", + trust_context:$context,observation:$snapshot, + authority:"none", + qualification:{state:"unavailable",reason_id:"adapter.unqualified"}, + effects:[] + } +end From 703e1a1ae701446f3ddda1fc44e2dafaf7626d8f Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 10:33:44 -0400 Subject: [PATCH 2/6] Test inactive Codex reviewer adapter package --- .../codex-native-reviewer/v1/manifest.json | 1 + ...ault-codex-native-reviewer-adapter.test.sh | 236 ++++++++++++++++++ 2 files changed, 237 insertions(+) create mode 100644 adapters/codex-native-reviewer/v1/manifest.json create mode 100755 scripts/test/default-codex-native-reviewer-adapter.test.sh diff --git a/adapters/codex-native-reviewer/v1/manifest.json b/adapters/codex-native-reviewer/v1/manifest.json new file mode 100644 index 0000000..159e0d8 --- /dev/null +++ b/adapters/codex-native-reviewer/v1/manifest.json @@ -0,0 +1 @@ +{"body":{"adapter_version":"v1","offered_capabilities":["core.review.change.v1"],"offered_execution_kinds":["model"],"offered_permissions":["core.perm.evidence.write.v1","core.perm.model.invoke.v1","core.perm.target.read.v1"],"offered_roles":["reviewer"],"offered_tools":[],"package_ref":{"location":{"kind":"path","value":"adapters/codex-native-reviewer/v1/normalize.jq"},"mode":"100644","object_id":"1a17e2f0b18f02be8f1c53d14c0264632f1dac2b","object_type":"blob","revision":{"commit_id":"5d6743d33487233f1115e8239c2ff3623f6cfa43","hash_algorithm":"sha1","repository_id":"ystack.source"}}},"id":"adapter.codex-native-reviewer.v1","kind":"adapter_manifest","schema_version":2} diff --git a/scripts/test/default-codex-native-reviewer-adapter.test.sh b/scripts/test/default-codex-native-reviewer-adapter.test.sh new file mode 100755 index 0000000..5c59244 --- /dev/null +++ b/scripts/test/default-codex-native-reviewer-adapter.test.sh @@ -0,0 +1,236 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2016 +set -euo pipefail +export LC_ALL=C + +root=$(CDPATH='' cd -P -- "${BASH_SOURCE[0]%/*}/../.." && pwd -P) +normalizer="$root/adapters/codex-native-reviewer/v1/normalize.jq" +manifest="$root/adapters/codex-native-reviewer/v1/manifest.json" +tmp=$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/ystack-codex-reviewer.XXXXXX") +trap '/bin/rm -rf -- "$tmp"' EXIT + +sha_file() { /usr/bin/shasum -a 256 "$1" | /usr/bin/awk '{print $1}'; } +fail() { /usr/bin/printf 'FAIL: %s\n' "$1" >&2; exit 1; } +passed=0 +pass() { passed=$((passed + 1)); /usr/bin/printf 'ok %s - %s\n' "$passed" "$1"; } + +platform=$(/usr/bin/uname -s):$(/usr/bin/uname -m) +case "$platform" in + Darwin:*) asset=jq-osx-amd64; digest=5c0a0a3ea600f302ee458b30317425dd9632d1ad8882259fcaf4e9b868b2b1ef ;; + Linux:x86_64) asset=jq-linux64; digest=af986793a515d500ab2d35f8d2aecd656e764504b789b66d7e1a0b727a124c44 ;; + *) fail "unsupported jq 1.6 proof platform: $platform" ;; +esac +jq_bin="${TMPDIR:-/tmp}/ystack-portable-core-jq16/$asset" +[ -f "$jq_bin" ] && [ "$(sha_file "$jq_bin")" = "$digest" ] || + fail 'verified jq 1.6 cache is required' +jq_command=("$jq_bin") +if [ "$platform" = Darwin:arm64 ]; then jq_command=(/usr/bin/arch -x86_64 "$jq_bin"); fi +[ "$("${jq_command[@]}" --version)" = jq-1.6 ] || fail 'jq version' +runtime_bin="$tmp/bin" +/bin/mkdir -m 700 "$runtime_bin" +/bin/ln -s "$jq_bin" "$runtime_bin/jq" + +check() { + local name=$1 + shift + "$@" >/dev/null 2>&1 || fail "$name" + pass "$name" +} + +mutate() { + local name=$1 filter=$2 + "${jq_command[@]}" -S -c "$filter" "$tmp/baseline.json" >"$tmp/$name.json" + "${jq_command[@]}" -e 'type == "object"' "$tmp/$name.json" >/dev/null || + fail "$name fixture" +} + +expect_state() { + local name=$1 filter=$2 expected=$3 + mutate "$name" "$filter" + "${jq_command[@]}" -S -c -f "$normalizer" "$tmp/$name.json" \ + >"$tmp/$name.out" 2>"$tmp/$name.err" || fail "$name" + [ ! -s "$tmp/$name.err" ] || fail "$name diagnostics" + "${jq_command[@]}" -e --arg state "$expected" \ + '.state == $state' "$tmp/$name.out" >/dev/null || fail "$name state" + pass "$name" +} + +expect_stale() { + local name=$1 filter=$2 selector=$3 + mutate "$name" "$filter" + "${jq_command[@]}" -S -c -f "$normalizer" "$tmp/$name.json" \ + >"$tmp/$name.out" 2>"$tmp/$name.err" || fail "$name" + [ ! -s "$tmp/$name.err" ] || fail "$name diagnostics" + "${jq_command[@]}" -e --arg selector "$selector" \ + '.state == "stale" and .stale_bindings == [$selector]' \ + "$tmp/$name.out" >/dev/null || fail "$name state" + pass "$name" +} + +expect_reject() { + local name=$1 filter=$2 + mutate "$name" "$filter" + if "${jq_command[@]}" -S -c -f "$normalizer" "$tmp/$name.json" \ + >"$tmp/$name.out" 2>"$tmp/$name.err"; then + fail "$name accepted" + fi + [ ! -s "$tmp/$name.out" ] && [ -s "$tmp/$name.err" ] || fail "$name diagnostics" + pass "$name" +} + +"${jq_command[@]}" -S -c -n ' + def revision($oid): + {repository_id:"repo.target",hash_algorithm:"sha1",commit_id:$oid}; + def content($id;$sha): + {content_id:$id,media_type:"application/json",sha256:$sha}; + def unavailable: {state:"unavailable",reason_id:"provider.hidden"}; + { + trust_context:{ + expected_repository_id:"1270665750",expected_change_request_id:"218", + expected_review_id:"300",expected_head:revision("1" * 40), + expected_base:revision("2" * 40),expected_github_app_id:"15368", + observation_time:"2026-09-02T12:00:00Z", + instruction_ref:content("instruction";"3" * 64), + review_policy_ref:content("review-policy";"4" * 64), + execution_boundary_id:"boundary.codex-review",invocation_kind:"native-review" + }, + snapshot:{ + repository_id:"1270665750",change_request_id:"218",review_id:"300", + head:revision("1" * 40),base:revision("2" * 40),github_app_id:"15368", + observed_at:"2026-09-02T12:00:00Z",status:"COMPLETED",complete:true, + started_at:"2026-09-02T10:00:00Z",updated_at:"2026-09-02T11:00:00Z", + terminal_at:"2026-09-02T11:00:00Z",dismissed_at:null, + reported_top_level_count:0,reported_inline_count:0, + top_level_findings:[],inline_findings:[], + hidden_execution:{model:unavailable,effort:unavailable,tools:unavailable,cost:unavailable}, + provider_metadata:{summary:"provider clean text",provider_verdict:"looks-good"} + } + } +' >"$tmp/baseline.json" + +check manifest-canonical /usr/bin/cmp -s "$manifest" \ + <("${jq_command[@]}" -S -c . "$manifest") +check manifest-core-document /usr/bin/env PATH="$runtime_bin:/usr/bin:/bin" \ + "$root/scripts/core-contract.sh" validate-document "$manifest" +generation=$("${jq_command[@]}" -er \ + 'select(type=="array" and length==1) | .[0].generation_id' \ + "$root/core/v2/generation-registry.json") +modules="$root/core/v2/generations/$generation/modules" +check manifest-public-reviewer-policy "${jq_command[@]}" -L "$modules" -e -n \ + --slurpfile manifest_doc "$manifest" ' + import "schema" as schema; + import "profile_graph" as graph; + $manifest_doc[0] as $manifest | + ($manifest | graph::adapter_manifest_self_ok) and + $manifest.body.offered_capabilities == schema::capabilities_for_role("reviewer") and + $manifest.body.offered_permissions == + schema::permissions_for_capability("core.review.change.v1";"model") and + $manifest.body.offered_tools == [] + ' + +package_ref_ok() { + local commit path object mode + commit=$("${jq_command[@]}" -r '.body.package_ref.revision.commit_id' "$manifest") + path=$("${jq_command[@]}" -r '.body.package_ref.location.value' "$manifest") + object=$("${jq_command[@]}" -r '.body.package_ref.object_id' "$manifest") + mode=$("${jq_command[@]}" -r '.body.package_ref.mode' "$manifest") + /usr/bin/git -C "$root" merge-base --is-ancestor "$commit" HEAD && + [ "$(/usr/bin/git -C "$root" rev-parse "$commit:$path")" = "$object" ] && + [ "$(/usr/bin/git -C "$root" hash-object "$normalizer")" = "$object" ] && + [ "$(/usr/bin/git -C "$root" ls-tree "$commit" -- "$path" | /usr/bin/awk '{print $1}')" = "$mode" ] +} +check manifest-package-ref package_ref_ok + +top='[{finding_id:"T1",body:"top finding",provider_severity:"custom-urgent", + provider_metadata:{classification:"provider-only"}}]' +inline='[{finding_id:"I1",path:"src/main.sh",line:7,side:"RIGHT", + commit_id:("1" * 40),body:"inline finding",provider_severity:"banana", + provider_metadata:{classification:"provider-only"}}]' +expect_state clean '.' clean +expect_state top-findings ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1)" findings +expect_state inline-findings ".snapshot |= (.inline_findings=$inline | .reported_inline_count=1)" findings +expect_state dismissed \ + '.snapshot |= (.status="DISMISSED" | .terminal_at="2026-09-02T10:59:59Z" | + .dismissed_at="2026-09-02T11:00:00Z")' dismissed +expect_state timeout '.snapshot |= (.status="TIMED_OUT" | .complete=false)' timeout +expect_state failed '.snapshot |= (.status="FAILED" | .complete=false)' failed +expect_state incomplete \ + '.snapshot |= (.complete=false | .reported_top_level_count=1)' inconclusive +expect_state in-progress \ + '.snapshot |= (.status="IN_PROGRESS" | .complete=false | .terminal_at=null)' inconclusive +expect_state unknown \ + '.snapshot |= (.status="UNKNOWN" | .complete=false | .terminal_at=null)' inconclusive + +expect_stale stale-app '.snapshot.github_app_id="15369"' app +expect_stale stale-base '.snapshot.base.commit_id=("7" * 40)' base +expect_stale stale-change-request '.snapshot.change_request_id="219"' change-request +expect_stale stale-head '.snapshot.head.commit_id=("8" * 40)' head +expect_stale stale-observation-time \ + '.snapshot.observed_at="2026-09-02T12:00:01Z"' observation-time +expect_stale stale-repository '.snapshot.repository_id="1270665751"' repository +expect_stale stale-review '.snapshot.review_id="301"' review + +expect_reject missing-field 'del(.snapshot.inline_findings)' +expect_reject extra-field '.snapshot.write_requested=true' +expect_reject unsupported-status '.snapshot.status="APPROVED"' +expect_reject invalid-date '.snapshot.started_at="2026-02-30T10:00:00Z"' +expect_reject future-update '.snapshot.updated_at="2026-09-02T12:00:01Z"' +expect_reject late-terminal '.snapshot.terminal_at="2026-09-02T11:00:01Z"' +expect_reject malformed-instruction '.trust_context.instruction_ref.sha256=("A" * 64)' +expect_reject exposed-model '.snapshot.hidden_execution.model={state:"present",value:"secret"}' +expect_reject missing-cost 'del(.snapshot.hidden_execution.cost)' +expect_reject complete-count-mismatch '.snapshot.reported_inline_count=1' +expect_reject missing-finding-body \ + ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1 | + del(.top_level_findings[0].body))" +expect_reject duplicate-finding-id \ + ".snapshot |= (.top_level_findings=$top | .inline_findings=$inline | + .inline_findings[0].finding_id=\"T1\" | .reported_top_level_count=1 | + .reported_inline_count=1)" +expect_reject unsorted-top-findings \ + ".snapshot |= (.top_level_findings=($top + $top) | + .top_level_findings[0].finding_id=\"T2\" | .reported_top_level_count=2)" +expect_reject unsorted-inline-findings \ + ".snapshot |= (.inline_findings=($inline + $inline) | + .inline_findings[0].finding_id=\"I2\" | .inline_findings[0].path=\"z.sh\" | + .inline_findings[1].path=\"a.sh\" | .reported_inline_count=2)" +expect_reject wrong-inline-commit \ + ".snapshot |= (.inline_findings=$inline | .inline_findings[0].commit_id=(\"9\" * 40) | + .reported_inline_count=1)" +expect_reject invalid-inline-line \ + ".snapshot |= (.inline_findings=$inline | .inline_findings[0].line=0 | + .reported_inline_count=1)" + +"${jq_command[@]}" -S -c -f "$normalizer" "$tmp/baseline.json" >"$tmp/repeat-a.json" +"${jq_command[@]}" -S -c -f "$normalizer" "$tmp/baseline.json" >"$tmp/repeat-b.json" +check canonical-repeat /usr/bin/cmp -s "$tmp/repeat-a.json" "$tmp/repeat-b.json" +check canonical-output /usr/bin/cmp -s "$tmp/repeat-a.json" \ + <("${jq_command[@]}" -S -c . "$tmp/repeat-a.json") +check read-only-no-authority-effects "${jq_command[@]}" -e ' + .review_mode == "read-only" and .authority == "none" and .effects == [] and + .qualification == {state:"unavailable",reason_id:"adapter.unqualified"} and + ([.. | objects | keys[]] as $keys | + ($keys | index("authority_ref") == null) and + ($keys | index("approval") == null) and ($keys | index("merge") == null) and + ($keys | index("write") == null)) +' "$tmp/repeat-a.json" +expect_state severity-is-opaque \ + ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1)" findings +check provider-severity-is-data "${jq_command[@]}" -e \ + '.observation.top_level_findings[0].provider_severity == "custom-urgent"' \ + "$tmp/severity-is-opaque.out" +check instruction-boundary-provenance "${jq_command[@]}" -e \ + --slurpfile input "$tmp/baseline.json" ' + .trust_context.instruction_ref == $input[0].trust_context.instruction_ref and + .trust_context.execution_boundary_id == $input[0].trust_context.execution_boundary_id and + (.observation.hidden_execution | [.model,.effort,.tools,.cost] | + all(.[]; .state == "unavailable")) + ' "$tmp/repeat-a.json" +check no-selected-generation-id /usr/bin/env sh -c \ + '! grep -E "g-[0-9a-f]{64}" "$1" "$2" "$3"' sh \ + "$normalizer" "$manifest" "$root/scripts/test/default-codex-native-reviewer-adapter.test.sh" +check pure-read-only-jq /usr/bin/env sh -c \ + '! grep -E "core[.]perm|@codex[[:space:]]+fix|approve|merge|credential|token|curl|graphql|github[.]com" "$1"' sh \ + "$normalizer" + +/usr/bin/printf 'default Codex native reviewer adapter: %s/%s checks passed\n' "$passed" "$passed" From 147bc23c0f62f53206b59d4df164a6371fd6ec91 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 10:38:51 -0400 Subject: [PATCH 3/6] Bind Codex reviewer package source repository --- adapters/codex-native-reviewer/v1/manifest.json | 2 +- scripts/test/default-codex-native-reviewer-adapter.test.sh | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/adapters/codex-native-reviewer/v1/manifest.json b/adapters/codex-native-reviewer/v1/manifest.json index 159e0d8..c474d36 100644 --- a/adapters/codex-native-reviewer/v1/manifest.json +++ b/adapters/codex-native-reviewer/v1/manifest.json @@ -1 +1 @@ -{"body":{"adapter_version":"v1","offered_capabilities":["core.review.change.v1"],"offered_execution_kinds":["model"],"offered_permissions":["core.perm.evidence.write.v1","core.perm.model.invoke.v1","core.perm.target.read.v1"],"offered_roles":["reviewer"],"offered_tools":[],"package_ref":{"location":{"kind":"path","value":"adapters/codex-native-reviewer/v1/normalize.jq"},"mode":"100644","object_id":"1a17e2f0b18f02be8f1c53d14c0264632f1dac2b","object_type":"blob","revision":{"commit_id":"5d6743d33487233f1115e8239c2ff3623f6cfa43","hash_algorithm":"sha1","repository_id":"ystack.source"}}},"id":"adapter.codex-native-reviewer.v1","kind":"adapter_manifest","schema_version":2} +{"body":{"adapter_version":"v1","offered_capabilities":["core.review.change.v1"],"offered_execution_kinds":["model"],"offered_permissions":["core.perm.evidence.write.v1","core.perm.model.invoke.v1","core.perm.target.read.v1"],"offered_roles":["reviewer"],"offered_tools":[],"package_ref":{"location":{"kind":"path","value":"adapters/codex-native-reviewer/v1/normalize.jq"},"mode":"100644","object_id":"1a17e2f0b18f02be8f1c53d14c0264632f1dac2b","object_type":"blob","revision":{"commit_id":"5d6743d33487233f1115e8239c2ff3623f6cfa43","hash_algorithm":"sha1","repository_id":"ystack.control-plane"}}},"id":"adapter.codex-native-reviewer.v1","kind":"adapter_manifest","schema_version":2} diff --git a/scripts/test/default-codex-native-reviewer-adapter.test.sh b/scripts/test/default-codex-native-reviewer-adapter.test.sh index 5c59244..0b984bc 100755 --- a/scripts/test/default-codex-native-reviewer-adapter.test.sh +++ b/scripts/test/default-codex-native-reviewer-adapter.test.sh @@ -129,12 +129,15 @@ check manifest-public-reviewer-policy "${jq_command[@]}" -L "$modules" -e -n \ ' package_ref_ok() { - local commit path object mode + local repository commit path object mode + repository=$("${jq_command[@]}" -r \ + '.body.package_ref.revision.repository_id' "$manifest") commit=$("${jq_command[@]}" -r '.body.package_ref.revision.commit_id' "$manifest") path=$("${jq_command[@]}" -r '.body.package_ref.location.value' "$manifest") object=$("${jq_command[@]}" -r '.body.package_ref.object_id' "$manifest") mode=$("${jq_command[@]}" -r '.body.package_ref.mode' "$manifest") - /usr/bin/git -C "$root" merge-base --is-ancestor "$commit" HEAD && + [ "$repository" = ystack.control-plane ] && + /usr/bin/git -C "$root" merge-base --is-ancestor "$commit" HEAD && [ "$(/usr/bin/git -C "$root" rev-parse "$commit:$path")" = "$object" ] && [ "$(/usr/bin/git -C "$root" hash-object "$normalizer")" = "$object" ] && [ "$(/usr/bin/git -C "$root" ls-tree "$commit" -- "$path" | /usr/bin/awk '{print $1}')" = "$mode" ] From bb7653a392671ccfb0bf08a2d3cccfdee3fe84b1 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 14:19:22 -0400 Subject: [PATCH 4/6] Stage Codex reviewer normalizer payload first --- README.md | 16 ++++++++ RESTORE.md | 15 +++++++ .../codex-native-reviewer/v1/manifest.json | 1 - ci/required-files.txt | 4 ++ ...ault-codex-native-reviewer-adapter.test.sh | 41 +------------------ 5 files changed, 37 insertions(+), 40 deletions(-) delete mode 100644 adapters/codex-native-reviewer/v1/manifest.json diff --git a/README.md b/README.md index 78006e5..268b6df 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,22 @@ main. The payload is offline and unqualified. It does not call GitHub or a CLI, credential, change a repository or request, grant authority or qualification, or activate a profile. +## Inactive Codex native reviewer normalizer payload + +`adapters/codex-native-reviewer/v1/normalize.jq` validates one untrusted +native-review snapshot against caller-supplied repository, change request, +review, head, base, GitHub app, time, instruction, policy, and execution-boundary +bindings. It returns a canonical generic observation for clean, findings, +dismissed, timeout, failed, stale, incomplete, or non-terminal state. Provider +severity and metadata stay opaque data, and unavailable hidden execution facts +remain explicit. + +This stage lands only the immutable normalizer payload. A later assembly PR can +add its manifest and default-set wiring after this payload has a durable commit +on main. The payload is offline, read-only, and unqualified. It does not invoke a +model or CLI, use a credential or network, post a review, grant authority or +qualification, or activate a profile. + ## The current default team You talk **only** to yshifu, in a Claude Code session. yshifu orchestrates the other roles diff --git a/RESTORE.md b/RESTORE.md index e872d00..42bac9d 100644 --- a/RESTORE.md +++ b/RESTORE.md @@ -464,6 +464,21 @@ durable main commit and add default-set wiring. The pure jq payload is offline a unqualified. It does not call GitHub, use a credential, change a repository or request, grant authority or qualification, or activate a profile. +Restore the two paths in the manifest's inactive Codex native reviewer +normalizer payload block, then run: + +```sh +bash scripts/test/default-codex-native-reviewer-adapter.test.sh +``` + +This checks exact caller bindings, deterministic clean and finding states, +opaque provider severity, explicit hidden-execution unavailability, and +fail-closed malformed or stale input. This stage intentionally has no adapter +manifest. A later assembly PR can bind the payload through a durable main commit +and add default-set wiring. The pure jq payload is offline, read-only, and +unqualified. It does not invoke a model or CLI, use a credential or network, +post a review, grant authority or qualification, or activate a profile. + --- ## 5. Smoke test — prove the rebuilt team is alive diff --git a/adapters/codex-native-reviewer/v1/manifest.json b/adapters/codex-native-reviewer/v1/manifest.json deleted file mode 100644 index c474d36..0000000 --- a/adapters/codex-native-reviewer/v1/manifest.json +++ /dev/null @@ -1 +0,0 @@ -{"body":{"adapter_version":"v1","offered_capabilities":["core.review.change.v1"],"offered_execution_kinds":["model"],"offered_permissions":["core.perm.evidence.write.v1","core.perm.model.invoke.v1","core.perm.target.read.v1"],"offered_roles":["reviewer"],"offered_tools":[],"package_ref":{"location":{"kind":"path","value":"adapters/codex-native-reviewer/v1/normalize.jq"},"mode":"100644","object_id":"1a17e2f0b18f02be8f1c53d14c0264632f1dac2b","object_type":"blob","revision":{"commit_id":"5d6743d33487233f1115e8239c2ff3623f6cfa43","hash_algorithm":"sha1","repository_id":"ystack.control-plane"}}},"id":"adapter.codex-native-reviewer.v1","kind":"adapter_manifest","schema_version":2} diff --git a/ci/required-files.txt b/ci/required-files.txt index 64bb6b8..8073369 100644 --- a/ci/required-files.txt +++ b/ci/required-files.txt @@ -232,3 +232,7 @@ scripts/test/orchestrator-reconciliation-plan.test.sh # Inactive GitHub forge normalizer payload adapters/github-forge/v1/normalize.jq scripts/test/default-github-forge-adapter.test.sh + +# Inactive Codex native reviewer normalizer payload +adapters/codex-native-reviewer/v1/normalize.jq +scripts/test/default-codex-native-reviewer-adapter.test.sh diff --git a/scripts/test/default-codex-native-reviewer-adapter.test.sh b/scripts/test/default-codex-native-reviewer-adapter.test.sh index 0b984bc..23666b7 100755 --- a/scripts/test/default-codex-native-reviewer-adapter.test.sh +++ b/scripts/test/default-codex-native-reviewer-adapter.test.sh @@ -5,7 +5,6 @@ export LC_ALL=C root=$(CDPATH='' cd -P -- "${BASH_SOURCE[0]%/*}/../.." && pwd -P) normalizer="$root/adapters/codex-native-reviewer/v1/normalize.jq" -manifest="$root/adapters/codex-native-reviewer/v1/manifest.json" tmp=$(/usr/bin/mktemp -d "${TMPDIR:-/tmp}/ystack-codex-reviewer.XXXXXX") trap '/bin/rm -rf -- "$tmp"' EXIT @@ -108,42 +107,6 @@ expect_reject() { } ' >"$tmp/baseline.json" -check manifest-canonical /usr/bin/cmp -s "$manifest" \ - <("${jq_command[@]}" -S -c . "$manifest") -check manifest-core-document /usr/bin/env PATH="$runtime_bin:/usr/bin:/bin" \ - "$root/scripts/core-contract.sh" validate-document "$manifest" -generation=$("${jq_command[@]}" -er \ - 'select(type=="array" and length==1) | .[0].generation_id' \ - "$root/core/v2/generation-registry.json") -modules="$root/core/v2/generations/$generation/modules" -check manifest-public-reviewer-policy "${jq_command[@]}" -L "$modules" -e -n \ - --slurpfile manifest_doc "$manifest" ' - import "schema" as schema; - import "profile_graph" as graph; - $manifest_doc[0] as $manifest | - ($manifest | graph::adapter_manifest_self_ok) and - $manifest.body.offered_capabilities == schema::capabilities_for_role("reviewer") and - $manifest.body.offered_permissions == - schema::permissions_for_capability("core.review.change.v1";"model") and - $manifest.body.offered_tools == [] - ' - -package_ref_ok() { - local repository commit path object mode - repository=$("${jq_command[@]}" -r \ - '.body.package_ref.revision.repository_id' "$manifest") - commit=$("${jq_command[@]}" -r '.body.package_ref.revision.commit_id' "$manifest") - path=$("${jq_command[@]}" -r '.body.package_ref.location.value' "$manifest") - object=$("${jq_command[@]}" -r '.body.package_ref.object_id' "$manifest") - mode=$("${jq_command[@]}" -r '.body.package_ref.mode' "$manifest") - [ "$repository" = ystack.control-plane ] && - /usr/bin/git -C "$root" merge-base --is-ancestor "$commit" HEAD && - [ "$(/usr/bin/git -C "$root" rev-parse "$commit:$path")" = "$object" ] && - [ "$(/usr/bin/git -C "$root" hash-object "$normalizer")" = "$object" ] && - [ "$(/usr/bin/git -C "$root" ls-tree "$commit" -- "$path" | /usr/bin/awk '{print $1}')" = "$mode" ] -} -check manifest-package-ref package_ref_ok - top='[{finding_id:"T1",body:"top finding",provider_severity:"custom-urgent", provider_metadata:{classification:"provider-only"}}]' inline='[{finding_id:"I1",path:"src/main.sh",line:7,side:"RIGHT", @@ -230,8 +193,8 @@ check instruction-boundary-provenance "${jq_command[@]}" -e \ all(.[]; .state == "unavailable")) ' "$tmp/repeat-a.json" check no-selected-generation-id /usr/bin/env sh -c \ - '! grep -E "g-[0-9a-f]{64}" "$1" "$2" "$3"' sh \ - "$normalizer" "$manifest" "$root/scripts/test/default-codex-native-reviewer-adapter.test.sh" + '! grep -E "g-[0-9a-f]{64}" "$1" "$2"' sh \ + "$normalizer" "$root/scripts/test/default-codex-native-reviewer-adapter.test.sh" check pure-read-only-jq /usr/bin/env sh -c \ '! grep -E "core[.]perm|@codex[[:space:]]+fix|approve|merge|credential|token|curl|graphql|github[.]com" "$1"' sh \ "$normalizer" From 3db24d423a61130d76044b395242141a4ca0f2c5 Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 17:11:27 -0400 Subject: [PATCH 5/6] Close Codex reviewer normalization gaps --- .../codex-native-reviewer/v1/normalize.jq | 51 ++++++++- ...ault-codex-native-reviewer-adapter.test.sh | 104 ++++++++++++++++++ 2 files changed, 149 insertions(+), 6 deletions(-) diff --git a/adapters/codex-native-reviewer/v1/normalize.jq b/adapters/codex-native-reviewer/v1/normalize.jq index 1a17e2f..432f09f 100644 --- a/adapters/codex-native-reviewer/v1/normalize.jq +++ b/adapters/codex-native-reviewer/v1/normalize.jq @@ -14,6 +14,42 @@ def sha256_ok: type == "string" and test("\\A[0-9a-f]{64}\\z"); def text_ok: type == "string" and utf8bytelength >= 1 and utf8bytelength <= 8192; +def media_type_ok: + type == "string" and + utf8bytelength <= 127 and + test("\\A[a-z0-9][a-z0-9!#$&^_.+-]*/[a-z0-9][a-z0-9!#$&^_.+-]*\\z"); + +def metadata_number_ok: + type == "number" and + (isnan | not) and + (isinfinite | not) and + . >= -9007199254740991 and + . <= 9007199254740991 and + tostring != "-0"; + +def metadata_value_ok($depth): + . as $value | + $depth <= 8 and + if type == "object" then + length <= 64 and + all(keys_unsorted[]; + utf8bytelength <= 128 and + ($value[.] | metadata_value_ok($depth + 1))) + elif type == "array" then + length <= 64 and all(.[]; metadata_value_ok($depth + 1)) + elif type == "string" then + utf8bytelength <= 4096 + elif type == "number" then + metadata_number_ok + else + type == "boolean" or type == "null" + end; + +def provider_metadata_ok: + type == "object" and + (tojson | utf8bytelength <= 16384) and + ([paths] | length <= 255) and + metadata_value_ok(1); def time_ok: type == "string" and @@ -45,8 +81,9 @@ def revision_ok: def content_ref_ok: exact_fields(["content_id","media_type","sha256"];[]) and (.content_id | id_ok) and - (.media_type | type == "string" and - test("\\A[a-z0-9][a-z0-9!#$&^_.+-]*/[a-z0-9][a-z0-9!#$&^_.+-]*\\z")) and + (.content_id | contains(":") | not) and + (.content_id | contains("/") | not) and + (.media_type | media_type_ok) and (.sha256 | sha256_ok); def trust_context_ok: @@ -88,7 +125,7 @@ def top_finding_ok: (.finding_id | finding_id_ok) and (.body | text_ok) and (.provider_severity | text_ok) and - (.provider_metadata | type == "object"); + (.provider_metadata | provider_metadata_ok); def inline_finding_ok($head): exact_fields( @@ -102,7 +139,7 @@ def inline_finding_ok($head): .commit_id == $head.commit_id and (.body | text_ok) and (.provider_severity | text_ok) and - (.provider_metadata | type == "object"); + (.provider_metadata | provider_metadata_ok); def findings_ok: . as $snapshot | @@ -143,7 +180,9 @@ def status_facts_ok: def timestamps_ok: (.started_at | time_ok) and (.updated_at | time_ok) and (.observed_at | time_ok) and .started_at <= .updated_at and .updated_at <= .observed_at and - (if .terminal_at == null then true else .terminal_at <= .updated_at end) and + (if .terminal_at == null then true + else .started_at <= .terminal_at and .terminal_at <= .updated_at + end) and (if .dismissed_at == null then true else .dismissed_at <= .updated_at end); def snapshot_ok: @@ -164,7 +203,7 @@ def snapshot_ok: (.status | IN("COMPLETED","DISMISSED","TIMED_OUT","FAILED","IN_PROGRESS","UNKNOWN")) and (.complete | type == "boolean") and (.hidden_execution | hidden_execution_ok) and - (.provider_metadata | type == "object") and + (.provider_metadata | provider_metadata_ok) and status_facts_ok and timestamps_ok and findings_ok; def stale_bindings($context; $snapshot): diff --git a/scripts/test/default-codex-native-reviewer-adapter.test.sh b/scripts/test/default-codex-native-reviewer-adapter.test.sh index 23666b7..ac9a387 100755 --- a/scripts/test/default-codex-native-reviewer-adapter.test.sh +++ b/scripts/test/default-codex-native-reviewer-adapter.test.sh @@ -28,6 +28,10 @@ if [ "$platform" = Darwin:arm64 ]; then jq_command=(/usr/bin/arch -x86_64 "$jq_b runtime_bin="$tmp/bin" /bin/mkdir -m 700 "$runtime_bin" /bin/ln -s "$jq_bin" "$runtime_bin/jq" +generation=$("${jq_command[@]}" -er \ + 'select(type=="array" and length==1) | .[0].generation_id' \ + "$root/core/v2/generation-registry.json") +modules="$root/core/v2/generations/$generation/modules" check() { local name=$1 @@ -112,6 +116,37 @@ top='[{finding_id:"T1",body:"top finding",provider_severity:"custom-urgent", inline='[{finding_id:"I1",path:"src/main.sh",line:7,side:"RIGHT", commit_id:("1" * 40),body:"inline finding",provider_severity:"banana", provider_metadata:{classification:"provider-only"}}]' + +check content-ref-public-schema-positive "${jq_command[@]}" -L "$modules" -e ' + import "schema" as schema; + all([.trust_context.instruction_ref,.trust_context.review_policy_ref][]; + schema::content_ref_ok) +' "$tmp/baseline.json" +expect_state content-ref-boundary ' + .trust_context |= + (.instruction_ref.content_id=("a" + ("b" * 127)) | + .instruction_ref.media_type=("a/" + ("b" * 125)) | + .review_policy_ref.content_id=("c" + ("d" * 127)) | + .review_policy_ref.media_type=("c/" + ("d" * 125))) +' clean +check content-ref-public-schema-boundary "${jq_command[@]}" -L "$modules" -e ' + import "schema" as schema; + all([.trust_context.instruction_ref,.trust_context.review_policy_ref][]; + schema::content_ref_ok) +' "$tmp/content-ref-boundary.json" +expect_reject content-ref-colon \ + '.trust_context.instruction_ref.content_id="instruction:bad"' +check content-ref-public-schema-reject-colon "${jq_command[@]}" -L "$modules" -e ' + import "schema" as schema; + (.trust_context.instruction_ref | schema::content_ref_ok) == false +' "$tmp/content-ref-colon.json" +expect_reject content-ref-slash \ + '.trust_context.review_policy_ref.content_id="review/policy"' +expect_reject content-ref-media-too-long \ + '.trust_context.instruction_ref.media_type=("a/" + ("b" * 126))' +expect_reject content-ref-media-invalid \ + '.trust_context.review_policy_ref.media_type="Application/JSON"' + expect_state clean '.' clean expect_state top-findings ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1)" findings expect_state inline-findings ".snapshot |= (.inline_findings=$inline | .reported_inline_count=1)" findings @@ -127,6 +162,69 @@ expect_state in-progress \ expect_state unknown \ '.snapshot |= (.status="UNKNOWN" | .complete=false | .terminal_at=null)' inconclusive +expect_reject completed-terminal-before-start \ + '.snapshot.terminal_at="2026-09-02T09:59:59Z"' +expect_reject failed-terminal-before-start \ + '.snapshot |= (.status="FAILED" | .complete=false | + .terminal_at="2026-09-02T09:59:59Z")' +expect_reject timeout-terminal-before-start \ + '.snapshot |= (.status="TIMED_OUT" | .complete=false | + .terminal_at="2026-09-02T09:59:59Z")' +expect_reject dismissed-terminal-before-start \ + '.snapshot |= (.status="DISMISSED" | + .terminal_at="2026-09-02T09:59:59Z" | + .dismissed_at="2026-09-02T10:30:00Z")' + +expect_state snapshot-metadata-serialized-boundary ' + .snapshot.provider_metadata={ + a:("x" * 4096),b:("x" * 4096),c:("x" * 4096),d:("x" * 4067) + } +' clean +check snapshot-metadata-exact-byte-boundary "${jq_command[@]}" -e ' + (.snapshot.provider_metadata | tojson | utf8bytelength) == 16384 +' "$tmp/snapshot-metadata-serialized-boundary.json" +expect_state snapshot-metadata-depth-boundary \ + '.snapshot.provider_metadata={a:{a:{a:{a:{a:{a:{a:"x"}}}}}}}' clean +expect_state snapshot-metadata-node-boundary ' + .snapshot.provider_metadata={ + a:[range(0;64)],b:[range(0;64)],c:[range(0;64)],d:[range(0;59)] + } +' clean +expect_state snapshot-metadata-scalar-domain ' + .snapshot.provider_metadata={ + null_value:null,bool_value:true,number_value:1.5,string_value:"opaque" + } +' clean +expect_state top-metadata-key-boundary \ + ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1 | + .top_level_findings[0].provider_metadata=({} | .[(\"k\" * 128)]=\"v\"))" findings +expect_state inline-metadata-container-boundary \ + ".snapshot |= (.inline_findings=$inline | .reported_inline_count=1 | + .inline_findings[0].provider_metadata={items:[range(0;64)]})" findings + +expect_reject snapshot-metadata-string-too-large \ + '.snapshot.provider_metadata={text:("x" * 4097)}' +expect_reject snapshot-metadata-serialized-too-large ' + .snapshot.provider_metadata={ + a:("x" * 4096),b:("x" * 4096),c:("x" * 4096),d:("x" * 4068) + } +' +expect_reject snapshot-metadata-too-deep \ + '.snapshot.provider_metadata={a:{a:{a:{a:{a:{a:{a:{a:"x"}}}}}}}}' +expect_reject snapshot-metadata-too-many-nodes ' + .snapshot.provider_metadata={ + a:[range(0;64)],b:[range(0;64)],c:[range(0;64)],d:[range(0;60)] + } +' +expect_reject snapshot-metadata-number-out-of-domain \ + '.snapshot.provider_metadata={value:9007199254740992}' +expect_reject top-metadata-key-too-large \ + ".snapshot |= (.top_level_findings=$top | .reported_top_level_count=1 | + .top_level_findings[0].provider_metadata=({} | .[(\"k\" * 129)]=\"v\"))" +expect_reject inline-metadata-container-too-large \ + ".snapshot |= (.inline_findings=$inline | .reported_inline_count=1 | + .inline_findings[0].provider_metadata={items:[range(0;65)]})" + expect_stale stale-app '.snapshot.github_app_id="15369"' app expect_stale stale-base '.snapshot.base.commit_id=("7" * 40)' base expect_stale stale-change-request '.snapshot.change_request_id="219"' change-request @@ -172,6 +270,12 @@ expect_reject invalid-inline-line \ check canonical-repeat /usr/bin/cmp -s "$tmp/repeat-a.json" "$tmp/repeat-b.json" check canonical-output /usr/bin/cmp -s "$tmp/repeat-a.json" \ <("${jq_command[@]}" -S -c . "$tmp/repeat-a.json") +"${jq_command[@]}" -S -c -f "$normalizer" \ + "$tmp/snapshot-metadata-serialized-boundary.json" >"$tmp/metadata-repeat-a.json" +"${jq_command[@]}" -S -c -f "$normalizer" \ + "$tmp/snapshot-metadata-serialized-boundary.json" >"$tmp/metadata-repeat-b.json" +check metadata-boundary-canonical-repeat /usr/bin/cmp -s \ + "$tmp/metadata-repeat-a.json" "$tmp/metadata-repeat-b.json" check read-only-no-authority-effects "${jq_command[@]}" -e ' .review_mode == "read-only" and .authority == "none" and .effects == [] and .qualification == {state:"unavailable",reason_id:"adapter.unqualified"} and From 0723a6244553cb30c31a60cd1608a54a880237aa Mon Sep 17 00:00:00 2001 From: ci Date: Wed, 2 Sep 2026 17:44:02 -0400 Subject: [PATCH 6/6] Allow Codex adapter schema compatibility proof --- scripts/test/portable-core-schema.test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/test/portable-core-schema.test.sh b/scripts/test/portable-core-schema.test.sh index 414d692..0f3c892 100755 --- a/scripts/test/portable-core-schema.test.sh +++ b/scripts/test/portable-core-schema.test.sh @@ -777,6 +777,7 @@ schema_import_path_ok() { local test_path case "$import_path" in orchestrator/v1/reconciliation-plan.jq|orchestrator/v1/state-scanner.jq) ;; + scripts/test/default-codex-native-reviewer-adapter.test.sh|\ scripts/test/default-github-forge-adapter.test.sh) ;; scripts/test/portable-core-*) test_path="${import_path#scripts/test/}"