loop-constructor-codex

Design an engineered gated loop for a medium/large (semi-)autonomous AI-coding task executed with the Codex CLI (single-agent, `codex exec`), emitted as a runnable .loop/ runbook. …

VincentJiang06

@vincentjiang06

Install

$ openclaw skills install @vincentjiang06/loop-constructor-codex

loop-constructor-codex

Design the engineered loop for a medium/large (semi-)autonomous coding task run with the OpenAI Codex CLI (single-agent, codex exec) by running an explicit selection procedure that decomposes it into a tree of gated sub-loops, then emit a machine-checkable loop-design JSON (which scripts/lint_loop_design.mjs PASSes) plus a decision log, and persist it as a runnable runbook under the project's .loop/.

The anchor (read first): a loop closes autonomously only when a fast, machine-runnable check can answer "is it done?". So design backward from the checkprinciple.closed_loop_needs_a_check. No runnable check ⇒ it is not a loop; the linter rejects it.

This skill is the applied front-end over the loop-principle KB. It does not restate theory — it RETRIEVES from the KB and REUSES its templates/checklists by path. Resolve the KB path once (see "Grounding"), then cite node ids for design judgments instead of asserting from memory.

The mechanism: SELECT → NEGOTIATE → FILL → VERIFY → PERSIST

Five phases. SELECT derives the loop's shape from an ordered decision procedure. NEGOTIATE turns that shape into a loop that won't converge on slop — separate the roles, agree the contract (the LOOPS.md operating model, references/loops-model.md). FILL writes the machine-checkable JSON; VERIFY + PERSIST gate and emit it. The whole point — write the loop, not the prompt — every hard call becomes an ordered, reviewable derivation, not judgment-by-vibes.

Codex runtime mapping

The design vocabulary above (roles, contract, separate-context evaluator, restart, gate, harness_primitives) is runtime-neutral — it names loop-engineering concepts, not any runtime's primitives. How each concept lands on the Codex CLI lives in references/codex-runtime.md. Load it during NEGOTIATE (roles realization — three roles = three separate codex exec invocations, the evaluator a fresh read-only one), FILL (harness_primitives = durable on-disk state; D4 parallelism = concurrent codex exec processes in git worktrees), and PERSIST (the emitted runbook carries a "How to run this loop (Codex CLI)" preamble). The design JSON itself stays neutral.

1. SELECT — run the decision procedure (references/loop-selection.md)

Answer D0–D6 in order; each answer determines part of the shape and is recorded with a one-line justification (the decision log). The ordered decisions: D0 is-it-a-loop (name the runnable "done?" check or route away) · D1 decompose (seam test → flat vs staged) · D2 per-stage pattern + check (+ falsifiable_when/passing_but_wrong) · D3 autonomy (in_the_loop vs on_the_loop) · D4 parallelism (large fan-out vs medium sequential — on Codex, fan-out = concurrent codex exec processes, never in-process subagents) · D5 guards (caps + on_failure + risk guards) · D6 iteration profile / cadence (completeness-first vs iteration-first, a dial that re-tunes D2/D3/D5). Load references/loop-selection.md and run the full procedure — each D-item there is the operational decision rule. The procedure is the selection method — it replaces altitude-by-vibes with a reviewable derivation. Record the answers as the selection_log array.

2. NEGOTIATE — separate the roles + agree the contract (references/loops-model.md)

Two moves from the LOOPS.md operating model, both linter-enforced for staged:

  • Separate the roles (§II). roles.{planner,generator,evaluator} = three contexts: planner→spec+contract (no code), generator→writes all (never grades itself), evaluator→fresh, adversarial (separate_context:true,adversarial:true), told the artifact is broken and to prove it. Self-grading breeds sycophancy — the #1 loop failure. On Codex these are three separate codex exec invocations (references/codex-runtime.md §1).
  • Negotiate the contract (§III). Generator proposes "done", evaluator pushes back → agreed testable assertions (contract.assertions[], each gradable + stage-traced). The contract, not the spec, is graded. The per-surface numbers are lower bounds over machine-gradable assertions (endpoint ≥8, module ≥12, app ≥20; ceiling 3×) — a contract too thin to disagree with gets rubber-stamped.

