| name | consensus-loop |
| description | Use when the user wants an unattended multi-perspective consensus engine to resolve repo-owned GitHub issues/PRs end-to-end (design-consensus, implementation, review, merge, release) with Codex workers in isolated worktrees and GitHub-visible status; audit/refactor is only a fallback issue producer when no actionable managed work is open. |
Refactor (iter319/issue-319): Old pattern: 物理拆 REFERENCE.md 后跨平台加载/维护退化.
New principle: 单文件 SKILL.md 用 Controller Contract Index + Detailed reference anchors 分层,禁止重新拆 REFERENCE.md 作为默认修复.
Maintainer directive recognizes this single SKILL.md as the canonical controller contract and detailed reference; use intra-file anchor links.
Refactor (iter1/issue-141): Old pattern: 下游没有 installer 时,装机步骤散落在 README、SKILL statusline 段和 restart helper 段,缺乏从安装 skill 到配置 host.env、调度守护进程、接入 statusLine 的单步 walkthrough。
New principle: Downstream install walkthrough 是唯一装机主段;README 链到 SKILL 锚点,SKILL 内部段落互链;source-regression 锁住单文件链接与必备 surface,bounded scheduler behavior test 锁住 consensus-rnd-cli restart-daemons 不无限阻塞。
Refactor (iter218/issue-218): Old pattern: ensure-project-rules 是 public CLI 默认写 host policy 文件($PROJECT_RULES),违反 skill 无 host 改动权边界
New principle: 改为 read-only check-project-rules probe + patch artifact:probe 只读判 sentinel block,非 current 写 .refactor-loop/runs/ patch 并 fail-closed 不派 actor;删 ensure-project-rules/_atomic_write,不引入 PROJECT_RULES_WRITE_ENABLE。严格按 plan 逐条改。
Consensus R&D Work-Unit Loop — Controller Contract
This SKILL.md is the single controller contract and detailed reference by maintainer directive. It must be enough to run the loop safely on first load while keeping heavy schemas, full templates, command bodies, and recovery runbooks reachable by intra-file anchors.
Use intra-file anchors when a phase needs the detailed body, such as host runtime details; do not force-load unrelated sections.
Controller Contract Index
| Contract | Keep-local invariant | Controller action | Reference anchor | Prompt/script surface |
|---|
| Host config | Loop runtime facts come only from host-owned host.env; skill text remains host-agnostic. | Require non-empty CONSENSUS_RND_HOST_ENV, then source "$CONSENSUS_RND_HOST_ENV" before running actors; fail closed if the locator or required vars are absent. .refactor-loop/ is skill-private runtime/cache/log state, not host production SSOT. | host runtime details | host.env.example, controller-internal ControllerActions |
| GitHub state | GitHub 是系统状态唯一显示面. Maintainer must see current state without local logs. | Post status banners and labels in the same turn as every spawn, completion, consensus, merge, block, or escalation. | status and escalation templates | ControllerActions.post_status_banner, GitHub labels |
| Active controller | 跨设备时 GitHub/已 push git 面只承载一个 ActiveControllerLease; local .refactor-loop is owner-machine cache/log only. Authenticated GitHub actor facts are read-only admission/preflight/display diagnostics after this owner gate, not a second write authority. | Owner may run controller write paths and restart-helper-managed write daemons from restart.py::DAEMON_COMMANDS; non-owner may peek/statusline or restart-daemons noop only. | active controller lease | active_controller.py, github_actor.py, ACTIVE_CONTROLLER_* |
| Pure orchestration | Controller execution may be interactive or #396 wakeup-runner; all reasoning remains in codex workers / consensus gates. phase9-router handles only deterministic design-consensus routes; wakeup-runner consumes only wakeup-plan evidence-bound closed action projection and existing controller helpers. | Never implement product/refactor code in the controller conversation. Ensure implementation, verification, fixing, review, and design-solving codex dispatch is covered by daemon-owned surfaces; direct controller spawn is only the stuck-gated fallback in Daemon-first controller boundary. | controller contract details | consensus-rnd-cli spawn-codex, prompt files |
| Sentinel | Every AI-authored GitHub body ends with a final independent ⟦AI:AUTO-LOOP⟧ line. | Filter AI comments by sentinel and AI banner prefixes; never react to own comments as maintainer input. | sentinel and comment filters | prompts, consensus-rnd-cli comment-monitor |
| Wake source | Every controller session must maintain a persistent daemon-event Monitor bridge. | Arm or confirm the daemon-event Monitor bridge through checked-in consensus-rnd-cli monitor-bridge-filter; before ending a turn, also confirm any in-flight codex task-notification or registered ScheduleWakeup fallback used as the next wake. | wake source rules | Monitor bridge, harness Bash background tasks, ScheduleWakeup fallback |
| First wakeup | Consensus-rnd Phase bootstrap is ordered and mandatory before any normal phase. | Run the Consensus-rnd Phase bootstrap checklist in this file, in order. | daemon command bodies | scripts, host.env |
| Work unit state | The work-unit contract is stable; do not rename, migrate, or wrap it. Root .refactor-loop/state.json is not a contract surface. | Use GitHub labels/comments, clean EXIT=0 logs, prompt artifacts, git topology, and named specialized state artifacts; export WORK_UNIT_ID=$CLUSTER_ID for audit-backed units. | work-unit contract, specialized state artifacts | .refactor-loop/state/*.json, daemon-owned state files |
| Structured consumption | Steady-state controller/daemon paths consume only clean-exit status plus final allowlisted standalone marker/verdict lines, artifact frontmatter, CLI JSON/action fields, and artifact paths. Raw log prose is diagnostic-only. | Do not read, understand, quote, relay, or transcribe worker log prose, review reject prose, judge reasoning, or progress raw tails during normal routing. Raw logs are allowed only for EXIT!=0, stream disconnect/503, stuck/crash, missing/invalid structured artifact, router fallback, or worker self-post failure diagnostics. | structured-consumption boundary, phase routing details | wakeup_plan.py, phase9/router.py, monitors/progress.py |
| Phase routing | Markers route immediately to the next actor in the same wakeup. | Sweep EXIT=0 logs, parse verdict markers only after clean exit, then confirm daemon-owned dispatch evidence for next work if actionable; direct spawn is stuck-gated only. | phase routing details | logs, prompts |
| Operational names | Parsed or cross-agent names are operational interfaces with owner-local fact sources. | Keep each parser/generator in its owner surface; do not add a production registry or whole-repo naming lint. | operational names | router/progress/concurrency/git/controller actions/labels/cli/stages |
| Design consensus | Concrete plans require Consensus-rnd Phase design-consensus multi-solver consensus and meta-judge consensus. | Dispatch minimal, structural, delete solvers; meta-judge returns consensus/converge only; router-owned stalled predicate may route qualifying converge to reflector, with legacy stalled markers read-only compatible. | design-consensus details, ConsensusGateProof | solver-*.md, meta-judge.md, consensus_gate.py |
| Large issue decomposition | Direction consensus only authorizes controller-private IssueDecompositionPlan and child/parent body artifacts; plan apply needs the plan-level judge artifact structured fields, validated plan digest/proof, #191 owner, live parent open/tracking, and helper-owned child-fingerprint idempotency. | Active-controller owner may run the checked-in internal apply helper to create only missing managed child design issues and write exact parent tracking comments; workers, daemons, public CLI, and generic wakeup-plan decompose/status projections do not create decomposition issues. | large issue decomposition | issue_decomposition.py, ControllerActions.apply_issue_decomposition_plan |
| Default issue intake claim | Open, non-PR, not-yet-managed issues use one default claim protocol only after DefaultIssueIntakeAdmission proves active cap, cooldown, upstream-idle, and concrete-work-unit admission. Durable public/protocol names are DefaultIssueIntakeClaim, DefaultIssueIntakeAdmission, DEFAULT_ISSUE_INTAKE_ENABLE, apply_default_issue_intake_claim, and crnd:default-issue-intake-claim; unmanaged is only an internal live predicate. | Active-controller owner may let #396 consume the closed projection and run the checked-in helper to write the earliest-wins claim or loser stop comment and apply the catalog design issue label bundle, after runner revalidation. | default issue intake claim | default_issue_intake_admission.py, default_issue_intake.py, wakeup_plan.py, wakeup_runner.py, ControllerActions.apply_default_issue_intake_claim |
| Floor | Keep $CODEX_FLOOR host-scoped codexes, default 5, hard lower bound 2. | Count only this loop's consensus-rnd-cli spawn-codex processes containing absolute $REPO_ROOT; require daemon-owned dispatch evidence before ScheduleWakeup, with controller direct spawn only as stuck-gated fallback. | concurrency floor details | consensus-rnd-cli concurrency, consensus-rnd-cli peek |
| Labels | Every issue/PR has exactly one phase label and one human label. | Sync labels and banner together; crnd:human:maintainer-decision only after allowed meta-layer routes. | label bootstrap loops | controller-internal ControllerActions, GitHub labels |
| Spawn | Mainline codex spawn uses harness background tasks, not detached nohup. | Use one background task per codex; if detached already happened, preserve work and rely on log sweep plus wake source. | codex invocation details | consensus-rnd-cli spawn-codex |
| Hard rules | All worker prompts inherit controller-level hard rules. | Include scope, git, test, language, and no-scope-creep constraints in every spawned prompt. | hard rules details | prompt templates |
| Language | Source files are English-only; external user-facing artifact language comes from host-owned HOST_WORK_LANGUAGE. README.md + README.zh-CN.md is the only English-canonical public-doc carve-out. No mandatory parallel English section. | Enforce on prompts, GitHub posts, commits, docs, source comments/logs. | language policy details, historical bilingual notes | prompts, docs, commit text |
Main path and fallback producer
The default main path is open actionable catalog-managed GitHub issue/PR resolution. The controller ensures the next-step actor for managed issues and PRs is dispatched through daemon-owned surfaces before starting any producer for new work; direct controller spawn is only the stuck-gated fallback in Daemon-first controller boundary.
issue-driven / Path A is the main-path issue entry surface: create or reuse a concrete GitHub issue, apply the catalog-derived design issue label bundle (crnd:lifecycle:managed, crnd:phase:design-solving, and crnd:human:auto), then let the daemon-owned router/runner/dispatch evidence surface cover Consensus-rnd Phase design-consensus. Historical non-crnd:* issue-entry labels are unmanaged residue and must not be written, queried, normalized, or cleaned up by the loop.
audit remains a stable compatibility producer value and fallback issue producer. It runs only after no open actionable managed issue/PR, queued dispatch, clean marker route, CI/no-gap route, maintainer-comment route, or higher-priority wakeup route exists. Audit produces or updates issues that feed back into the main path; it is not a co-equal entry mode or a parallel R&D lane. Issue-driven work uses the router-injected GitHub issue source snapshot as the work-unit source when no local audit artifact is provided; gh issue view <N> is fallback-only when that snapshot is unavailable. Concrete plans still require Consensus-rnd Phase design-consensus solver consensus and meta-judge consensus before implementation.
Capability boundary: the loop supports bounded repo-owned work expressible as managed GitHub issues or PRs, including feature, bug, documentation, governance, and refactor work. It does not provide arbitrary GitHub administration: no Projects, milestones, assignee management, Discussions, label-taxonomy mutation outside the catalog, issue/PR body edits outside named helpers, tag/release operations outside the release allowlist, or custom lifecycle authority.
Workflow stage display names are sourced from scripts/codex_refactor_loop/workflow_stages.py. The built-in registry remains the default compatibility vocabulary; public built-in stage display must use Consensus-rnd Phase <stage>. Legacy phase9-router and phase9-issue... strings are compatibility command and artifact dialects only.
Operational names
Operational names are names parsed, routed, spawned, inspected, consumed by GitHub/git state, or passed across agents. They are protocol surfaces, not style preferences. Each surface is owner-local: its owner declares the field order, allowed charset, canonical write policy, legacy read/migration policy, behavior tests, and source-regression anchors. Do not add codex_refactor_loop/names.py, check_naming.py, or naming_policy.py as a production fact source; do not copy existing label, command, stage, or route catalogs into a generic registry.
Owner map:
| Owner | Operational names owned | Policy |
|---|
scripts/codex_refactor_loop/phase9/router.py | phase9-issue<N>-r<R>-<role>, solver-issue<N>-r<R>-<role>, meta-judge-issue<N>-r<R>, and design-consensus artifact references | Canonical writer/parser for design-consensus filename identity and artifact references; legacy input is local to the router. |
scripts/codex_refactor_loop/review_fix_dispatch.py | fix-pr<N>-round-<R> review-fix dispatch filename identity | Canonical writer of review-fix prompt, log, and report artifact filename identity. |
scripts/codex_refactor_loop/controller_actions.py + scripts/codex_refactor_loop/reviewer_liveness.py + scripts/codex_refactor_loop/review_gate_selection.py + scripts/codex_refactor_loop/review_evidence_recovery.py | review-pr<N>-<role>-r<R> reviewer prompt/log filename identity, same-head reviewer pending/redispatch facts, GitHub-visible final-sentinel review evidence selection, bounded review-evidence-redispatch recovery keys, and repeated same-head blocker projection | controller_actions.py is the canonical writer for #396 reviewer dispatch prompt/log artifact names; reviewer_liveness.py is the owner-local read model for same-head local pending/terminal/redispatchable reviewer facts, including supervisor-refreshed log heartbeat and matching same-device spawn holder PID from task_spawn_claim.py only as duplicate-dispatch suppression evidence; completed reviewer evidence authority is GitHub-visible same-head final-sentinel comments selected by review_gate_selection.py; planner/runner redispatch uses review_evidence_recovery.py keys (pr, role, head, reason) with cap 2, and same-head repeated review verdict signatures may produce status-only repeated_review_blocker / explicit-approval waits. Cross-head same-PR counts are diagnostic only. Local review artifacts and spawn-task locks are pending/debug inputs only. |
scripts/codex_refactor_loop/monitors/progress.py | progress-comment target extraction for review-pr<N>-<role>-r<R>, fix-pr<N>-<round>, and phase9-issue<N>-r<R>-<role> | Read-only extraction owner only; malformed near-misses return empty and prompt fallback is allowed only when a prompt file exists. |
scripts/codex_refactor_loop/harness_spawn_intent_target.py | read-only stale-target extraction for HARNESS_SPAWN_INTENT fields task_id, intent_id, source, route, and reason matching PR #<N>, issue #<N>, phase9-issue<N>-r<R>-<role>, review-pr<N>-<role>-r<R>, and `fix-pr-(r | round-)` |
scripts/codex_refactor_loop/monitors/concurrency.py | mutable/read-only dispatch task_id prefix classification | Classification owner only; main-readonly prefixes must match exact owner-local forms before $REPO_ROOT cd is allowed. |
scripts/codex_refactor_loop/controller_actions.py and scripts/codex_refactor_loop/git.py | refactor/iter<I>-<cluster> branch/worktree generation | Validate iteration digits and cluster [A-Za-z0-9._-]+ locally; this is duplicated owner-local safety until one implementation is removed or delegated. ControllerActions.safe_worktree() is the canonical branch-provenance writer. Push admission treats provenance as local admission evidence only, not a durable ownership claim; missing provenance fails closed except for bounded legacy canonical implementation heads refactor/iter<N>-issue-<N>, where controller_actions.py may backfill through that same writer only after active-controller ownership, GitHub actor admission, canonical branch/path/current-branch checks, matching issue identity, and live managed issue validation, then rereads provenance before normal owner/worktree/PR-author checks continue. |
scripts/codex_refactor_loop/controller_actions.py, scripts/codex_refactor_loop/sync/dev.py, scripts/codex_refactor_loop/wakeup_plan.py, and scripts/codex_refactor_loop/wakeup_runner.py | rollup/<integration_sha> release rollup heads | Controller-owned singleton rollup head family only; sync/dev.py detects existing open rollups and adopts merged rollups only when the merged head is $INTEGRATION_BRANCH, rollup/<expected origin/$INTEGRATION_BRANCH sha>, or has that exact head SHA; wakeup_plan.py classifies rollup-only actions, wakeup_runner.py admits only rollup-prefixed auto-merge actions, and no generic branch-name registry is introduced. |
scripts/codex_refactor_loop/default_issue_intake.py | DefaultIssueIntakeClaim, DEFAULT_ISSUE_INTAKE_ENABLE, apply_default_issue_intake_claim, crnd:default-issue-intake-claim, and crnd:default-issue-intake-stop | Owner-local #623 default issue intake claim protocol. default is the only public/protocol identity; unmanaged remains an internal live predicate and must not appear as env/action/marker/type naming. |
scripts/codex_refactor_loop/labels.py | crnd:<group>:<slug> labels | Sole canonical label catalog; consumers import/use catalog helpers instead of copying regex truth tables. |
scripts/codex_refactor_loop/cli.py::COMMANDS | public command names and authority tokens | Public command catalog and authority fact source; controller lifecycle primitives stay outside COMMANDS. |
scripts/codex_refactor_loop/workflow_stages.py | workflow stage display names and slugs | Sole built-in stage catalog; HostWorkflowSpec may project host: data without overwriting built-ins. |
HOST_WORKFLOW_SPEC may point at one repo-relative JSON HostWorkflowSpec. Empty or unset keeps built-in behavior. The file is data-only route vocabulary and a seven-surface data-only projection for events, host stages, work-unit kinds, roles, prompt bindings, consensus policies, and issue-intake mappings (events, stages, work_unit_kinds, roles, prompt_bindings, consensus_policies, and issue_intake_mappings). All host-added names must use the reserved host: namespace, and WorkflowInvariantValidator rejects attempts to overwrite built-ins, public compatibility aliases, marker families, producers, or cluster aliases. HostWorkflowSpec grants no lifecycle authority: no command, shell, argv, git, commit, push, merge, close, label mutation, assignee, milestone, import, or executor fields are allowed. It also cannot downgrade consensus: design-consensus-shaped host policy still requires at least three independent solvers, exactly one independent judge, peer-output isolation, and fixed marker families. First-version scope is bounded to status/prompt/intake projection; it is not a DAG executor and does not create public marker aliases. Consensus-rnd Phase design-consensus router direct-spawn-intent ignores host roles, dispatch, and consensus_policies completely; its allowlist is always the built-in minimal/structural/delete solver triplet plus built-in judge.
Host 配置(通用化注入点)
These variables are injected by the host project. The skill must not hardcode project facts.
CONSENSUS_RND_HOST_ENV locates the host-owned host.env loop runtime injection file; it is the only runtime locator for host facts and is not a host production config schema.
Host env surface matrix
This matrix is the only manually maintained host.env contract. host.env.example is a copyable template view; tests derive its expected exports, categories, defaults, and prompt placeholders from this table.
| Variable | Category | Owner | Default/example | Missing/empty behavior | Consumer | Test owner |
|---|
$CONSENSUS_RND_HOST_ENV | required | HostEnvLocator | repo-relative host-owned path, e.g. .config/consensus-rnd/host.env | required for host fact loading; when set it must be repo-relative or repo-contained absolute; missing, empty, unreadable, or invalid locators fail closed; no .refactor-loop/host.env fallback is read; it is not host production config schema; daemon-status fingerprint/currentness projection fails closed before inventory/fingerprint reads when missing or invalid | LoopContext locator, daemon-status admission | test_loop_context.py, test_daemon_status.py, test_host_env_surface_matrix.py |
$REPO_ROOT | required | LoopContext | host absolute repo path | fail closed; do not infer from cwd unless an explicit read-only fallback test allows it | LoopContext | test_loop_context.py |
$GH_REPO_SLUG | required | LoopContext | OWNER/REPO | fail closed for GitHub operations when absent or not OWNER/REPO; preferred slug | LoopContext, release-gate | test_loop_context.py, test_auto_release_gate.py |
$GH_OWNER | compatibility | LoopContext | optional owner fallback | noop when $GH_REPO_SLUG is present; used only with $GH_REPO_NAME compatibility construction | LoopContext | test_loop_context.py |
$GH_REPO_NAME | compatibility | LoopContext | optional repo-name fallback | noop when $GH_REPO_SLUG is present; used only with $GH_OWNER compatibility construction | LoopContext | test_loop_context.py |
$BUILD_CMD | required | LoopContext | host shell command string | fail closed for build-required work; callers must execute with bash -lc "$BUILD_CMD" after sourcing host.env | prompt templates | test_skill_entrypoint_contract.py |
$TEST_CMD | required | LoopContext | host shell command string | fail closed for test-required work; callers must execute with bash -lc "$TEST_CMD" after sourcing host.env | prompt templates | test_skill_entrypoint_contract.py |
$INTEGRATION_BRANCH | required | sync helpers | host integration branch name | fail closed when missing or empty; never infer host branch topology or default to a product branch | sync helpers, release-gate | test_sync_dev.py, test_auto_release_gate.py |
$REVIEW_BASE_BRANCH | required | sync helpers | host review-base branch name | fail closed when missing or empty; never infer host branch topology or default to a product branch | sync helpers, release-gate | test_sync_dev.py, test_auto_release_gate.py |
$PROJECT_RULES | defaulted | LoopContext | CLAUDE.md | default to CLAUDE.md as host-owned read-only prompt/bootstrap evidence; non-current fixed points produce a patch artifact and fail closed | LoopContext, prompt templates | test_ensure_project_rules_fixed_points.py |
$RELEASE_AUTO_ENABLE | defaulted | release-gate | false | false or empty exits 0 with noop reason and writes no release decision artifact | release-gate | test_auto_release_gate.py, test_release_gate_module.py |
$HOST_GITHUB_RELEASE_REQUIRED_CHECKS | defaulted | release required-check projection | host-owned comma-separated exact check-run names, e.g. ci,lint,typecheck | comma-separated exact GitHub check-run names; empty has no effect for non-release hosts, but RELEASE_AUTO_ENABLE=true with empty/missing fails closed with missing_host_required_release_checks | release-gate, ReleasePublishPreflight, ReleasePublisher | test_required_release_checks.py, test_auto_release_gate.py, test_release_publish_preflight.py |
$UPDATE_CHECK_ENABLE | optional-noop | update-check probe | false | false or empty exits 0 with noop reason and writes disabled update-check state | update-check, restart-daemons | test_update_check.py, test_restart_daemons.py |
$UPDATE_CHECK_INTERVAL_SECONDS | defaulted | update-check probe | 21600 | default to 21600 seconds; fresh local update-check state is reused for manual probes | update-check, concurrency snapshot projection | test_update_check.py, test_concurrency_monitor_snapshot.py |
$UPDATE_CHECK_TIMEOUT_SECONDS | defaulted | update-check probe | 5 | default to 5 seconds for GitHub release/tag reads; failures write unknown state and never block restart | update-check | test_update_check.py |
$RUNTIME_RETENTION_ENABLE | optional-noop | RuntimeRetention | false | false or empty exits 0 with noop reason; true runs same-inode compaction for pending-events and concurrency-alert logs, planner-proven stale worktree remove/prune, and log-EXIT= proven spawn-task lock unlink; legacy generated_files entries are ignored with no delete authority | runtime-retention, restart-daemons | test_runtime_retention.py, test_host_env_surface_matrix.py |
$AUDIT_FALLBACK_ENABLE | optional-noop | wakeup-plan audit fallback | false | missing, empty, false-like, or invalid values disable audit fallback spawn actions without blocking ordinary plan output; true-like values true, 1, yes, or on allow hard-gate audit fallback prompt and pending-marker creation | wakeup plan | test_wakeup_plan.py, test_host_env_surface_matrix.py |
$DEFAULT_ISSUE_INTAKE_ENABLE | defaulted | DefaultIssueIntakeClaim | true | missing, empty, or true-like values keep default issue intake claim enabled; false-like values false, 0, no, or off disable the #623 claim projection and helper before any GitHub side effect | wakeup-plan, wakeup-runner, controller actions | test_default_issue_intake.py, test_wakeup_plan.py, test_wakeup_runner.py, test_host_env_surface_matrix.py |
$DEFAULT_ISSUE_INTAKE_ACTIVE_DESIGN_CAP | defaulted | DefaultIssueIntakeAdmission | 3 | missing, invalid, or negative defaults to 3; #623 claim projection and runner execution are admitted only when live managed design-solving issue count is below this cap | wakeup-plan, wakeup-runner | test_default_issue_intake_admission.py, test_wakeup_plan.py, test_wakeup_runner.py, test_host_env_surface_matrix.py |
$DEFAULT_ISSUE_INTAKE_CLAIM_COOLDOWN_SECONDS | defaulted | DefaultIssueIntakeAdmission | 3600 | missing, invalid, or negative defaults to 3600; #623 claim projection and runner execution wait while the skill-private last-claim cooldown remains active | wakeup-plan, wakeup-runner, default issue intake helper | test_default_issue_intake_admission.py, test_default_issue_intake.py, test_wakeup_plan.py, test_wakeup_runner.py, test_host_env_surface_matrix.py |
$RELEASE_AUTO_MIN_MERGES | defaulted | release-gate | 1 | default to 1 recent merge for stability scoring | release-gate | test_auto_release_gate.py |
$RELEASE_AUTO_MIN_INTERVAL_HOURS | defaulted | release-gate | 24 | default to 24 hours since last release decision | release-gate | test_auto_release_gate.py |
$RELEASE_ROLLUP_MIN_COMMITS | defaulted | sync helpers | 1 | default to 1 integration-ahead commit before release-rollup pending event | sync helpers | test_sync_dev.py |
$RELEASE_ROLLUP_COOLDOWN_SECONDS | defaulted | sync helpers | 21600 | default to 21600 seconds before duplicate release-rollup event for the same integration SHA | sync helpers | test_sync_dev.py |
$ROLLUP_AUTO_MERGE | defaulted | rollup-autonomous-merge | auto | missing, empty, auto, or true-like values enable CI-only squash merge for singleton rollup PRs; manual or false-like values leave the rollup PR open for human review/merge; invalid values fail closed to wait without merge | wakeup-plan, wakeup-runner, controller actions | test_controller_actions.py, test_wakeup_plan.py, test_wakeup_runner.py, test_host_env_surface_matrix.py |
$CI_GUARDS | optional-noop | prompt templates | empty or host guard script | empty skips with reported noop reason guards skipped: CI_GUARDS unset | prompt templates | test_skill_entrypoint_contract.py |
$CODEX_FLOOR | defaulted | concurrency floor | 5 | missing or invalid defaults to 5; values below hard min 2 clamp to 2 | concurrency monitor, wakeup plan | test_concurrency_monitor.py, test_wakeup_plan.py |
$STALE_REVIVAL_HOURS | defaulted | stale revival | 3 | missing, invalid, or non-positive defaults to 3 hours; converts to seconds and gates re-triggering stuck managed work whose blocking local evidence (e.g. a redispatchable implement log) is older than the threshold | wakeup plan | test_wakeup_plan.py |
$META_ESCALATION_STUCK_HOURS | defaulted | repository stalled meta-reflector | 3 | missing, invalid, or non-positive defaults to 3 hours; effective threshold is max(META_ESCALATION_STUCK_HOURS, STALE_REVIVAL_HOURS) so repository-stalled evaluation is not earlier than ordinary stale revival | wakeup plan | test_wakeup_plan.py, test_host_env_surface_matrix.py |
$ACTIVE_CONTROLLER_DEVICE_ID | optional-noop | active-controller lease | empty | empty means default single-device local-owner noop; multi-device opt-in requires a stable per-device id on every upgraded device | active_controller, restart/concurrency/router/comment/progress/dev-sync/controller actions | test_active_controller_lease.py, test_host_env_surface_matrix.py |
$ACTIVE_CONTROLLER_TTL_SECONDS | defaulted | active-controller lease | 1800 | missing or invalid defaults to 1800; owner renews before expiry; expired lease may be acquired by another device | active_controller | test_active_controller_lease.py, test_host_env_surface_matrix.py |
$MANAGED_WORK_SNAPSHOT_TTL_SECONDS | defaulted | ManagedWorkSnapshot | 300 | default to 300 seconds; fresh read-only snapshot is reused for open managed work discovery | managed_work_snapshot, wakeup plan, phase9-router, concurrency monitor, comment-monitor | test_managed_work_snapshot.py, test_host_env_surface_matrix.py, test_skill_reference_anchors.py |
$MANAGED_WORK_SNAPSHOT_STALE_MAX_SECONDS | defaulted | ManagedWorkSnapshot | 900 | default to 900 seconds; stale cache may be reused under low GraphQL headroom or transient fetch failure, otherwise discovery returns loaded_ok=false and callers fail closed/noop | managed_work_snapshot, wakeup plan, phase9-router, concurrency monitor, comment-monitor | test_managed_work_snapshot.py, test_host_env_surface_matrix.py, test_skill_reference_anchors.py |
$PHASE9_ROUTER_INTERVAL_SECONDS | defaulted | restart-daemons | 120 | restart helper injects 120 seconds into the phase9-router daemon command; direct daemon invocations may use the CLI interval flag | restart-daemons, phase9-router | test_restart_daemons.py, test_host_env_surface_matrix.py |
$WAKEUP_RUNNER_INTERVAL_SECONDS | defaulted | restart-daemons | 60 | restart helper injects 60 seconds into the wakeup-runner daemon command; direct daemon invocations may use the CLI interval flag. Host overrides remain allowed; default log-sweep completion detection worst case is about 60 seconds. | restart-daemons, wakeup-runner | test_restart_daemons.py, test_host_env_surface_matrix.py |
$PATROL_INSPECTOR_ENABLE | optional-noop | patrol-inspector | true | false or empty exits 0 with disabled patrol state and no patrol-owned issue publication; the copyable template enables the #541 narrow helper, but host-owned explicit false/empty keeps it off | patrol-inspector, restart-daemons | test_patrol_authority.py, test_host_env_surface_matrix.py |
$PATROL_INSPECTOR_INTERVAL_SECONDS | defaulted | patrol-inspector | 7200 | default to 7200 seconds for the explicit patrol-inspector daemon command; this is a loop runtime injection knob, not host production SSOT | patrol-inspector | test_patrol_inspector.py, test_host_env_surface_matrix.py |
$PATROL_INSPECTOR_MAX_FINDINGS | defaulted | patrol-inspector | 25 | default to 25 findings per patrol tick; invalid, missing, or non-positive values use the default | patrol-inspector | test_patrol_inspector.py, test_host_env_surface_matrix.py |
$CONTROLLER_TICK_SUPERVISOR_ENABLE | optional-noop | ControllerTickSupervisor | false | false or empty keeps the legacy restart-helper-managed daemon list unchanged; true opts into the narrow supervisor target while LegacyDaemonModeGuard still rejects same-target legacy/supervisor overlap | supervisor, restart-daemons | test_controller_tick_supervisor.py, test_restart_daemons.py |
$COMMENT_MONITOR_INTERVAL | defaulted | comment-monitor | 30 | default to 30 seconds; higher values lower fixed search cost while unchanged updatedAt items skip comments queries | comment-monitor | test_comment_monitor.py |
$COMMENT_MONITOR_LOOKBACK | optional-noop | comment-monitor | empty or YYYY-MM-DD / updated:>=YYYY-MM-DD search fragment | empty adds no lookback filter; non-empty is limited to a GitHub updated: search qualifier and must not change labels, ownership, or write behavior | comment-monitor | test_comment_monitor.py, test_host_env_surface_matrix.py |
$HOST_HOLISTIC_STATUS_ENABLE | optional-noop | global-dashboard-status-card | false | false or empty exits 0 with noop reason; true only enables the progress-reporter issue-comment PATCH subpath after #191 owner, interval, hash, and headroom gates | progress-reporter, holistic-status | test_holistic_status.py, test_progress_reporter.py, test_host_env_surface_matrix.py |
$HOST_HOLISTIC_STATUS_ISSUE_NUMBER | optional-noop | global-dashboard-status-card | empty host issue number | required only when $HOST_HOLISTIC_STATUS_ENABLE=true; empty or non-numeric disables the GitHub sync path without creating comments | progress-reporter | test_progress_reporter.py, test_host_env_surface_matrix.py |
$HOST_HOLISTIC_STATUS_COMMENT_ID | optional-noop | global-dashboard-status-card | empty existing issue-comment id | required only when $HOST_HOLISTIC_STATUS_ENABLE=true; empty or non-numeric disables the GitHub sync path; the writer may PATCH this exact issue comment only and must not create a comment | progress-reporter | test_progress_reporter.py, test_host_env_surface_matrix.py |
$HOST_HOLISTIC_STATUS_INTERVAL_SECONDS | defaulted | global-dashboard-status-card | 600 | default to 600 seconds; unchanged rendered hash skips PATCH and only refreshes local progress-reporter bookkeeping | progress-reporter | test_progress_reporter.py, test_host_env_surface_matrix.py |
$HOST_REFACTOR_COMMENT_POLICY | defaulted | prompt templates | none | missing/empty/default normalizes to none; rationale belongs in external artifacts; explicit self-doc-comment is downstream compatibility opt-in and must obey source English-only; any other value is invalid and fail-closed | prompt templates | test_host_env_surface_matrix.py, test_refactor_comment_policy_prompt_contract.py, test_source_language_policy.py |
$HOST_WORK_LANGUAGE | defaulted | work language policy | en | single external user-facing artifact language fact; allowed values are en and zh; missing/empty/default normalizes to en; invalid values fail closed; explicit zh preserves Chinese working artifacts | prompt templates, GitHub body renderer, controller templates | test_host_env_surface_matrix.py, test_work_language_policy.py, test_github_body_renderer.py |
$SOURCE_GLOBS | optional-noop | review prompts | host source glob hints | empty means review from actual diff and project evidence; do not invent host source layout | review prompts | test_host_env_surface_matrix.py |
$MAINTAINER_WHITELIST | conditional-fail-closed | comment-monitor | host GitHub handles | optional for hosts without comment-monitor/direct-mention intake; when that surface runs, empty fails closed | comment-monitor | test_comment_monitor.py |
$HOST_TEST_FILE_GLOBS | prompt-empty-infer | prompt templates | empty | infer from existing tests; fail closed if unsafe to locate writable tests | prompt templates | test_host_env_surface_matrix.py |
$HOST_TEST_NAMING_RULE | prompt-empty-infer | prompt templates | empty | mirror existing tests; do not assume suffix, extension, or framework | prompt templates | test_host_env_surface_matrix.py |
$HOST_COMMENT_RULE | prompt-empty-infer | prompt templates | empty | match surrounding file style or mark not applicable; do not invent a host comment syntax | prompt templates | test_host_env_surface_matrix.py |
$HOST_CODE_FENCE_LANG | prompt-empty-infer | prompt templates | empty | omit language tag; do not invent a host language default | prompt templates | test_host_env_surface_matrix.py |
$HOST_PROTO_POLICY | prompt-empty-infer | prompt templates | empty | treat schema/protocol checks as diff/project-rule driven only; do not invent protobuf or schema defaults | prompt templates | test_host_env_surface_matrix.py |
$HOST_ARCHITECTURE_GREP_CHECKS | prompt-empty-infer | prompt templates | empty | use $PROJECT_RULES, $SOURCE_GLOBS, $CI_GUARDS, and diff evidence only | prompt templates | test_host_env_surface_matrix.py |
$HOST_WORKFLOW_SPEC | optional-noop | WorkflowSpecLoader | empty or repo-relative JSON | empty/unset keeps built-in behavior; non-empty validates exactly seven data-only projection surfaces: events, stages, work_unit_kinds, roles, prompt_bindings, consensus_policies, and issue_intake_mappings; phase9 direct-spawn-intent ignores host roles/dispatch/policies and keeps the built-in allowlist | workflow_spec, triage, wakeup plan, controller templates | test_host_workflow_spec.py, test_skill_reference_anchors.py, test_phase9_router_package.py |
Prompt templates reference these fields as ${HOST_*} placeholders so normal host.env sourcing plus render_template/envsubst injects them at prompt construction time. Do not add aliases for the rejected Set B names.
$HOST_REFACTOR_COMMENT_POLICY controls only refactor-history self-documentation source-comment semantics. Missing, empty, or default policy is none, which rejects Old/New refactor-history source comments and keeps rationale in external artifacts. Explicit self-doc-comment is a downstream compatibility opt-in; ${HOST_COMMENT_RULE} only supplies comment syntax in that mode and does not override source English-only.
$HOST_WORK_LANGUAGE controls only external user-facing artifact language. Source files, sentinel, markers, labels, schema fields, command names, file paths, protocol vocabulary, artifact filenames, and historical artifacts are not translated.
Host config rules:
host.env is the only loop runtime fact injection point. It is not host production configuration schema.
GH_REPO must not be exported as a bare repo name; use GH_REPO_SLUG.
CI_GUARDS is optional. Use [ -n "${CI_GUARDS:-}" ] before invoking it and report guards skipped: CI_GUARDS unset when absent.
- Source host-owned
$CONSENSUS_RND_HOST_ENV before daemon or codex supervision commands; if unset, fail closed and ask the host to set the explicit locator.
$BUILD_CMD and $TEST_CMD are shell command strings. They may contain cd, &&, pipes, and host script invocations; callers must run bash -lc "$BUILD_CMD" / bash -lc "$TEST_CMD" or an equivalent sourced shell invocation, never split them into argv.
- Detailed daemon start examples live in daemon command bodies, including the
bash -c 'test -n "${CONSENSUS_RND_HOST_ENV:-}" && source "$CONSENSUS_RND_HOST_ENV" && exec' pattern and why env $(grep ...) is unsafe.
- Runtime scripts must consume host.env through LoopContext or the shared parser in context.py; root host.env and unlisted aliases such as INTEGRATION, REVIEW_BASE, and WORKTREE are not compatibility inputs.
$REPO_ROOT/${PROJECT_RULES:-CLAUDE.md} is host-owned read-only evidence. consensus-rnd-cli check-project-rules may inspect the sentinel block and write .refactor-loop/runs/project-rules-fixed-point.patch; it must never apply host policy edits.
- Multi-device mode is safe only after all devices run a version that honors the #191 active-controller lease. Mixed old/new versions are not safe for multi-device mode.
- The active-controller lease ref is a code-owned singleton constant, not a host scope. Host env must not define alternate active-controller refs.
.refactor-loop/ is the skill-private runtime home for cache/log/state/prompt/run artifacts. Host production facts, branch topology, durable ledger authority, and host-owned config SSOT must live in host-owned config/rules/artifacts, not in .refactor-loop/.
Skill Root Contract
<skill-root> means the installed skills/consensus-loop directory containing this SKILL.md, scripts/consensus-rnd-cli spawn-codex, and prompts/. Runtime scripts self-locate from their own file path; CODEX_REFACTOR_LOOP_SKILL_ROOT is optional and only for wrappers or nonstandard packaging. If that override is set but invalid, scripts fail closed instead of falling back to .claude/skills.
Detailed path examples and host installation variants stay in the detailed reference section of this SKILL.md; the controller-contract section keeps only self-location invariants.
Downstream install walkthrough
This walkthrough is the only downstream install runbook for consensus-loop. It documents existing checked-in surfaces only: plugin or copy install, loop runtime fact injection through host-owned host.env located by CONSENSUS_RND_HOST_ENV, user-level cron or launchd calling consensus-rnd-cli restart-daemons, Claude Code statusLine pointing at the read-only consensus-rnd-cli statusline, and uninstall/rollback.
The skill must not modify a host repository's .git config, CI config, or policy files. It must not add installer scripts, host runtime installers, statusline installers, or a root INSTALL.md. Loop runtime facts come only from host-owned host.env; CONSENSUS_RND_HOST_ENV locates that file and is not a second host production config schema.
Install the skill
Use the platform plugin mechanism when available. For direct copy install, copy the checked-in skill directory into the agent's skills directory:
mkdir -p ~/.claude/skills
cp -R skills/consensus-loop ~/.claude/skills/consensus-loop
When the skill is installed by a plugin, use that installed <skill-root> path. When it is copied, <skill-root> is the copied consensus-loop directory.
Inject host facts
From the host repository root:
mkdir -p .config/consensus-rnd
cp <skill-root>/host.env.example .config/consensus-rnd/host.env
$EDITOR .config/consensus-rnd/host.env
export CONSENSUS_RND_HOST_ENV=.config/consensus-rnd/host.env
Fill the host-owned host.env according to the Host env surface matrix: required values must be set, defaulted values may keep their template defaults, optional/noop values may stay empty, and conditional fail-closed surfaces such as MAINTAINER_WHITELIST are required only when their surface is enabled. The optional HOST_* language-policy variables are empty by default and may stay empty unless the host has explicit policy text to inject. CONSENSUS_RND_HOST_ENV must point at this host-owned file before loop runtime commands run.
Greenfield host bootstrap path
For a greenfield host, the maintainer must first create a minimal repository skeleton that can build and test without consensus-loop. Run the host-owned build and test commands successfully in the main checkout before enabling issue-driven / Path A.
Only after that baseline is green, configure the host-owned .config/consensus-rnd/host.env from host.env.example. BUILD_CMD and TEST_CMD must be self-contained shell command strings that also work in a fresh isolated $REPO_ROOT/.worktrees/<id> checkout after CONSENSUS_RND_HOST_ENV is sourced. If ignored dependencies such as node_modules/ are absent in that worktree, the commands must prepare them themselves, for example npm ci --no-audit --no-fund && npm run build and npm ci --no-audit --no-fund && npm test.
This is documentation-only bootstrap guidance. It does not add a setup CLI, installer, Node-specific environment variable, runtime preinstall hook, README command matrix, host .git or CI edits, or host write authority. Once the minimal skeleton builds and tests in both the main checkout and a fresh isolated worktree, enter the normal issue-driven / Path A flow.
GitHub workflow portability checklist
#104 setup is folded into this skill's existing owner surface. It may only generate or fill host-owned .config/consensus-rnd/host.env, a repo-relative JSON file named by HOST_WORKFLOW_SPEC, and optional repo-relative prompt or body binding files referenced from that JSON. It must not create a standalone setup skill or a second protocol owner.
Allowed host artifacts:
.config/consensus-rnd/host.env with CONSENSUS_RND_HOST_ENV=.config/consensus-rnd/host.env.
- A repo-relative HostWorkflowSpec JSON with exactly seven data-only surfaces:
events, stages, work_unit_kinds, roles, prompt_bindings, consensus_policies, and issue_intake_mappings.
- Optional repo-relative prompt/body binding files referenced by HostWorkflowSpec.
Forbidden setup actions: no host .github edits, no label mutation, no issue/PR mutation, no branch-protection probing or edits, no git mutation, no branch creation, and no merge/close side effects. Future #357 interactive configuration may guide a maintainer through the same contract, but it must output these same host-owned artifacts rather than owning a new setup protocol.
Guided GitHub consensus workflow setup
When a host user asks for guided setup, do not add a renderer, CLI command, setup skill, installer, template directory, or root install document. Follow this walkthrough and write advisory artifacts by hand under .refactor-loop/runs/github-workflow-setup/<timestamp>/:
host-env.patch.md: derive a suggested host-owned .config/consensus-rnd/host.env patch from the Host env surface matrix and host.env.example; include the explicit CONSENSUS_RND_HOST_ENV=.config/consensus-rnd/host.env locator setup.
labels-plan.json: derive the label plan from scripts/codex_refactor_loop/labels.py; do not run gh label create, gh label edit, or gh label delete.
scheduler.md: point at the existing cron/launchd consensus-rnd-cli restart-daemons examples below; do not install a scheduler.
statusline.json: point at the existing read-only consensus-rnd-cli statusline; do not write Claude Code settings.
host-workflow-spec.json: optional data-only HOST_WORKFLOW_SPEC draft when the host needs workflow invariants; use the existing workflow_spec.py / WorkflowInvariantValidator contract, host: namespace entries, repo-relative paths, and no command, git, label, merge, or lifecycle fields.
walkthrough.md: summarize what the host still needs to review and apply.
Do not produce summary.json or host-workflow-spec.example.json. These artifacts are advisory only: they must not modify host .git, .github, CI, policy, branch protection, GitHub labels, issues, PRs, commits, pushes, merges, closes, tags, releases, installers, settings, or any lifecycle surface.
Keep existing daemons alive
The controller's periodic wakeup is the primary daemon supervisor: each wakeup reads daemon-status --json and runs restart-daemons only when a daemon is stale or dead, so a running controller session needs no OS scheduler. OS cron/launchd is an optional outer keepalive only for unattended operation with no controller session (to restart wrappers after death/reboot/sleep when no session is active). A stable loop rarely needs restart at all; the goal is stable skill code so a host can run-and-use it without long-term babysitting. Activation needs Claude Code; steady-state with stable code is self-sustaining through the controller's periodic wakeup.
Human host operator runbook: optionally install exactly one user-level scheduler if the host wants unattended OS-level daemon keepalive without a controller session. This skill does not write, load, unload, or delete cron entries or LaunchAgent plists. Agents, skills, and controller sessions also must not write, edit, load, unload, bootstrap, delete, or install cron entries, LaunchAgent/LaunchDaemon plists, watchdog processes, or any OS-level auto-restart surface; the host operator owns those OS-level actions. The scheduler entry's only loop action is to call the existing checked-in consensus-rnd-cli restart-daemons helper after setting a non-empty CONSENSUS_RND_HOST_ENV and running source "$CONSENSUS_RND_HOST_ENV". That preserves values with spaces and keeps all loop runtime facts in the host-owned env file.
Existing scheduler entries must be updated to set CONSENSUS_RND_HOST_ENV to the host-owned file; .refactor-loop/host.env is not a runtime fallback. When host.env path or contents change, restart-daemons detects the changed DaemonLaunchFingerprint and reloads helper-managed daemons so they do not keep a stale environment.
Cron example:
*/5 * * * * cd /abs/path/to/host-repo && bash -lc 'export CONSENSUS_RND_HOST_ENV=.config/consensus-rnd/host.env; source "$CONSENSUS_RND_HOST_ENV" && exec python3 <skill-root>/scripts/consensus-rnd-cli restart-daemons' >> .refactor-loop/logs/restart-cron.log 2>&1
launchd user LaunchAgent example for a human host operator only. Replace com.example.consensus-rnd.restart-daemons with a host-owned label, write it manually to ~/Library/LaunchAgents/<label>.plist, then run launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/<label>.plist; unload with launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/<label>.plist; delete by removing that plist after unload. Do not add a second watchdog or installer.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>com.example.consensus-rnd.restart-daemons</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-lc</string>
<string>cd /abs/path/to/host-repo && export CONSENSUS_RND_HOST_ENV=.config/consensus-rnd/host.env && source "$CONSENSUS_RND_HOST_ENV" && exec python3 <skill-root>/scripts/consensus-rnd-cli restart-daemons >> .refactor-loop/logs/restart-cron.log 2>&1</string>
</array>
<key>StartInterval</key><integer>300</integer>
</dict>
</plist>
The helper remains the existing scheduler-backed anti-stop surface. Cron/launchd is an optional outer keepalive only for unattended operation with no controller session, covering wrapper death, boot/post-wake repair, fingerprint or host.env reload, duplicate cleanup, and #191 owner refresh while no session runs; when a controller session runs the loop, its periodic wakeup performs this supervision instead. A live restart-managed wrapper may self-heal only its own static-allowlist child by reading the actor-owned heartbeat, terminating that child on exit or malformed/future heartbeat immediately, or on missing/stale numeric heartbeat only after the current child has had one heartbeat freshness window to write its own tick, then restarting the same resolved command after at least one wrapper poll interval. It has no lifecycle authority: it must not commit, push, merge, label, create, close, or edit issues/PRs.
For operator inspection, read daemon state with python3 <skill-root>/scripts/consensus-rnd-cli daemon-status --json. If the projection reports stale/dead owner daemons, run the existing scheduler command above or python3 <skill-root>/scripts/consensus-rnd-cli restart-daemons to repair/reload; do not use a separate start/stop/restart/reload verb.
Add the Claude Code status line
Set Claude Code statusLine manually to the checked-in read-only script:
{
"statusLine": "python3 /abs/path/to/skills/consensus-loop/scripts/consensus-rnd-cli statusline"
}
Use the installed python3 <skill-root>/scripts/consensus-rnd-cli statusline path. This is not an installer; it does not edit settings for the user.
Uninstall or rollback
Remove the user-level cron entry or unload/delete the LaunchAgent plist, remove the Claude Code statusLine setting, stop any running helper-managed daemon wrappers if needed, and remove the copied skill directory only if it was installed by direct copy. Keep or delete the host repository's host-owned host.env according to the host's own rollback policy.
Named runtime exception — autonomous release gate(per #56)
Authorization mirror: skills/consensus-loop/authorizations/runtime-exceptions.md#autonomous-release-gate-56. It records META_JUDGE_DONE:consensus:A-with-host-opt-in-as-gate: autonomous release decision after one host opt-in gate. $RELEASE_AUTO_ENABLE=true in host.env is that opt-in; when it is absent or not true, consensus-rnd-cli release-gate exits 0 with a noop reason and writes no release decision.
consensus-rnd-cli release-gate is decision-artifact-only. 禁止 decider 直接 bump/commit/push: it must not run git, bump mapped manifests, commit, push, tag, publish, merge, close, or otherwise exercise lifecycle authority. It only computes stability from GitHub/state artifacts and writes durable release decision/candidate artifacts for the controller-owned release publisher to consume.
Before making the release decision, the controller runs consensus-rnd-cli release-commits --target-ref origin/$REVIEW_BASE_BRANCH, then runs consensus-rnd-cli release-gate. release-commits is the independent narrow producer for .refactor-loop/state/release-commits.json. Authorization mirror: skills/consensus-loop/authorizations/runtime-exceptions.md#release-commits-producer-232. Allowed: read git by fetching tags, resolving the latest release tag, resolving the target ref, selecting a branch-reachable release since-anchor from a tag ancestor or mapped manifest version transition, and logging that release range; atomically write .refactor-loop/state/release-commits.json; authority tokens are read-git and write-artifact only. Forbidden: no gh, push, merge, reset, tag, release, lifecycle mutation, or inline execution inside release-gate. Fact source: local git tags, target branch refs, .version-bump.json, and mapped manifest version fields. Verification: behavior and source-regression coverage in test_release_commits.py and test_cli_command_router.py. Release-gate only reads .refactor-loop/state/release-commits.json; it does not run git and must remain a consumer-only decider.
Command contract:
| Command | Behavior |
|---|
python3 <skill-root>/scripts/consensus-rnd-cli release-commits --target-ref origin/$REVIEW_BASE_BRANCH | Pre-gate projection producer. Read local git tags/refs and mapped manifest versions, derive commits since the branch-reachable release anchor for the target ref, and atomically write .refactor-loop/state/release-commits.json; no GitHub or lifecycle authority. |
python3 <skill-root>/scripts/consensus-rnd-cli release-gate | Dry-run. Compute stability, decide release type when ready, write .refactor-loop/state/release-decision.json, and print a summary. |
python3 <skill-root>/scripts/consensus-rnd-cli release-gate --dispatch | Compute a ready decision and write .refactor-loop/state/release-decision.json plus .refactor-loop/state/release-candidate.json; print a hint that the controller-owned publisher owns bump/commit/push/tag/release after preflight. |
python3 <skill-root>/scripts/consensus-rnd-cli release-gate --score-only | Compute and print stability only; it does not require release opt-in and does not write the decision file. |
Stability requires all signals green and fail-closed handling on missing or red evidence: the shared Checks API projection must see exact check-run name success for every name in $HOST_GITHUB_RELEASE_REQUIRED_CHECKS on both $REVIEW_BASE_BRANCH and $INTEGRATION_BRANCH (host.env); zero open crnd:phase:blocked PRs; zero crnd:human:maintainer-decision labels; zero Consensus-rnd Phase review-gate reject churn at three or more consecutive rounds; last 30 minutes P0 alert streak at most 3; at least RELEASE_AUTO_MIN_MERGES recent merge commits in .refactor-loop/state/recent-pr-merges.json for the last two hours(default 1); every restart-managed daemon heartbeat fresh within 90 seconds; and zero unresolved META_RESOLVED:escalate-human records. RELEASE_AUTO_ENABLE=true with missing or empty $HOST_GITHUB_RELEASE_REQUIRED_CHECKS fails closed with missing_host_required_release_checks. recent-pr-merges.json is a controller-owned post-merge projection produced by merge_pr after successful non-admin gh pr merge; the release decider only reads it and never discovers merge facts from git or GitHub. Release cadence also requires more than RELEASE_AUTO_MIN_INTERVAL_HOURS hours since last release(default 24). Detailed scoring and the release decision schema live in release decision schema.
release-decision.json records from_version, to_version, bump_type, coordinate_policy, commits, decided_at, stability_score, signals, ready, blocked_reasons, and release_interval. release-candidate.json records the artifact-only handoff metadata, including the decision artifact path, target version, target ref, coordinate policy, expiry, decision digest, required signal projection, host opt-in name, publish preflight name, and controller lifecycle owner.
Named runtime exception — release-publication(per #322)
Authorization source: skills/consensus-loop/authorizations/runtime-exceptions.md#release-publication-322. Exact-SHA evidence mirror: skills/consensus-loop/authorizations/runtime-exceptions.md#controller-release-publisher-334; it records source_issue #334, source_round r5, source_marker META_JUDGE_DONE:converge:round-4:decide, and skill_anchor #release-pipeline-integrationpost-61. The release lifecycle surface consumes decision-artifact-only output through the controller only. A scheduled/on-demand active-controller owner may run ReleasePublisher only after ReleasePublishPreflight validates RELEASE_AUTO_ENABLE=true, fresh .refactor-loop/state/release-candidate.json, fresh .refactor-loop/state/release-decision.json, matching decision_digest, matching target_ref, mapped manifest from_version, matching coordinate policy when present and mandatory coordinate_policy.transition=beta_core_promotion evidence for beta core promotion, and required checks green. ReleasePublisher may run the first-run publication allowlist through a controller-private detached release-publish transaction: git fetch origin <INTEGRATION_BRANCH>, git rev-parse origin/<INTEGRATION_BRANCH>, the checked-in ReleaseCommitTransaction detached worktree creation step under <repo>/.worktrees/release-publish/<version>-<attempt>, then in that detached worktree python3 .github/scripts/bump_version.py --version <to_version>, git add .version-bump.json <mapped manifests>, git commit -m "Release v<to_version>", git rev-parse HEAD, re-fetch and re-read origin/<INTEGRATION_BRANCH>, and only if the remote base still matches run git push origin HEAD:refs/heads/<INTEGRATION_BRANCH>, then read gh api repos/<slug>/commits/<fresh release commit sha>/check-runs --paginate --slurp through ReleaseRequiredChecksProjection, generate a controller-private release notes file from .refactor-loop/state/release-commits.json, and only then gh release create v<to_version> --target <fresh release commit sha> --notes-file <controller-generated release notes file> [--prerelease]. ReleasePublisher may also use already-bumped reentry when the only preflight mismatch is mapped manifests already equal to_version and git show -s --format=%s HEAD proves the HEAD subject is exactly Release v<to_version>; reentry may skip only python3 .github/scripts/bump_version.py --version <to_version>, git add .version-bump.json <mapped manifests>, and git commit -m "Release v<to_version>", then must run git rev-parse HEAD, read gh api repos/<slug>/commits/<exact release/reentry commit sha>/check-runs --paginate --slurp, generate a controller-private release notes file from .refactor-loop/state/release-commits.json, and only then gh release create v<to_version> --target <exact release/reentry commit sha> --notes-file <controller-generated release notes file> [--prerelease]. Remote already-bumped reentry may run only read-only git for-each-ref --format=%(refname:short) refs/remotes/origin/rollup, git rev-parse origin/rollup/<40hex>, git show -s --format=%s origin/rollup/<40hex>, and git show origin/rollup/<40hex>:<mapped manifest> evidence probes, must prove the suffix equals the resolved commit sha, and after integration-head and rollup-head proofs miss may first use suffix-validated live origin/rollup/<40hex> histories by running only git log --format=%H --fixed-strings --grep "Release v<to_version>" origin/rollup/<40hex> without --max-count; if rollup-history recall does not prove a release SHA, it may then use origin/<INTEGRATION_BRANCH> reachable history by running only git log --format=%H --fixed-strings --grep "Release v<to_version>" origin/<INTEGRATION_BRANCH> without --max-count. history recall output is candidate discovery only, not authorization, and requires exactly one 40hex history candidate whose git show -s --format=%s <40hex> subject is exactly Release v<to_version> and whose git show <40hex>:<mapped manifest> mapped manifests all equal to_version. It then must read gh api repos/<slug>/commits/<exact release/reentry commit sha>/check-runs --paginate --slurp, generate a controller-private release notes file from .refactor-loop/state/release-commits.json, and only then gh release create v<to_version> --target <exact release/reentry commit sha> --notes-file <controller-generated release notes file> [--prerelease]. Mirror summary: commit/push the release manifest commit through the detached origin-tip transaction or prove the already-bumped reentry commit or read-only proven remote integration/rollup/history release SHA, read exact-SHA Checks API, generate the controller-private notes file, and create tag/release only after that exact fresh SHA is green. Missing GH_REPO_SLUG, pending/red/missing/stale exact-SHA required checks, invalid Checks API JSON, Checks API failure, or pending/red/missing/API-fail fail closed before release notes generation, release creation, and .refactor-loop/state/release-publish-result.json writes. It writes .refactor-loop/state/release-publish-result.json only after release creation. Fact source: release candidate/decision artifacts + mapped manifests + exact detached transaction SHA or local already-bumped SHA or read-only proven remote integration/rollup/history release SHA + Checks API projection + .refactor-loop/state/release-commits.json for release notes body only, with coordinate policy validated by #322 preflight when applicable. consensus-rnd-cli release-gate remains decider only; release.yml is read-only manual preview/verification with contents: read and no tag or GitHub Release authority. Forbidden: no public consensus-rnd-cli release-publish, no public consensus-rnd-cli publish-release, no workflow tag/release creation, no tag target without exact-SHA green checks, no proof-ticket/resume system, no git tag, no force-push, no git merge, no git rebase, no git reset, no current-checkout first-run release commit/push, no git fetch origin HEAD, no git rev-list --count HEAD..origin/HEAD, no git push origin HEAD, no arbitrary branch push, no worker diff commit, no GitHub Release edit/delete/upload, no approval-ticket/emoji gate, no issue/PR lifecycle, no issue lifecycle, PR lifecycle, label mutation, label lifecycle, merge/close, or generic lifecycle actor. Verification: test_release_publisher.py, test_release_notes.py, test_release_publish_preflight.py, test_cli_command_router.py, test_runtime_exception_authorization_sources.py, test_release_pipeline_contract.py, test_skill_reference_anchors.py, and test_controller_actions.py. no_new_runtime_authority: this names and mirrors the existing controller-owned ReleasePublisher path; mirror only, not a runtime API/loader/schema/proof ticket/authorization source; the notes file is a controller-private artifact generated after exact-SHA checks and before release creation, not a new public CLI, workflow release authority, tag authority, release edit/delete/upload authority, issue/PR/label lifecycle authority, or host production SSOT.
Named runtime exception - rollup-autonomous-merge(maintainer-directive 2026-06-06)
Authorization source: skills/consensus-loop/authorizations/runtime-exceptions.md#rollup-autonomous-merge-2026-06-06. Durable directive: .refactor-loop/runs/maintainer-directives/2026-06-06-rollup-autonomous-merge.md.
Allowed: active-controller owner only; exactly one open release-rollup PR from $INTEGRATION_BRANCH to $REVIEW_BASE_BRANCH; rollup head branches must start with rollup/. When a rollup PR is already open, ControllerActions.open_release_rollup_pr_from_pending_event() may force-update that existing rollup head with git push --force-with-lease origin <integration_sha>:refs/heads/<existing-rollup-head> and refresh its detailed Chinese title/body with commit count, range summary, issue references, and ⟦AI:RELEASE-ROLLUP⟧. wakeup-plan classifies open rollup/ PRs as rollup-only and excludes them from reviewer dispatch, review-fix, and remote-ci-fix projections. wakeup-runner may call only the named helper auto_merge_release_rollup_pr_from_action for rollup PRs whose action fields bind target_kind=PR, head_ref=rollup/*, base_ref=$REVIEW_BASE_BRANCH, and exact head_sha. The helper must re-read live PR baseRefName, headRefName, and headRefOid, verify $ROLLUP_AUTO_MERGE is auto/true-like, and require ReleaseRequiredChecksProjection green for every $HOST_GITHUB_RELEASE_REQUIRED_CHECKS check on the exact live head SHA before running gh pr merge <N> --squash --delete-branch.
Forbidden: no cluster PR review policy change, no reviewer bypass for non-rollup PRs, no generic merge-to-review-base authority, no direct push to $REVIEW_BASE_BRANCH, no force-push except the singleton rollup head update, no admin merge, no branch-protection bypass, no issue lifecycle, no label lifecycle, no tag/release, no #322 release publication behavior change, no public lifecycle CLI, no generic lifecycle actor, and no worker-owned commit/push/merge.
Fail-closed behavior: missing/invalid branch config, non-rollup head, wrong base, stale action head, missing required-check config, pending/red/missing/API-failed required checks, invalid $ROLLUP_AUTO_MERGE, or gh pr merge failure logs a grepable pending event and leaves the PR for humans.
Verification: test_sync_dev.py, test_controller_actions.py, test_wakeup_plan.py, test_wakeup_runner.py, test_host_env_surface_matrix.py, test_runtime_exception_authorization_sources.py, and test_skill_reference_anchors.py.
Notify-only update check(per #231)
Authorization source: skills/consensus-loop/authorizations/runtime-exceptions.md#update-check-231. skills/consensus-loop/VERSION.json is the checked-in VersionSourceManifest: data-only fields are schema, version, repository, release_source, and install_hint; only version is listed in .version-bump.json.
consensus-rnd-cli update-check is notify-only. It reads local VERSION.json, reads the GitHub latest release and then tags for ChronoAIProject/consensus-rnd, and atomically writes .refactor-loop/state/update-check.json. $UPDATE_CHECK_ENABLE missing, empty, or not true exits 0 and writes disabled state with a reason. GitHub/network/manifest errors write unknown state and do not block restart-daemons.
Downstream apply remains host-owned: the probe must not copy, overwrite, reinstall, edit host config, run installers, mutate .git, commit, push, tag, release, open/close/edit issues or PRs, mutate labels, or create a daemon. restart-daemons may call maybe_run_update_check(startup=True) only after the fixed daemon start/skip pass, and failures are warnings. concurrency may project fresh, positive update state into statusline-snapshot.json; statusline remains snapshot-only and never reads update-check.json directly or touches the network. When update_available=true, statusline displays up:v<latest>.
Verification is locked by test_update_check.py, test_cli_command_router.py, test_restart_daemons.py, test_concurrency_monitor_snapshot.py, test_statusline.py, test_runtime_exception_authorization_sources.py, and test_skill_reference_anchors.py.
CLI runtime authority fact source(per #166)
skills/consensus-loop/scripts/codex_refactor_loop/cli.py::COMMANDS[*].authority is the unique mechanical fact source for CLI runtime command authority for worker-visible, non-lifecycle public commands and for the single #396 lifecycle daemon command wakeup-runner. Each CommandSpec.authority is an inline closed-token tuple that states a public command's maximum git/gh/spawn/write-artifact/label/merge capability. Controller lifecycle primitives such as merge_pr, open_pr_with_label, open_release_rollup_pr_from_pending_event, apply_human_label_or_skip, safe_push, safe_sync_main, and triage apply stay outside COMMANDS; their authorization comes from controller-owned decisions/action facts and direct internal call boundaries. wakeup-plan may emit only fixed facts such as controller_action: "safe_push" with no_lifecycle_authority: true; #396 wakeup-runner may consume only wakeup-plan closed action projection with apply_authority: "wakeup-runner-396-only" and runner_authority: "wakeup-runner-396". peek may display those facts but must not render executable lifecycle commands. SKILL prose explains the human contract, narrow allowlist, durable authorization source, and no lifecycle authority by default; it must not grant a CLI capability missing from CommandSpec.authority. Worker prompt authority remains in prompt contracts and prompt tests, not as pseudo-commands in COMMANDS. New or expanded public CLI runtime authority requires matching behavior test and source-regression anchor coverage.
Task spawn claim(per #490)
Authorization source: skills/consensus-loop/authorizations/runtime-exceptions.md#task-spawn-claim-490. consensus-rnd-cli spawn-codex / spawn.py is the same-device per-codex-task atomic spawn-claim enforcement point and same-device per-codex-task mutual exclusion only. Before ProcessSupervisor.supervise(...), TaskSpawnClaimStore.acquire(...) creates .refactor-loop/locks/spawn-tasks/<safe-task-id>.lock with O_CREAT|O_EXCL; only successful acquisition may launch the supervisor. A live conflict writes one diagnostic-only JSONL line whose exact fields are task, log, lock, source, time, and no_lifecycle_authority; source is SPAWN_CLAIM_HELD, no_lifecycle_authority is true, and the command returns 0 skip/noop (exits 0 as skip/noop). Invalid task ids, lock directory/permission failures, unreadable metadata, metadata mismatch, or unsafe recycle conditions fail closed nonzero before supervisor launch. Existing locks may be recycled only when metadata matches the task/log path and the matching log has an EXIT= marker; the same metadata parser is the owner-local validation source for RuntimeRetention's later completed-lock cleanup and for reviewer_liveness.py same-device pending suppression of matching reviewer tasks.
This is local process mutual exclusion only. Upstream wakeup-plan, wakeup-runner, phase9-router, controller actions, and concurrency top-up must not add read-lock preflight or treat the lock artifact or SPAWN_CLAIM_HELD diagnostic as standalone authorization, retry source, or happy-path coordination. The only read-lock consumer carveout is reviewer_liveness.py, which may read a matching review-pr<N>-<role>-r<R> lock and holder PID to suppress duplicate same-head reviewer dispatch while the holder is alive; dead holders, mismatched metadata, missing locks, or terminal EXIT= evidence do not authorize side effects. The claim is not #191 ActiveControllerLease, not a cross-device per-work claim, not lifecycle authority, not host-defined lease scope, not a generic distributed lock, and not host production SSOT. Verification is locked by test_task_spawn_claim.py, test_spawn_claim.py, test_spawn_supervisor.py, test_reviewer_liveness.py, test_wakeup_plan.py, test_controller_actions.py, test_runtime_exception_authorization_sources.py, and test_skill_reference_anchors.py.
Named runtime exception - global-dashboard-status-card(per #504)