localize-pipeline

Localize Guardian

Guardian follows up on translation reviews. It checks trusted feedback against the current source and glossary, applies permitted corrections, and can propose pipeline fixes when a problem keeps recurring. You decide what it may change; humans still review and merge the results.

This is an optional, self-hosted tool, not a hosted service. Each project supplies its own machine, credentials, trusted-reviewer list, and Codex/ChatGPT plan (or explicitly opts into API billing). The operator handles costs, updates, logs, and recovery. Pipeline maintainers receive no credentials or private access.

Start with observe, which records assessments without GitHub writes. Enable editing only after checking a successful observation run and its configuration.

The rest of this page is the operator reference. Closed-PR remediation and pipeline-prevention work each require their own explicit policy.

Authority modes

The configured mode is a ceiling. A command-line option cannot raise the authority granted by the operator-owned config.

Mode Maximum authority
observe Report-only intake, assessment, audit records, and local status. It creates no commits, pushes, comments, or other GitHub writes. This is the default.
prepare Everything in observe, plus validation of eligible value-only replacements in a disposable local checkout. It stores the outcome and a changed-key count in private action state, but retains no patch or reviewable plan. It cannot push or comment.
apply-owned-translations Advance an allowed, Guardian-owned translation PR with validated value replacements, then post one concise status reply. An independently configured closed-PR remediation policy may also create bounded current-base correction PRs ready for review.
propose-prevention Everything above, plus at most the configured number of prevention PRs per poll, shared across repositories, for recurring pipeline defects. It never merges either kind of PR.

The CLI creates new prevention and historical-correction PRs ready for review so repository review bots can start immediately. Signing, regression validation, numeric actor/repository checks, and publication limits are unchanged. Recovery accepts both ready-created PRs and older drafts with a one-way ready transition; it never reopens or converts an existing PR. The internal draft ledger names and max_*_drafts_per_run keys are retained for compatibility and still bound all new publications. Low-level broker callers retain draft creation by default; the runtime explicitly selects ready creation. This does not implement automatic review follow-up on pipeline prevention PRs, nor enable automatic merging.

Review bots may append release notes to a PR description. Recovery still requires every byte of the Guardian-authored body and its evidence marker to match the private ledger. It additionally permits one appended CodeRabbit release-note block of at most 8 KiB, bounded by its standard start/end markers. This suffix is untrusted commentary, not proof of its author’s identity or authority for any edit. Rewritten original text, nested markers, arbitrary trailing text, and oversized annotations remain conflicts; repository, actor, head, and lifecycle checks still apply. The complete remediation body retains its existing size cap.

The Guardian does not merge pull requests, approve reviews, resolve review threads, delete or edit reviewer comments, overwrite an existing remote ref, or broaden its own policy. A human remains responsible for accepting translations and prevention work.

Trust model

The Guardian treats review comments, repository content, pull-request metadata, and model output as untrusted data. Any of them can contain prompt injection or malformed content. The controller never treats text from those sources as a command, policy change, credential request, or authorization decision.

Authorization comes only from the local Guardian config, which should live outside every monitored checkout. Across intake, assessment, validation, and the final remote-write boundary, the controller enforces:

The localization pipeline config and glossary are inputs to validation, not sources of Guardian authority. The backward-compatible default, pipeline_config_source: base, loads them from the exact trusted base SHA, never from the PR head. Their paths must remain inside that trusted tree.

For a project whose pipeline config must stay outside its repository, set pipeline_config_source: operator. The existing relative pipeline_config_path then resolves beside the private Guardian YAML. Before any GitHub or model work, each scheduled or manual poll reads the pipeline config and its configured (or default) safe relative glossary through non-following file descriptors and snapshots them into a private per-poll bundle. The Guardian YAML directory and every bundle directory must be owned by the current operator with mode 0700; pipeline-config and glossary files must be current-user-owned, non-symlink regular files with mode 0600. Inputs are bounded and must be valid UTF-8 YAML or JSON. Unsafe ancestors, absolute paths, parent traversal, malformed content, and an absent explicitly configured glossary fail closed. A missing implicit default glossary.json remains valid, matching pipeline behavior.

The private snapshot cannot change when an operator file is replaced during a poll. Its deterministic digest enters model evidence and the assessment cache identity. In both source modes, source-locale strings still come only from the exact base SHA, and the configured profile source locale must match the Guardian’s local source_locale policy. A PR that changes these inputs cannot use its changed versions to authorize itself.

On the translation-correction path, only value-only replacements are eligible. The read-only assessment model cannot add or remove keys, edit a source-locale file, choose a new path or locale, or replace whole files. A stale observation fails closed and is reconsidered on a later run. The separate, opt-in prevention author may change executable pipeline code only within its explicit code/test allowlists and the additional controls described below.

Oversized open-PR correction proposals are applied in bounded batches, never by raising max_value_edits_per_run. Each run publishes at most that many eligible value edits; a later poll reassesses the remainder against the fresh PR head. This also supports a single review comment requesting more edits than the cap. Partially corrected and wholly deferred comments remain pending, including after publication-reply recovery. Duplicate targets still reject the proposal before batch selection; all selected edits retain the normal validation and signing gates. A zero edit limit disables edits without repeatedly retrying unchanged policy. Progressive batching applies only to apply-owned-translations and propose-prevention. prepare retains no patch or changed head, so it keeps its single-pass bounded validation: an oversized proposal is rejected once under the current policy, not repeatedly prepared in identical partial batches.

Poll outcomes and the latest Guardian health record expose deferred_value_edits, deferred_feedback_items, and translation_policy_rejections separately from failed runs. A successful bounded poll does not mean every correction is finished. Audit rows use translation_batch_deferred with the actual changed/deferred counts and published commit where applicable. State schema 12 also records public explanation delivery and held decisions. Back up the idle database before upgrading; do not downgrade a schema-12 database.

Private deterministic quality findings retain a source-commit link in signed correction commits. This evidence is separate from reviewer comment links and must match the exact scanned head, or its recorded Guardian publication lineage, repository and GitHub host. Public feedback still requires an exact PR-comment link; a commit link in public feedback does not grant correction authority.

Feedback explanations and maintainer decisions

In the two publishing modes, completed open-PR assessments produce bot-labelled explanations at the original review thread. Issue comments and review summaries instead receive a PR comment linking to the source feedback. Explanations distinguish validated corrections, alternatives, already-addressed findings, inapplicable suggestions, deferred work, and maintainer decisions. Applied corrections link to the recorded commit. Threads are never automatically resolved.

Clean CodeRabbit summaries remain in the private assessment audit without public replies when the assessment confirms no applicable correction or maintainer decision. Private prevention analysis continues independently. Unknown summary formats and summaries containing nitpick, outside-diff, or actionable findings retain normal reporting. Previously delivered clean-only summaries are not recreated after operator cleanup; withdrawal notices for other feedback remain enabled.

