Son of Anton Docs
Code review

Publish Modes and Rollout

Native shadow/live publication, policy gates, ledger boundaries and rollout precautions.

Publication behavior depends on the runtime. This page describes son-of-anton-review (feat/cloudflare-native) and distinguishes its native container from the standalone Node worker.

Native publish mode

cloudflare-native/src/publish-mode.js resolves GILF_PUBLISH_MODE:

ValueResult
Absent, empty or whitespace-onlyshadow
shadowIn-memory RecordingPublisher
liveGitHubPublisher; an authenticated client is required
Case/whitespace variants, such as LiveNormalized with trim and lowercase, therefore live
Other values, such as liv or onThrows invalid GILF_PUBLISH_MODE

The native environment allowlist does forward GILF_PUBLISH_MODE. Configure it as a Worker var, for example in the existing vars object of cloudflare-native/wrangler.jsonc:

{
  "GILF_PUBLISH_MODE": "shadow"
}

This is a vars fragment, not a complete Wrangler configuration. Set live only when the native key-broker publication path is configured. The entrypoint logs omp-container: publish mode <mode> and checks consistency with the dispatch lease. Shadow uses container-dispatch:shadow; live uses container-dispatch.

A local-development container using GILF_DB_PATH has no key-broker transport and cannot publish live. The native live publisher obtains a scoped client through the broker; the container does not receive the App private key.

Standalone Node worker is different

worker.mjs does not use GILF_PUBLISH_MODE to select its publisher. It starts with RecordingPublisher and selects GitHubPublisher when GITHUB_APP_ID and either GITHUB_APP_PRIVATE_KEY or GITHUB_APP_PRIVATE_KEY_PATH are configured. Do not use a native shadow-mode instruction as a safety switch for that runtime. This page does not require inspecting or printing any credential value.

What shadow means

In the native container, review publication calls go to RecordingPublisher, which records check_run, review, comment and pr_description events and returns synthetic IDs for check runs. It does not issue those GitHub mutations. Cloning, model requests, sandbox validation and other read-side work are separate and remain conditional on their own gates; shadow publication is not a no-work mode.

Shadow publication ledger actions and check-run IDs are namespaced separately from live ones. A succeeded shadow action does not count as a succeeded live action for the same head. A stored analysis checkpoint may be reused, so a container does not necessarily analyze the PR again on every dispatch.

Do not confuse three features:

  • Publish shadow: suppresses GitHub publication through the selected publisher.
  • Validation shadow: comparison telemetry from another executor; not merged into findings.
  • Hypothesis shadow: default off and normally secondary, but usable findings can become recovery output after semantic failure.

Live publication policy

The mode and publishing fields in DEFAULT_REPO_CONFIG are not the gates consulted by ReviewService.#publish / #publishCheck. Those methods use normalized operator review policy.

Policy keyDefaultBehavior
statusChecks.enabledtrueCreates/updates the Son of Anton / review check. If a check already exists when disabled, it is completed neutrally.
statusChecks.requiredConfidence0Integer 0–5. Above zero, missing or insufficient confidence returns a failing conclusion.
statusChecks.postStatusCommentsfalseEnables the per-review-key status comment.
updatePrDescriptionfalseAllows the publisher's PR-description update after checking the current head.
autoApprove.enabledfalseAllows approval only when the additional confidence, coverage, risk and policy checks pass.

Normal PR review events are COMMENT, or APPROVE when allowed. There is no REQUEST_CHANGES event in this publisher. Findings appear in the review body and check text, not inline comments or check annotations. The finding schema has no line positions.

The check is created with external_id = reviewKey and updated by ID using PATCH. The review is POSTed with commit_id = headSha and an <!-- anton-review <reviewKey> --> body marker. A new head or explicit variant can create another review; the marker does not imply cross-head finding-comment updates.

Check conclusions