3. FILL — write the loop-design JSON (references/loop-design-shape.md)

The decisions above mostly determine the spec. Fill the canonical staged object (or flat if D1 found 0 seams): per-stage definition_of_done (a machine-verifiable predicate, not prose — each tracing back to a contract assertion) · loop_pattern · feedback_signal (check + expect:"pass" + falsifiable_when + passing_but_wrong) · stop_conditions (per-stage cap + on_failure, incl. restart where a build can become archaeology) · depends_on; and design-level loop_altitude (+rationale) · roles · contract · human_placement · maker_checker · harness_primitives (name the durable on-disk state so the loop survives context loss — each codex exec is fresh; disk is the only memory, see references/codex-runtime.md §2) · outer stop_conditions (with a non-empty success) · risk_guards (map to Codex sandbox / approval levers, codex-runtime.md §4). Include the selection_log. Reuse KB templates by path (references/loop-principle-map.md).

4. VERIFY — linter + fresh-reader (eat the dogfood)

Run the linter on the produced JSON before returning it:

node scripts/lint_loop_design.mjs <produced-design.json>

It must print all PASS and exit 0. Any FAIL <field>: <reason> → fix and re-run.

Then do the fresh-reader pass with the operational template assets/fresh-reader-checklist.md — the linter checks structure, not meaning. Re-read the design cold and confirm, per stage, that the check would actually fail on a broken impl, falsifiable_when is a real break (not a goal restatement), passing_but_wrong is an honest concrete false-pass, failure branches are reachable, and success matches what the checks prove. It also judges what the linter structurally can't: whether the contract is actually sufficient (≈20 assertions for an app-sized task, not a rubber-stampable handful) and the roles are genuinely separate (the evaluator never saw the impl). A green linter on a hollow check — or a thin contract — is the exact trap this pass exists to catch.

5. PERSIST — render the runbook

node scripts/render_loop_doc.mjs <design.json>   # writes .loop/<slug>.loop.{md,json}

render_loop_doc.mjs re-validates first and refuses to emit a runbook for a design the linter rejects — a written .loop/ doc is itself proof the design passed. The emitted Markdown carries a "How to run this loop (Codex CLI)" preamble (per-stage codex exec pattern, evaluator-as-fresh-codex exec, re-read-disk on codex resume; for large designs, concurrent codex exec + worktrees). A REFUSED: line → fix the design and re-run. Tell the user the two paths.

Report

Hand back: the decision log (D0–D6), the roles + negotiated contract, the loop-design JSON, the lint result (PASS), the fresh-reader verdict, a self-scored rubric (loop-principle/templates/loop_quality_rubric.template.json), the current bottleneck (where the weakest link is now — plan / verification / taste; LOOPS.md §IX), and residual risks. State the run-report contract the designed loop must honour when it stops: every success/autonomy metric paired with its integrity/damage counterpart or tagged not measured (references/loops-model.md §VII·b) — an unpaired success number is the shape of slop, not evidence. If the loop-principle KB was not found (see Grounding), say so out loud — the design ran in KB-degraded mode.

Grounding (KB path resolution)

The skill reads from the loop-principle KB, which this Codex skill does not embed (avoiding a duplicate copy). Default: resolve <kb> to the sibling skill's copy — try, in order, ../loop-constructor/loop-principle then ../vince-loop-constructor/loop-principle (installed sibling dirs carry the vince- prefix), relative to this skill folder. If an operator overrides it with $LOOP_PRINCIPLE, use that absolute path. Do not hardcode-fail; if the KB is absent (e.g. this skill is installed on a machine without the sibling), say so out loud in the report and degrade to the cited node ids in references/ — KB-degraded mode is acceptable. Retrieval recipe: node <kb>/tools/query_kb.mjs "<topic>".

Modules