An interrupted older write can leave a public comment whose delivery was never confirmed locally. Quiet-report retirement preserves that attempt’s payload for audit but does not remove the remote comment; operators must inspect and clean up any such old comments separately.

Public reasons are fixed templates selected by schema-validated codes, not raw model rationale, configuration paths, credentials, or replacement text. An alternative selected to respect the glossary still requires a terminology decision; publishing that correction does not settle the disagreement.

One managed summary comment links to individual explanations and highlights decisions. This is the safe fallback rather than editing PR descriptions: the original CI/translation report, human text, and exact Guardian-generated PR body recovery contracts remain untouched. External edits to a managed comment cause publication to stop rather than overwrite them.

Completed action records (including atomic publication recovery plans) are the durable explanation intents. Exact public payloads and acknowledgements are persisted separately. A lost GitHub response is recovered by checking the exact marker, writer identity, content, and thread; it does not require another model call or correction. Changed feedback must be revalidated/reassessed before any new post, and closed PRs cannot receive these open-PR reports. observe and prepare never publish explanations.

When actionable corrections are pending, Guardian assesses and attempts them before sending old status explanations. A reviewer bot can react immediately to a status reply and change the trusted snapshot; such replies must not repeatedly starve the repair itself. Reporting-only polls still reconcile pending replies without a model call. Every publication retains the same fresh authority checks.

A complete open-PR intake retires pending deliveries for pulls no longer eligible for these reports. Retirement is not a delivery acknowledgement or a policy decision; a reopened eligible pull can reconcile its exact report again. Failed processing records a generic deferred explanation for the next authorized intake, without exposing provider errors or bypassing a quota/authentication circuit.

A recorded decision remains held across model reassessments, PR head movement, and bot replies under the same trusted configuration snapshot. A maintainer must choose the policy, and the operator must deliberately update the governed configuration before reconsideration. This feature does not interpret comments, thread resolution, or merging as glossary-policy authorization. Keep unrelated configuration changes separate from decision resolution and review the next assessment; changing the trusted configuration invalidates the old hold context. If a bounded batch applies part of a glossary alternative, its remaining edits are explicitly reported as human-held. They are not silently discarded, claimed complete, or automatically reconsidered while the policy decision remains held.

guardian status exposes pending explanation acknowledgements and recorded maintainer decisions alongside reporting health. These are separate from pending translation edits and last successful poll. A successful correction or poll does not imply every decision has been settled. Model/session limits, subscription authentication, retry bounds, signing, and existing repository authority remain unchanged. Rendering and delivering reports use no additional model session.

Assessment explicitly considers recurring failures, including untranslated source-identical text and lost safety warnings, as well as individual corrections. Candidates must cite trusted feedback and describe a possible regression test; the prevention author must verify any hypothesized root cause against current pipeline code. Empty candidate lists require an explanation in the assessment summary. This is model assessment, not a guarantee that every systemic defect will be detected. Project-specific terminology remains distinct from generic pipeline fixes; legitimate source-identical brand or shared-language terms must not be changed merely to make them different.

Reviewer authorization

Trust is set per repository and locale. Put native human reviewers under trusted_reviewers and deterministic service accounts under trusted_bots. Each entry includes its immutable numeric GitHub ID and expected API type. Login names are display labels only; they never grant authority.

The same rule applies to repository ownership. base_repo_id and each allowed_head_repositories[].id are authoritative. base_repo is also the expected current API route and must be updated after a base-repository rename. An allowed head repository’s full_name is operator-readable routing metadata; fresh PR authorization follows its numeric ID, so a rename is not confused with an account that reuses the old name.

Human and bot lists are deliberately separate, but both can be explicit feedback authorities. A bot whose numeric ID is listed under trusted_bots may authorize auto-application for that repository and locale; this supports automated review nitpicks. It never inherits trust from trusted_reviewers, and a human entry never authorizes a bot with the same display name. Every resulting proposal still passes the same model-output and deterministic value policy. IDs must be unique across both lists for a locale. Each operator must replace every example ID with values read from GitHub’s API.

For each allowed open PR, intake paginates issue comments, review bodies, and inline review comments. Each authorized edit or deletion is a new immutable observation; it is not silently replaced in the audit trail. Feedback outside the configured reviewer/bot and locale allowlists cannot authorize work and is not sent to Codex. The Guardian’s own marked status replies are excluded from authorization and assessment.

Private repositories

Repository visibility is read from GitHub before any model call. A private repository requires explicit opt-in through private_repo_model_opt_in: true. Without that opt-in, no private review text or repository evidence is sent to Codex. Before enabling it, the operator must confirm that their OpenAI account, data controls, and organizational policy permit the transfer.

Codex assessment boundary

The Guardian uses the non-interactive codex exec interface described in the official Codex documentation. It runs the assessment with a sanitized evidence directory and a fixed JSON Schema. A custom guardian_evidence permission profile grants minimal filesystem reads plus read access to the evidence workspace. The driver uses --ephemeral, --output-schema, --json, --skip-git-repo-check, --ignore-user-config, --ignore-rules, --strict-config, and --ask-for-approval never. Review text remains data rather than shell input or a command-line argument.

Codex receives no GitHub write or signing credential. Its tool processes inherit no model credential, and it cannot edit the monitored checkout. The controller rejects missing, invented, duplicated, oversized, non-UTF-8, or schema-invalid output. It then reconstructs locale, feedback identity, and source values from trusted controller data and independently applies every policy check. Structured output constrains parsing; it does not make model output trusted.

Process containment

Every Codex invocation and focused prevention test runs in a new process session with inherited CPU, file-size, descriptor, and supported process-count limits. On Linux, the Guardian additionally requires a fresh cgroup-v2 leaf for each bounded invocation. It joins the direct child before exec, activates the kernel’s recursive cgroup.kill control on every completion path, waits for populated 0, and only then removes the leaf. The leaf’s maximum depth and descendant-cgroup count are both zero, so a child cannot leave empty nested cgroups behind. This includes a descendant that calls setsid() or setpgid() and escapes the original process group.

The operator’s Linux service or container cgroup must be delegated so Guardian can create those transient leaves. guardian doctor performs a real create/join/kill/remove canary and fails closed when cgroup v2, delegation, or cgroup.kill is unavailable. The cgroup is a lifecycle boundary, not a filesystem sandbox: the Codex and prevention-test sandbox canaries also require that an untrusted tool cannot open the parent cgroup.procs for writing. That denial prevents a same-user descendant from migrating out of the leaf.

macOS has no cgroup-v2 equivalent. There the resource limits, process-group cleanup, Codex permission profile, and operator-supplied prevention sandbox remain in force, but process-group cleanup alone is not a hard boundary against a deliberately detached descendant. Operators who require kernel-enforced descendant teardown should run the Guardian in a Linux container or VM whose outer runtime also tears down the complete container on exit.