reviewCheckConclusion evaluates in this order:

  1. If a positive required-confidence threshold is unmet, return failure.
  2. If normalized merge status is not clear, return neutral.
  3. With a positive threshold, incomplete/degraded/unpinned semantic coverage or an absent missingValidations array returns neutral.
  4. Otherwise return success.

At threshold zero, merge status alone decides success versus neutral in this helper. A positive threshold does not prove validation passed: the code requires the gap array to exist, not to be empty. These conclusions are not a substitute for reading the review or configuring required checks in GitHub.

Publication ledger: native and local differ

The service claims actions before publication and records success/failure afterward. Relevant actions include pr_review, pr_description, check-state actions, review_status_comment and review_receipt_comment. Manual help/status replies are direct publisher calls, so “every GitHub mutation uses this ledger” would be too broad.

Storage pathReclaim behavior
Native nativePublishAction in cloudflare-native/src/state-transport.jsRequires dispatch lease identity. A new claim inserts running; success stores succeeded; a reported failure becomes uncertain. Existing running/uncertain actions do not expire into another POST. Remote-receipt reconciliation is required first.
Standalone SQLiteStore.claimPublishActionRefuses succeeded actions and running actions within their lease; expired running actions can be reclaimed. This is not the native uncertain-outcome contract.

The native error requires reconciliation; it does not automatically find and reconcile a GitHub review merely because a marker exists. Before retrying an ambiguous mutation, compare GitHub state with the stored review key, marker, check external_id and receipts. A requeue alone is not proof that retrying publication is safe or that an uncertain action has been cleared.

Separate shadow semantic model

CodexReviewRunner accepts a secondary model via shadowModel or GILF_CODEX_SHADOW_MODEL, with GILF_CODEX_SHADOW_PROVIDER, GILF_CODEX_SHADOW_REASONING_EFFORT and GILF_CODEX_SHADOW_TIMEOUT_MS overrides. It is disabled when no shadow model is set. It runs after a successful normal semantic pass, not on every review shortcut, and its findings are stored separately rather than selected as the authoritative findings.

The provider utility recognizes GILF_MODEL_SHADOW, but this runner constructor does not use it to configure its secondary lane. The native entrypoint does not pass shadow-model options, and the allowlist forwards neither GILF_MODEL_SHADOW nor the GILF_CODEX_SHADOW_* settings. Worker vars alone cannot turn this lane on.

This is an operating recommendation, not an automatically enforced promotion workflow:

  1. Start native publication in shadow. Check the consolidated runtime evidence for the actual deployment before enabling writes.
  2. Enable live comments with requiredConfidence: 0 and auto-approval off. Keep the check optional in branch protection while assessing behavior.
  3. Trial a positive confidence threshold while the check is still optional. Review both failures and neutral conclusions; a confidence number is not semantic coverage.
  4. Only make the check required after deciding how those conclusions fit repository merge policy.

To roll back native publication, change the Worker publish mode to shadow and deploy that configuration through the normal release process. This is not cancellation of already-running live work. Drain or reconcile in-flight runs before starting another publisher. Cron mutation authority is a separate control; CRON_AUTHORITY=shadow is not a substitute for changing the container publish mode.

Source evidence

  • son-of-anton-review/cloudflare-native/src/publish-mode.js:17-69; cloudflare-native/src/container-env.js:15-85; cloudflare-native/container/entrypoint.mjs:285-313.
  • son-of-anton-review/worker.mjs:35-53; src/publisher.js; src/review-service.js:801-821,964-1096.
  • son-of-anton-review/src/operator-review-policy.js:26-45; src/review-format.js:138-148; src/github-publisher.js:22-104.
  • son-of-anton-review/cloudflare-native/src/state-transport.js:490-524; src/sqlite-store.js:415-462.
  • son-of-anton-review/src/codex-review-runner.js:2324-2337,2650-2709,3198-3245; src/model-provider.js:349-355; cloudflare-native/src/cron-config.js.

Related: Validation and evidence, Context graph, Triggers and commands.

On this page