FileWhen to load
references/loop-selection.mdPhase 1 (SELECT) — the D0–D6 decision procedure that derives the loop shape + decision log.
references/loop-design-shape.mdPhase 3 (FILL) — the exact canonical loop-design JSON keys the linter validates (flat + staged shapes, incl. roles/contract/restart + the persist contract).
references/loops-model.mdPhase 2 (NEGOTIATE) + judgment layer — the LOOPS.md operating model: separate roles, negotiate the contract, write-to-disk state, score-the-subjective, read-the-traces, delete-the-harness, the moving bottleneck.
references/codex-runtime.mdCodex realization — how roles/state/parallelism/guards land on codex exec. Load during NEGOTIATE (roles), FILL (harness_primitives + D4), PERSIST (runbook preamble).
references/loop-principle-map.mdKB grounding: each decision/field → loop-principle node ids + docs + which templates/checklists to reuse, and the query_kb recipe.
assets/fresh-reader-checklist.mdPhase 4 (VERIFY) — the operational fresh-reader template (per-stage + design-level boxes the linter can't check).
scripts/lint_loop_design.mjsThe deterministic verifier. Flat or staged. CLI or import { validate }.
scripts/render_loop_doc.mjsRenders a linter-valid design into a runnable runbook (with the Codex how-to-run preamble); validates first, refuses invalid.
assets/golden-loop-design.jsonA passing flat fixture (the atomic single-stage unit).
assets/golden-loop-design-medium.jsonA passing staged fixture — copy as the starting point for a decomposed design.
assets/golden-loop-design-large.jsonA passing large-altitude (fan-out) fixture — exercises the concurrent-codex exec Orchestration preamble; copy for a parallel-shard design.
evals/run_all.mjsRe-runnable adversarial battery over the linter + renderer; node evals/run_all.mjs.

Controls

  • Design-only — persist, don't execute. Writing the loop-design JSON + the runbook to .loop/ is producing the design artifact. Never run the designed loop, run the target's code, or modify the loop-principle KB.
  • Single-agent runtime. Never design a stage that requires in-process subagents or a Task/Workflow tool; parallelism and fresh-evaluator contexts are realized as separate codex exec OS processes (see references/codex-runtime.md).
  • Run the selection procedure. Don't pick a shape by vibes — derive it from D0–D6 and emit the decision log. A design without a decision log is incomplete.
  • Emit STAGED unless D1 genuinely finds 0 seams. The flat shape stays valid as the atomic single-stage unit (linter still accepts it).
  • Separate the roles; the evaluator is adversarial. planner/generator/evaluator are three contexts (§II) — three separate codex exec invocations; a model that grades its own work turns sycophantic, so the evaluator is a fresh read-only codex exec told the artifact is broken. Required for staged.
  • Negotiate the contract; grade it, not the spec. Agree the testable assertions before building (§III); too few lets the evaluator rubber-stamp. Required for staged.
  • Restart beats archaeology. Where a build can rot into a patch-pile, route on_failure: restart (discard the worktree + re-derive from the contract, §V) — and don't human-interrupt a restart; escalate only a wrong contract, not a broken build. Its trigger is a counter fixed before the run ("2 consecutive same-class failures"), never an in-flight call.
  • Stop on both sides. stop_conditions carries a zero-change gate ("N iterations with zero new changes → stop", the anti-arms-race brake) and a minimum-progress floor below which an early stop escalates instead of counting as done (D5).
  • Delete the harness as the model improves (§VIII). Prune scaffolding the model now does for free; match degrees-of-freedom to the task. A growing-only harness is one you've stopped reading.
  • Reject-on-no-check (per stage). A stage with no runnable feedback signal FAILs the linter; the anchor holds for every stage.
  • Mandatory caps. Every stage + the outer loop carry a finite max_iterations, written inside stop_conditions; the stage graph must be acyclic. A loop may trip a cap, never raise one — cap changes happen outside the loop.
  • Self-verification gate. Return a design only after the linter PASSes and the fresh-reader checklist is clean.
  • Cite, don't assert. Back design choices with loop-principle node ids.

Lifecycle

  • version in frontmatter (0.2.0).
  • Shared schema. The loop-design JSON schema + lint_loop_design.mjs are shared verbatim with loop-constructor 0.2.0 — designs are cross-compatible between the two skills; only the runtime prose (SKILL.md, codex-runtime.md, the runbook preamble) differs. A change to the schema the linter binds to (a new required field, a renamed key) is the only kind of breaking change.
  • Rollback = git restore the skill dir; the skill only writes design artifacts under the target's .loop/ and never executes a loop, so a bad design is inert.

Top skills in this category