The shipped Guardian default is gpt-5.6-terra with reasoning effort high. Terra is OpenAI’s balanced intelligence/cost model and currently uses about half Sol’s Codex token-credit rate; high retains deliberate reasoning for semantic review while every proposed action remains deterministically checked. See the official GPT-5.6 Terra model reference and Codex rate card. Model and effort remain explicit operator policy: use Sol/max only when representative evaluations justify the additional plan allowance or API cost.

Plugin isolation

localize guardian deliberately does not run the translation plugin loader. An explicit --plugin argument is rejected, while LOCALIZE_PLUGIN_MODULES and installed localize.format_adapters entry points are ignored. This prevents project plugin code from running in the credential-bearing Guardian process. Only the package’s built-in registered adapters are available; a trusted-base pipeline config that requires a custom adapter fails closed during a run. doctor reports the adapters already registered in that isolated process, but it does not claim custom-project compatibility.

Install and configure

Prerequisites:

Create an operator-owned config from the report-only example:

GUARDIAN_CONFIG="$HOME/.config/localize/guardian.yaml"
install -d -m 700 "$HOME/.config/localize"
localize guardian init --config "$GUARDIAN_CONFIG"

init refuses to overwrite an existing file. Compare its output with examples/guardian.config.yaml, then set the repository, owned PR/head identities, branch and path constraints, locales, and numeric reviewer IDs. Keep mode: observe until doctor, a manual run, and the local audit output are clean.

The Guardian config is strict: unknown fields, duplicate YAML keys, unsafe relative paths, duplicate identities, and malformed limits are errors. Never load it from a pull-request head or let a monitored repository modify it. Use pipeline_config_source: operator only when the pipeline config and glossary are stored under the same private config directory as described above; base remains the default.

Codex authentication and credentials

The default, codex_auth_mode: chatgpt, consumes the operator’s Codex access and ChatGPT plan allowance, not metered API billing. It can still consume purchased ChatGPT/Codex credits when the operator’s account or workspace allows them, so it is not a guarantee of zero marginal cost. First create the config, then perform one device login into the dedicated codex_home:

localize guardian login --config "$GUARDIAN_CONFIG"

guardian login forces forced_login_method="chatgpt" and cli_auth_credentials_store="file", and Codex refreshes the cached login when needed. The Guardian requires the dedicated directory to be owned by the current user with mode 0700, and its non-symlink auth.json to be a regular user-owned file with mode 0600. Treat that file as a password: do not copy, publish, back it up to a shared location, or commit it. The generated scheduler points at the same private login, so no interactive login is required during a scheduled poll. See the official Codex authentication guide for the distinction between ChatGPT and API-key authentication.

The login, status check, assessment, and prevention-author processes receive a minimal environment. In particular, inherited CODEX_API_KEY and OPENAI_API_KEY are removed, while the forced authentication method prevents an accidental switch from plan allowance to API billing. Codex tool processes receive neither the subscription credential directory nor a model API key.

API-key mode is an explicit opt-in. Set codex_auth_mode: api-key, remove codex_home, configure runtime.codex_api_key_command, and configure both USD limits. In API-key mode, the self-hosted Guardian does not accept an ambient API key. Instead it invokes the configured operator-owned helper just in time, injects CODEX_API_KEY only into the bounded Codex process, and uses an ephemeral Codex home. The helper must print one API key to stdout and nothing else. Keep it outside monitored repositories and backed by an OS secret store.

GitHub secrets are also never stored in Guardian YAML, launchd property lists, generated wrappers, state, model evidence, or logs. Configure an OS-secret-store token helper before installation. The helper must print one repository-scoped token to stdout and nothing else; prefer a short-lived token when the provider supports one. It is invoked as an argv array with shell=False, a timeout, and redacted errors. The token is retained only in memory for the bounded Guardian operation; temporary Git askpass material is removed afterward. Write scopes are used only for an authorized translation branch update and status reply, an explicitly configured prevention branch and PR, or an explicitly configured current-base remediation branch and PR. Historical remediation publication is available in apply-owned-translations or propose-prevention; prevention publication is available only in propose-prevention.

On macOS, either helper can retrieve its credential from Keychain. Keep helpers outside monitored repositories, owned by the operator, and executable only by that user. Do not put a token literal in a helper, environment file, or scheduler argument.

Commit signing

OpenPGP is the backward-compatible default. Keep signing_format: openpgp, set signing_program to the trusted GPG executable, and set signing_key to the full 40- or 64-hex fingerprint (optionally suffixed by ! for an exact GPG key selector). Existing OpenPGP installations need no migration.

Agent-backed Git SSH signing is an opt-in alternative:

runtime:
  signing_format: ssh
  signing_program: /usr/bin/ssh-keygen
  signing_key: SHA256:REPLACE_WITH_EXACT_PUBLIC_KEY_FINGERPRINT
  signing_public_key: /absolute/path/to/guardian-signing-key.pub

signing_key is the exact unpadded SHA-256 fingerprint printed by ssh-keygen -l -E sha256 -f <public-key>. The public-key file must contain exactly one supported key. It must be an absolute, non-symlink regular file, owned by the operator or root, not writable by group or other users, and have one link. Its ancestors must also be trusted. DSA, malformed or multiple keys, RSA keys below 3072 bits, and a fingerprint mismatch fail closed.

The Guardian never reads or stores an SSH private key. Before external poll work it copies the validated public key into a private bounded snapshot and derives a one-key allowed-signers file. Git selects that frozen public key and asks the existing ssh-agent for the matching private operation. The validated agent endpoint must have a private 0700 parent. If a higher macOS system directory is root-owned but group-writable, Guardian hard-links the exact socket inode into the private signing snapshot and verifies both names before use; ordinary mutable ancestors still fail closed. SSH_AUTH_SOCK is passed only to the git commit subprocess—not to Codex, credential helpers, repository fetch/push, signature verification, or focused tests. The resulting commit is verified against the one-key trust file both after creation and immediately before publication.

An existing GitHub SSH signing key can therefore be reused when its private half is already available through the operator’s agent; only its public half and exact fingerprint enter config. Register the public key as a GitHub signing key as usual. On macOS, Keychain can remember the private key, but it must also be available to the launchd-visible agent (for example via ssh-add --apple-use-keychain). Do not place a private-key path, private key, or a literal agent-socket path in Guardian YAML or a launchd property list. Run guardian doctor from the same user/session used by the scheduled job; its real sign-and-verify probe catches an unavailable agent or key before writes are enabled.

Interactive runs may resolve tools from the operator’s PATH. Before staging a LaunchAgent, set runtime.codex_executable, runtime.git_executable, runtime.github_token_command[0], and—only in API-key mode— runtime.codex_api_key_command[0] to actual executable absolute paths. A write mode also requires an absolute runtime.signing_program. These paths must be non-symlink, operator- or root-owned executables with trusted parent directories; interpreted helpers must use an absolute interpreter in their shebang. In particular, an npm shim or script using #!/usr/bin/env node is rejected: point codex_executable at the package’s platform-native Codex binary instead. Every custom credential command must contain exactly one argv item naming the inspected helper. The sole multi-item exception is the exact GitHub CLI command gh auth token. Python, Node, shells, nice, nohup, and other interpreter or dispatcher programs are not accepted as argv[0] with an unchecked helper script in a later argument. A credential helper may itself be an executable script, because Guardian inspects that script and its absolute shebang interpreter chain; the shebang itself must contain only the interpreter path, without flags or other arguments. guardian doctor resolves interactive command names and validates each resulting executable before invoking any of them. guardian install additionally requires absolute configured paths. Every due scheduled run checks the executable and its interpreter chain again before credentials or repository data are used, so a background process cannot silently pick up a replaced binary from a changed PATH.

A model-credential-helper failure in API-key mode or any Codex authentication failure opens the poll’s authentication circuit, as does a GitHub credential-helper or API authentication failure. The circuit stops later repository and model work in that poll. Non-authentication GitHub transport, API, and policy failures fail closed as ordinary repository or candidate failures; later policy-scoped work may still be attempted.

An exhausted ChatGPT allowance, insufficient Codex credits, or a hard API billing quota opens a separate model-capacity circuit. The Guardian does not immediately retry that non-recoverable condition or start model work for later repositories in the poll. It records only a redacted health outcome; inspect the provider account for the reset time or billing decision.

Preflight and first run

localize guardian login --config "$GUARDIAN_CONFIG"
localize guardian doctor --config "$GUARDIAN_CONFIG"
localize guardian run --config "$GUARDIAN_CONFIG"
localize guardian status --config "$GUARDIAN_CONFIG"

doctor makes no monitored-repository or GitHub writes and redacts credential material. It checks the config, state directory and process-lock safety without acquiring an active poll lock, the Codex executable and schema, the dedicated ChatGPT login or API-key helper, GitHub credential helper, GitHub identity, repository visibility, signing setup, and built-in adapters registered in the isolated Guardian process. Target-project adapter compatibility is checked later from the exact base checkout during a run.

In either write mode, every repository must name a top-level publication_actor, and the same read-only probe resolves /user once. That credential’s immutable numeric ID and API type must match every repository’s actor. If a positive publication cap enables nested remediation or prevention draft creation, each enabled nested actor must be the same identity; configurations that would require different actors fail closed. Neither the token nor mutable login is printed.

The publication actor must have GitHub API type User. Use a narrowly scoped personal access token for a dedicated machine user, or another user token that GitHub supports on GET /user. A GitHub App installation access token writes as a Bot but cannot satisfy this user-identity proof, so installation-token publication is intentionally rejected. Supporting it would require a separate installation-identity protocol. This restriction applies only to publication; review feedback from explicitly configured bots remains supported.

For apply-owned-translations and propose-prevention, set runtime.signing_key explicitly (and runtime.signing_public_key for SSH). The doctor creates an ephemeral local commit and proves that exact key can sign and verify with global and system Git config disabled—the same isolation boundary used for Guardian commits. For SSH it also uses the private one-key allowed-signers snapshot and withholds the agent socket from verification. A global user.signingkey is deliberately not accepted. Each operator-run guardian run executes one finite poll. limits.run_timeout_seconds is one elapsed-time budget measured against a monotonic deadline for active work, not a fresh timeout for each repository, request, page, retry, or subprocess. Setup snapshots and recursive workspace scans check the same deadline; GitHub streaming and pagination recheck it between chunks and pages; credential helpers, Codex, prevention tests, signing inspection, and Git subprocesses receive no more than the remaining budget. Expiry stops new work and prevents later remote mutations. Bounded SQLite finalization, recovery bookkeeping, process teardown, temporary-file cleanup, and lease release may finish after expiry so timeout handling cannot strand unsafe or misleading state.

status shows the last completed feedback run, component health, pending feedback revisions and historical hydration retries, aggregate action and remediation lifecycle counts, and current UTC daily model calls (completed plus active or unknown reservations) without printing raw review bodies or secrets. In API-key mode it additionally shows committed API cost (settled cost plus active or unknown reservations).

Failed polls also print a short stderr alert pointing to guardian status. That command shows the latest private failure ID, timestamp and structured diagnostic: poll ID, available source PR/run identity, operation, exit or HTTP status, fixed reason codes and pipeline code locations. At most 32 diagnostics are appended per poll to the existing private audit database. Unknown errors remain unclassified with code locations; raw stderr, exception prose, command arguments, credentials and absolute paths are never stored in these records or copied into public replies. A later failure does not replace the last-success timestamp. Desktop notifications depend on the operator’s scheduler wrapper. Rejected prevention regression proofs record red_green_mismatch with the base and patched outcomes (passed, failed, error, or timed_out) and bounded numeric exit codes. This distinguishes an already-green baseline from a failing candidate or test-runner error without retaining test output. The requirement that tests fail on the base and pass on the candidate is unchanged.

Two bounded, redacted operator worklists expose durable recovery state under the same exclusive poll lock:

localize guardian remediation list --config "$GUARDIAN_CONFIG" --limit 100
localize guardian history-retry list --config "$GUARDIAN_CONFIG" --limit 100

remediation list is the detailed provenance and lifecycle surface. It orders active attempts before terminal local history and explicitly reports when the requested attempt bound or the per-draft coverage bound truncates output. For each bounded result it prints the draft key and branch-identity version; exact target, push, ref, and commit identities; local publication phase; PR number and canonical URL when present; terminal resolution; the latest remote observation and its state/draft/merged/base/time fields; and the exact source PR, policy, revision, coverage reason, linked draft keys, and whether that coverage is currently effective. It never prints review-comment bodies. status intentionally keeps this information aggregated.

remediation quarantine requires --acknowledge-terminal-local-skip and atomically appends an operator_quarantined resolution plus terminal coverage for every exact source linked to the listed local attempt. It is an explicit terminal local skip, not an inference from remote absence, and does not change its remote branch or pull request. history-retry quarantine requires the exact repository name and numeric ID, policy digest, pull ID, PR number, and the same acknowledgement. It is a deliberately permanent source-PR veto under that policy digest: later comments on that PR are ignored while the policy is unchanged, and changing the policy makes the PR eligible again. Neither command edits, closes, reopens, comments on, or otherwise changes GitHub state.

Review at least one real report-only run before moving to prepare. The status command shows aggregate action state, not the prepared key count or a diff. Because the validated checkout is disposable and no patch is retained, use a controlled test PR and the configured deterministic limits before enabling apply-owned-translations; do not treat prepare as a reviewable diff preview.

What can be written

In apply-owned-translations, the Guardian writes only to a PR that still matches every configured ownership constraint. It creates a signed, normal descendant commit and advances the existing head without force. It never creates an unrelated project runner or commits project-specific policy to this pipeline repository.

Every repository in either write mode must configure a top-level publication_actor. It is the exact GitHub actor that publishes ordinary translation commits and authors status comments, identified authoritatively by numeric ID and API type User; its login is display-only audit metadata. This actor is deliberately independent of allowed_pr_authors, which controls which existing PR owners may have their branches advanced, so the publication actor does not gain PR-ownership authority by being configured. The write broker authenticates the credential against the actor before repository and pull-request access, then checks it again around the commit push and status-reply write boundaries. An identity mismatch or mid-operation rotation fails closed.

After the new head is confirmed, it may post one idempotent, bot-marked, commit-linked reply. The shape is deliberately modest:

🤖 Localize Guardian: Applied a validated translation-only correction in the linked commit. The review thread remains open for reviewer confirmation.

The hidden idempotency marker prevents duplicate replies after a crash, but the marker alone is never trusted. Recovery accepts it only when the numeric actor ID/type, full canonical body, comment ID-derived URL, publication evidence, and current PR authority all match. A foreign, altered, or duplicate marker fails closed instead of suppressing a genuine reply. A reply claims only what the deterministic checks and confirmed commit establish.

Closed pull-request backfill and remediation

Closed-PR processing is disabled unless a repository has an explicit closed_pr_backfill block. Each poll finishes the open-PR phase across the configured repositories first. Closed history is then traversed newest first in durable scan cycles. A cycle freezes both its UTC start as an upper bound and its lookback_days cutoff as a lower bound. Every later poll restarts at GitHub page 1 and skips exact pull ID/number pairs in an append-only per-cycle seen set; it does not persist a mutable pagination position as discovery progress. Before reporting the cycle complete, the reader performs a second identity-only traversal and requires it to contain no uncovered eligible pull. This catches page shrink, insertion, and equal-timestamp reordering that the two traversals actually observe.

GitHub’s REST listing is not an atomic snapshot. Completion therefore requires a quiescent, bounded discovery-and-confirmation pass; it is not a claim that no concurrent mutation could occur outside the two observed traversals. A later cycle rechecks the window and can discover such a change.

Each discovery or confirmation traversal is limited to 100 numeric pages and 10,000 list entries. If either traversal cannot reach the frozen cutoff or the end of the list within that ceiling, the poll fails visibly and keeps the cycle incomplete; narrow lookback_days before retrying such a high-volume window. Within that ceiling, while its dependencies succeed, and once the listing is quiescent long enough for confirmation, repeated polls cover the full frozen window even when earlier entries are ineligible or already complete. Hydration remains separately bounded to max_prs_per_poll eligible pull requests for that repository. The strict configuration ranges are 1–3650 days and 1–100 pull requests per poll.

A non-authentication GitHub failure while hydrating one pull is retried up to three times immediately. After the third failure, the error is recorded and the identity is skipped for the rest of the current cycle, so one persistently malformed pull cannot starve older history. The durable pending retry is prioritized on later polls independently of the discovery window until it succeeds or an operator explicitly vetoes it. Authentication failures abort the poll and do not advance the affected work.

The frozen upper bound and lookback cutoff govern discovery of new closed-PR evidence only. At most one durable pending branch-only remediation batch is selected for direct reconciliation in a repository poll. Its exact source pull identities must fit within max_prs_per_poll, but reconciliation uses immutable stored evidence and the exact remote branch/PR identity; it does not claim to rehydrate every source before inspecting an already-created remote artifact. Direct recovery ignores the discovery window, so durable pending work or an already-published branch cannot age out before reconciliation. When a fresh candidate is required, the whole source group enters the durable priority hydration path ahead of ordinary discovery, even when those sources are now older than lookback_days; each source must still match its exact identity, remain closed, and satisfy current policy and trust eligibility.

If any recovery source cannot be hydrated after its three immediate attempts, the whole group is deferred for the rest of the current cycle and no partial candidate is published. This temporary deferral is not the explicit, terminal-local-skip remediation quarantine action. A persistent operator-only conflict likewise gets only this bounded recovery path before its sources advance for the current cycle, allowing older history to continue. A later cycle can reconsider the batch. Malformed, ambiguous, duplicate, or mismatching remote PR metadata fails closed and remains visible to the operator. This deliberately favors publication safety and backlog fairness over automatic liveness.

After a cycle reaches the cutoff or end of the closed list, the next poll starts a fresh cycle at the newest page. This periodic rescan can discover an edit or deletion of exact authorized feedback even when the closed pull request’s head SHA and top-level updated_at did not change. Feedback authority is a point-in-time poll snapshot: an edit or deletion observed by a later scan causes a recheck, but cannot retroactively revoke a remote mutation that has already begun. Untrusted comment churn and other unrelated update noise do not invalidate a completed assessment. Both eligible merged and unmerged closed pull requests are covered; the same configured PR, head-repository, head-owner, branch, reviewer/bot, and locale authorization still applies.

A historical pull request and its review feedback are evidence only. The Guardian materializes historical revisions through a read-only checkout and never applies or publishes a historical branch. It separately captures the configured repository’s exact current base SHA and builds fresh current source and target evidence. A historical correction is actionable only when the reported defect independently exists on that current base and the proposed value passes today’s deterministic localization policy. This rule is the same for merged and unmerged history: an old comment cannot authorize a stale or unrelated change. If the finding is already fixed or otherwise obsolete on the configured current base branch, the Guardian records a terminal no-action checkpoint and publishes nothing. Compatible current-base fixes may share one batch; conflicting proposals for the same target, ambiguous evidence, and other unsafe cases remain deferred.

An authorized, still-valid finding that is uncovered and selected for remediation is published only through a new bot-marked, ready-for-review correction PR against the configured current base. That PR contains a signed commit and links to the closed source PR and validated feedback. The Guardian leaves the historical PR and its branch untouched. This translation correction is separate from any optional pipeline-prevention proposal.

observe and prepare perform no GitHub writes for closed-PR work. observe records the bounded assessment and completion checkpoint. prepare may also validate eligible replacements in the disposable current-base checkout, with the same non-reviewable local outcome described above. Historical recurrence candidates may contribute to prevention analysis in propose-prevention, but that remains subject to the separate prevention policy and publication cap. A nested remediation policy may remain configured while either read-only mode keeps it dormant; changing mode is the authority ceiling and does not require editing the repository policy.

Publishing a historical correction requires all of the following: mode apply-owned-translations or propose-prevention; an explicit nested closed_pr_backfill.remediation policy; a positive max_remediation_drafts_per_run; and the write-mode signing and credential setup. No prevention block is required in apply-owned-translations. Keep max_remediation_drafts_per_run: 0 as the report-only and remediation kill-switch setting; zero is also the schema default. The Guardian combines compatible current findings into at most one remediation batch per repository per poll. max_remediation_drafts_per_run is a separate global per-poll cap shared across repositories; it does not increase model-call demand because the history assessment already produced the candidate.

The remediation policy names one exact numeric-ID push_repository, a push_branch_prefix, and one typed numeric-ID publication_actor. The publication actor must have API type User and must be the same identity as the repository’s top-level ordinary publication actor. It is independent of allowed_pr_authors, which grants authority to advance existing PR branches. The push repository must already appear in allowed_head_repositories, and allowed_branch_globs must contain the literal push_branch_prefix followed by *; a broader pattern alone is not enough. The actual generated branch—the prefix followed by a deterministic 64-character lowercase hexadecimal identity—is checked against the allowlist again at publication time. Use a dedicated ordinary Guardian-owned head scope such as localization/guardian-remediation-*, and ensure the GitHub credential can create the resulting pull request. An unexpected existing ref, repository identity, or branch fails closed.

Every remediation broker session resolves the credential’s authenticated GitHub actor. Its immutable numeric ID and API type must match the configured publication_actor, and any created or recovered pull request must name that same actor as its author and retain the exact generated title and body. The publication actor’s login is a display and audit label only; an actor change, rewritten draft text, or a different allowlisted author fails closed.

For an actionable batch, the Guardian revalidates the exact current target base and push repository, creates a signed commit on the deterministic new branch, and opens a new bot-marked pull request ready for human and automated review. The exact [Localize Guardian bot] title prefix and body text identify it as bot-generated; this marker is not a GitHub label. Before creation it performs a coherent sequential pass: after local preparation it rechecks the destination actor, repository IDs, base SHA, candidate branch, and duplicate-PR state, then revalidates exact source authority as the final remote observation before the POST. GitHub’s independent REST resources cannot provide an atomic snapshot; later changes are detected by the next bounded poll and never make an earlier observation retroactively atomic. It never reopens, edits, or comments on a closed pull request, never advances its branch, and never merges the remediation draft.

Append-only publication phases, the deterministic branch, an embedded evidence marker, and preserved private durable state provide crash recovery without creating a duplicate branch or draft. Recovery requires the canonical GitHub URL, publication actor, head and base identities, candidate commit, maintainer_can_modify: false, exact generated title, and full generated body including the embedded marker, except for the bounded untrusted annotation described above. Current open-draft, open-ready, closed-unmerged, and merged states are accepted when all of that metadata remains exact; malformed or rewritten metadata and ambiguous or duplicate remote identities fail closed. The Guardian never rewrites or reopens the PR.

Each reconciliation appends an exact, not_found, or conflict remote observation to the private ledger. An exact merged observation and its terminal merged resolution are recorded atomically so lifecycle and coverage cannot disagree. not_found means the bounded exact lookup completed without a match; authentication, transport, and malformed-response failures remain failures and never manufacture absence.

An already-created exact PR remains recoverable after ordinary target-base advancement. A correction PR that a maintainer closes without merging is a human veto and continues to cover its exact edits, so the Guardian records the closed lifecycle and does not recreate the same correction unchanged. When a correction is merged, its draft-backed source coverage becomes ineffective. If the same defect later recurs on a newly validated current base, a new coverage generation and a distinct remediation attempt may be created.

New remediation branch identities use version 2 and bind the exact remediation policy digest as well as the attempt’s immutable inputs. This prevents a policy change from colliding with a branch left by an older attempt. Rows migrated from version 1 retain their original identity calculation and remain recoverable. If evidence or the target base moves before a branch-only attempt has an exact PR, the Guardian marks that local attempt abandoned, leaves any remote branch untouched, and durably retries its source group. Fresh validation can then create a distinct attempt; no overwrite or branch deletion is used. Unexpected remote content or identity still defers instead of being adopted.

Deduplication is semantic rather than tied to a comment revision. An exact edit is identified by path, key, current source value, expected target value, and proposed target value; its target identity is the path-and-key pair. An exact edit covered by an open or human-closed-unmerged Guardian correction PR is removed from a mixed batch, while uncovered edits may proceed. Merging that PR ends its draft-backed suppression, allowing a later independently revalidated recurrence to receive a new coverage generation. A different edit aimed at the same target identity conflicts and is deferred instead of opening a competing PR. Pending exact edits proceed only through their grouped, bounded recovery path.

Discovery progress is also append-only and compare-and-swap protected for the exact repository identity and policy digest. A stale concurrent progress writer fails closed. A crash before an exact pull identity is marked seen safely rehydrates it on the next restart-from-page-one pass; immutable completion checkpoints keep already-finished model work idempotent. Completion binds the pull identity, relevant pull and changed-file evidence, canonical current source and target content, exact authorized feedback revisions, authority scope, and policy digest. Editing or deleting an authorized reviewer/bot item causes a recheck after a later scan observes that revision, while unrelated or untrusted comment noise does not. Changes to the Guardian policy or to the trusted pipeline config-and-glossary bundle—whether sourced from the exact current base or the private operator snapshot—also start an independent scan and make prior work eligible for reassessment. Unchanged completed work is skipped within a cycle.

Recurrence and prevention

Repeated feedback can indicate a one-off translation problem, project policy, pipeline validation defect, prompt weakness, or an ambiguous case. The model may classify a recurrence candidate, but it cannot modify the running installation.

With propose-prevention, prevention authoring happens in a separate disposable workspace without GitHub write or signing credentials. A prevention change is eligible only when it is within the configured pipeline paths, cites immutable feedback evidence, and includes a focused regression test that is failing on the base revision and passing with the draft. The controller runs each configured test argv with a minimal, credential-free environment and prepends the exact operator-supplied sandbox_argv_prefix. This field is deliberately a one-item argv containing one absolute, directly invoked sandbox-wrapper executable. Put the confinement policy inside that inspected wrapper; a policy or helper-script path supplied as another unchecked prefix argument is rejected. Before every focused command, a runtime probe must be able to read and write generated paths inside the test workspace while reads and writes of generated paths outside it are denied. It also requires denial of an AF_INET loopback bind and a connection to a live parent loopback canary, plus denial of a filesystem AF_UNIX canary connection when the platform supports it. On Linux it also requires denial of a write-open on the parent cgroup’s cgroup.procs, which would otherwise let a same-user test process leave its recursive kill scope. A failed probe rejects the prevention candidate. This focused probe does not prove the policy’s behavior for every host path or network route; the operator still owns and maintains the OS sandbox policy. The parent Guardian process must be allowed to create those local canaries; a host policy that blocks their creation also fails prevention closed.

A regression proof only shows that the new rule fires on its own fixture. Before publication, the controller therefore also measures the candidate on real data. It copies the real target localization files of the source pull request (the exact head checkout used for assessment, the trusted base source files, the operator pipeline profile, and a controller-computed base-to-head diff) into two fresh snapshots of the prevention base: one with base code and one with the candidate. Inside the same sandbox wrapper, probe, and timeout as the focused tests, the Guardian’s own Python runs each tree’s localize.translation_quality_gate entry point on that data. Closed-PR prevention measures the current base’s real files for the same paths without a diff. The candidate is rejected when it raises semantic plus source-identical findings by more than limits.max_new_real_data_findings (default 5), with or without a new blocking reason. A narrow rule that adds a blocking reason with at most that many new findings, such as one that catches the reported defect, passes, as do fewer or unchanged findings. A missing corpus, gate error, malformed report, or timeout also rejects the candidate. Rejections are recorded like failed regression proofs in the private failure ledger (stage real-data-impact, a reason code, and base and candidate finding counts) and never publish a branch. The sandbox policy must therefore also permit the Guardian’s Python and its installed dependencies. The author prompt states this bound, so narrow rules are preferred.

Exact Git checkouts do not contain ignored project virtual environments. Every focused_test_argv should therefore start with an operator-controlled absolute interpreter or test executable outside the repository. The sandbox policy must permit that executable, the Guardian’s Python used by the probe, their required runtime libraries, and the disposable workspace—without granting broader host or network access.

The Guardian pushes only the bounded signed branch needed to open a pull request ready for review; it does not merge or deploy that proposal. Immediately before a branch push or draft-creation POST, it checks the live poll lease and consumes a non-refundable per-poll publication slot. A lost response therefore does not make that slot available to another prevention mutation. Runtime defaults cap prevention at one draft publication workflow across the entire poll, shared by all repositories and feedback runs. Despite the configuration key’s max_prevention_drafts_per_run name, the counter is not reset per feedback run. limits.max_prevention_drafts_per_day (default 1) adds a durable UTC-day cap, so frequent polling cannot open several prevention pull requests per day. The same boundary records a slot for the candidate in the local ledger before its push or POST. Slots are never refunded: an interrupted or ambiguous publication still counts for that day, while recovery of that same candidate reuses its slot. When the day’s slots are used, new candidates defer before any model call. The report-only example pins the cap to zero. propose-prevention always requires an explicit prevention block for every monitored repository. A zero cap is a prevention-publication kill switch, per run or per day: recurrence candidates are skipped, although that mode still retains the translation-write authority described above.

The prevention target may be a different repository from the monitored translation project. The prevention block pins target and push repositories by full name and numeric ID, the exact target base branch, the push branch prefix, a typed numeric-ID publication_actor, code/test path allowlists, and focused test commands. The actor login is mutable, human-readable audit metadata; the exact numeric ID and GitHub User type grant authority. Every prevention REST session authenticates GET /user and fails closed unless that identity matches, and a created or recovered pull request must have the same author identity. The nested actor must also match the repository’s top-level ordinary publication actor so one poll never changes GitHub identity between write paths. Every block must state private_target_model_opt_in. If the exact target base is private, its code is sent to the authoring model only when that field is true; use false for a public target. The monitored translation repository’s private_repo_model_opt_in governs its review evidence and does not grant consent for a different private prevention target. If both are private, both opt-ins are required.

Prevention policy collections have finite parser and runtime bounds: at most 100 code globs, 100 test globs, 64 focused commands, 256 arguments in each focused command, and exactly one sandbox-wrapper executable in the sandbox prefix. Every string in those collections is at most 4096 UTF-8 bytes, max_changed_files is at most 100, and push_branch_prefix must leave 77 characters for the generated <base-prefix>-<evidence-hash> identity inside a 255-character branch name. The canonical source-policy and maximum test-result attestations must each also fit 512 KiB, so a configuration that combines many individually maximal strings can still fail closed before a model or test starts. Codex output may contain at most 100 recurrence candidates with at most 100 evidence feedback IDs each. Newly generated prevention titles are at most 120 Unicode characters and 256 UTF-8 bytes; bodies are at most 60 KiB. When a human-facing evidence, path, or command list would exceed its section budget, the body includes a deterministic omitted-item count and full-list fingerprint instead of cutting through a Unicode character or Markdown item.

Do not use prevention PRs for project terminology or locale style that belongs in the consuming project’s own config or glossary.

An ordinary prevention-draft failure or exhausted draft slot does not block an otherwise authorized correction on an open translation PR. The signed correction and pending prevention work are recorded separately; later polls retry prevention without repeating the correction. Authentication, quota, lease, source-authority and publication checks still stop work at their respective boundaries. A published correction is not evidence that its prevention PR succeeded: inspect the poll’s prevention failures and deferred counts as well. Private failure diagnostics retain the underlying adapter stage and reason, not raw stderr or credentials.

Prevention recovery also requires the canonical GitHub URL; exact generated title and Guardian-authored body including its marker; exact head, base, and candidate; and maintainer_can_modify: false. Ready-created PRs, untouched legacy open drafts, their one-way draft-to-ready transition, and a terminal close-unmerged from either draft or ready state are accepted. A reopen, redraft, rewritten metadata, or over-bound event history fails closed and is never adopted as Guardian-owned state.

Durable state, budgets, and recovery

The Guardian keeps local SQLite state. Each authorized feedback content change, and each observation of that feedback against a distinct PR head/base revision, becomes a new immutable revision tied to repository, PR, feedback object, author numeric identity, body hash, head SHA, and base SHA. Runs, terminal actions, health, recorded token usage, and recorded cost remain auditable across restarts. Publication phases and marked replies have dedicated reconciliation and deduplication records.

This is not whole-run exactly-once execution. A successful assessment is cached against its exact evidence, head/base revisions, model, and effort before its call completes in the ledger. A process death while a call is in flight leaves that reservation committed. Without a cached result, a later run can repeat an ambiguous model attempt and consume another call slot. In API-key mode it may also incur another charge. Prevention authoring is likewise bounded but is not replayed from an assessment cache. Treat limits as start-call guards and inspect the local ledger after recovery; API-key operators should also compare it with provider billing.

Cache admission and reuse both check feedback IDs, authorized target paths and key existence against the trusted source evidence. A rejected old cache entry is removed with a private audit record; existing cost and call records remain. Fresh invalid assessments use the same configured attempt limit as other model failures, not an additional retry loop. Each started attempt counts toward the daily session cap. Guardian never repairs a model-invented key by guessing its intended spelling, and repeated invalid output cannot publish edits.

After raw_retention_days, raw comment-body rows are logically deleted from the active SQLite tables; their body hash and revision metadata remain. SQLite freelists, WAL files, filesystem snapshots, and backups can retain older bytes, so this setting is not a secure-erasure guarantee. Protect the state directory and apply the operator’s storage-retention policy to it and its backups. Do not publish it.

max_model_calls_per_day is the UTC daily model-call start limit in both authentication modes. Every assessment and prevention-authoring attempt first reserves one durable slot atomically. Completed, active, and unknown calls count; an attempt proved not to have started is cancelled and does not count. The cap limits Guardian starts, but it is not a ChatGPT-plan guarantee: other Codex use shares the operator’s plan allowance, and provider limits remain authoritative. The report-only starter sets the cap to two, enough for one assessment with its single retry; raise it only from observed workload and allowance data.

The configured cap must at least fit every retry for one assessment. In observe, prepare, and apply-owned-translations, it requires:

max_model_calls_per_day >= max_attempts

When propose-prevention has a positive draft cap, the cap must leave capacity for an assessment plus every allowed prevention authoring draft, at every allowed attempt. Here drafts_per_poll is the smaller of max_prevention_drafts_per_run and max_prevention_drafts_per_day:

max_model_calls_per_day >= max_attempts * (1 + drafts_per_poll)

This is a configuration-coherence minimum, not reserved capacity for the whole poll. A poll can discover multiple assessments, and prior or earlier calls on the same UTC day can exhaust the cap; remaining work then defers. Each feedback run separately has a value-edit limit, while the prevention-publication cap is shared across the whole poll.

Only API-key mode enables daily_cost_limit_usd and model_call_reservation_usd. The daily value is the local UTC threshold for starting another API-billed model attempt, not a provider billing cap. Every started attempt gets its own conservative cost reservation. A call already in flight can finish above the threshold, and usage without a reliable price keeps the reservation as unknown spend. Check provider billing separately, especially after model-price changes.

For API-key propose-prevention, the cost limit must cover the same worst-case retry shape:

daily_cost_limit_usd >= model_call_reservation_usd *
                        max_attempts *
                        (1 + drafts_per_poll)

For example, one draft, two attempts, and a $5 reservation require a daily API cost limit of at least $20, plus a call cap of at least four. These are conservative start reservations, not predictions or provider limits.

A crashed or interrupted translation publication or marked reply is reconciled against the fresh PR head and its durable publication record before any retry. Pending prevention publication is reconciled against the exact target base and branch state. Other work may be assessed again after a crash as described above. Never delete the state database merely to make a pending action disappear.

Daily launchd schedule on macOS

Stage the user agent only after a clean manual report-only run:

localize guardian install --config "$GUARDIAN_CONFIG"

The generated launchd property list and wrapper contain absolute paths and no credentials. RunAtLoad plus a conservative StartInterval wakes the wrapper periodically; persistent state records the start of each poll attempt and uses top-level schedule.hour (0-23) and schedule.minute (0-59) to decide whether the once-daily run is due in the machine’s local wall-clock time. The default is 00:00, preserving earlier behavior. A private process lock in the config’s state directory prevents a manual run and scheduler wake from starting overlapping polls; scheduled lock contention exits successfully while a manual caller receives a clear already-running error. This provides catch-up after sleep, logout, or a missed wall-clock time without running the full model workflow every interval. In the default daily mode, a failed scheduled attempt is not retried on the next 15-minute wake; use an explicit manual guardian run after diagnosing it. Manual runs always execute and become that local day’s latest attempt checkpoint.

For responsive open-PR review, opt in in the private configuration:

schedule:
  hour: 0
  minute: 0
  poll_interval_seconds: 900
  max_polls_per_day: 96

Each scheduled wake after the configured local start time can then poll GitHub, at least 900 seconds apart and at most 96 polls per local day. Failed attempts count too; a crash does not lose the checkpoint. Authentication or provider/model capacity circuit failures stop subsequent scheduled attempts for that day; diagnose the cause before an explicit manual retry. These are poll limits, not Codex session limits: unchanged completed feedback does not start another model session. The shared limits.max_model_calls_per_day budget still counts every model retry, and the existing per-run timeouts, bounded model retries, and no-fallback authentication policy remain unchanged. A run with unfinished feedback can retry that work, but completed publications retain their existing idempotency checks.

Responsive scheduled polls attempt the closed-PR backlog only once per local day; later polls concentrate on open PRs and their prevention follow-up. This also means a failed historical publication waits until the next daily history attempt unless the operator explicitly runs guardian run without --scheduled after repair. Manual polls include historical work and count toward the day’s poll checkpoint, but deliberately bypass scheduling gates. A later successful manual recovery clears the responsive scheduler’s circuit checkpoint. Omitting poll_interval_seconds preserves the original once-daily behavior.

install stages the files but does not load the LaunchAgent. The generated runner is an operator-local artifact beside the external Guardian config, not a file to commit to the monitored project or this pipeline repository. If staging fails, installation removes only regular files created by that attempt and preserves pre-existing logs or artifacts. Inspect the printed property-list, wrapper, state, and log paths before loading it explicitly. Run localize guardian status --config "$GUARDIAN_CONFIG" after the first launchd wake. Re-run doctor after CLI, model, authentication, credential, signing, or policy changes. If the ChatGPT session expires or is revoked, run guardian login again before the next scheduled poll.

Operator checklist

Optional translation quality checks

GitHub comments are for reviewers, not a machine-data transport. Guardian keeps its structured findings in its private audit state. Public updates must explain what needs attention or what changed, with useful commit links; they must not contain diagnostic JSON or hidden machine payloads.

Keep bot comments action-first and usually one or two sentences: ask for the specific review or decision needed, or say that no action is needed. Routine internal progress belongs in private logs, not another PR comment.

Set GUARDIAN_QUALITY_REPORT_ENABLED=true in the translator environment to publish one concise, bot-labelled quality summary for a revision with findings. This summary does not grant Guardian authority to edit anything. In each Guardian repository policy, set quality_report_actor: {login: producer-login, id: 12345, type: User} using the actual producer’s numeric GitHub identity. This existing opt-in enables internal deterministic quality checks and retains compatibility with older reports. It is separate from the reviewer whitelist: ordinary comments by that actor confer no review authority.

The initial scope is unexpected source-identical values and disallowed control characters in changed entries. Guardian derives findings from exact base/head checkouts using the trusted brand/ignore policy, rather than relying on a public comment. Findings are bound to the repository, PR, commits, target path, locale, and source/target values and are revalidated before publication. An unreproduced finding is recorded as rejected with insufficient evidence and excluded from model input and recurrence authority. Other verified findings and trusted review feedback in the same poll can still proceed; an invalid-only batch makes no model call and does not read or populate the assessment cache. Source-echo flags do not prove shared-language wording is wrong; the assessment must still distinguish a defect from legitimate identical wording. Placeholder, semantic, glossary and model-failure narratives do not independently authorize edits; those still need trusted review feedback. No arbitrary check-status or PR-body text is consumed as authority.

Internal per-key events let edit limits retain unfinished work. Public dispositions are consolidated in the existing PR summary. Only a durably recorded Guardian commit can carry unresolved evidence forward; an external head change requires fresh evidence. Already-published corrections may supply historical prevention evidence, but cannot authorize another edit. Evidence limits fail explicitly instead of silently dropping findings.

To publish a human-readable check summary for an existing PR without model calls, use an exact local head and the trusted operator configuration. Guardian does not need this comment to perform its internal checks:

python -m localize.guardian.quality_report_publication \
  --repository owner/project --pull-number 123 --expected-head FULL_HEAD_SHA \
  --repo-root /path/to/checkout --input-folder /path/to/checkout/l10n \
  --config /path/to/trusted/config.yaml

The producer rejects modified report target/source files and source differences from the current PR base. Other pending translation batches remain untouched. It posts only the summary and prints separate internal finding/report counts and public comment counts. Rerunning with the same findings at the same revision does not duplicate the comment. An unchanged English value is a candidate for review, not proof of a mistranslation; product names and shared-language terms may correctly remain unchanged.