Skip to content

agents

Recipe card from the charly-internals plugin (Development — contributor internals).

This card has additional detail pages:

OpenCharly is built to be driven from multiple agent harnesses’ multi-agent primitives. This skill is the authoritative reference for the primitives per harness, the charly agent roster, the shipped workflows, the default multi-agent execution model, the bed-scoped parallel-testing discipline, and the hooks/lifecycle rules that hold an autonomous run together.

The one rule that binds every reference below: a bed run is R10-class — the commit is gated on a full final-code bed test (pasted), but beds run freely throughout to verify (see references/parallel-bed-testing.md “The binding rule”). The project rulebook is AGENTS.md, the single harness-neutral rulebook carrying R0–R10; this skill never restates it, only points to it.

Topic Reference
Primitives per harness (Claude Code / Codex / Kimi); when to use a sub-agent vs. dynamic workflow vs. agent team; the charly agent roster (executors/enforcers); the shipped workflows (/audit-deploy-configs, /triage-check-failure, /verify-status); the agent-team primitive setup references/agent-roster.md
The default multi-agent execution model: orchestrator/teammate model-tier split, maximum parallelization, the slot budget, concurrent landing, the orchestrator’s bidirectional verification duty, architectural-integrity ownership, the responsibility matrix and tie-breakers references/orchestration-model.md
Program-wide alignment: the north-star protocol, the IOU register, per-merge measurement, migration-ledger discipline, crossed-ruling reconciliation, brief verification and stop-and-respawn, whack-a-mole escalation references/program-discipline.md
Bed-scoped parallel real-deployment testing: the concurrency ceilings (store lock, exclusive-resource tokens, long-bed ownership, in-tree build artifacts, per-worktree binaries) and their fixes; the charly binary in a multi-worktree setup; the binding rule for running a bed; implementation-workflow shape; speed levers references/parallel-bed-testing.md
Delegation as fresh context; teammate context lifecycle; rotate-on-stall (stop a stalled session, spawn a fresh one); the nine sub-agent operational invariants; the hooks doctrine (current hook inventory); agent lifecycle hygiene; the todo ledger & interruption safety; the universal PR-gate audit; worktree/validator lifecycle references/hooks-and-lifecycle.md
  • /charly-check:check — the bed surface these agents/workflows drive (charly check run/image/live, the disposable check-bed inventory, exit codes).
  • /charly-internals:disposable — why disposable: true is the sole destroy authorization.
  • /charly-internals:git-workflow — the R10-gated landing the executors feed.
  • /charly-internals:skills — agent/skill discovery and the signpost convention.
  • The project rulebook’s “Agents, Workflows & Teams”, R10 / “Hard Cutover by Default”, and AI Attribution sections.

Invoke before authoring or invoking an charly sub-agent / dynamic workflow / agent team, before wiring agent-lifecycle or commit/push gate hooks, and whenever deciding which primitive should drive the charly check beds for a given verification.

Every delegated worker MUST read the relevant skill before its first tool call (R1 2026-09-07)

Section titled “Every delegated worker MUST read the relevant skill before its first tool call (R1 2026-09-07)”

No sub-agent / dynamic-workflow child may start working without reading the SKILL.md(s) the task triggers. The parent brief must name the skill(s) AND the worker must load them (their SKILL.md + the referenced reference docs) BEFORE its first tool call - this is a hard precondition, not a suggestion. Measured failures this rule exists for: a worker chose charly check live (verify-only, skips every mutating step) over charly check run for an R10 proof because it never read the check skill, producing 26 skipped mutating steps + 43 downstream failures; a worker pushed four un-gated heads (gofmt-dirty, stale worktree replace) because it never loaded the git-workflow skill’s gate-before-push rule. A parent that fails to name the skill, or a worker that proceeds without loading it, commits an R1 violation - STOP and load it before continuing.

And every worker MUST run the pre-validator self-audit before its FIRST push. The parent brief MUST embed the pre-validator self-audit checklist from the /charly-internals:git-workflow skill’s pre-validator self-audit reference, and the worker MUST run that preflight against its own head before pushing. It classifies the change from the diff (never from intent), pastes only commands actually executed on this head, accounts for every applicable rule, and refuses to surface a failure it cannot own - the pass that turns a BLOCK→BLOCK→PASS cycle into a first-try PASS (umbrella #286). Full checklist: the /charly-internals:git-workflow skill’s pre-validator self-audit reference.

And a brief must never bake in a VOLATILE org fact — a CI workflow file name, a pin/tag/branch, or a moving issue/PR number. The brief names the DISCOVERY command; the worker discovers the value at run time. Full rule and the measured .github#141 evidence: references/program-discipline.md (“Volatile facts are DISCOVERED, never baked into a brief”).

The harness-config LOCATION (umbrella-only; layer-charly-internals#49)

Section titled “The harness-config LOCATION (umbrella-only; layer-charly-internals#49)”

ALL harness config lives in the umbrella root (opencharly/opencharly) and NOWHERE else: .claude/, .opencode/, .codex/, .pi/, .reasonix/, and opencode.json. No submodule or sibling repo — .github, a distro-*/layer-*/ pod-*/plugin-* repo, or charly/ — carries a copy, and there is no parity gate: the former charly task harness (which compared umbrella↔charly/ byte-identical files) is DELETED. Sessions root at the umbrella (umbrella AGENTS.md, “Sessions root at the umbrella”), and harness config resolves from the SESSION’S project root, so config anywhere else would bind a situation that cannot legally occur — one root, one config home. A rulebook (AGENTS.md) is NOT harness config and stays in its own repo. The clone-level git hook is installed per clone with charly task hooks. This skill documents the mechanism; the rulebooks stay harness-neutral.

The harness-config gate. scripts/check-harness-config.mjs (umbrella root) audits the harness-config surfaces themselves: each still parses and stays wired (the PreToolUse hooks block, the plugin set, the permission arms, the DeepWiki MCP entry, the SOUL.md injection), and --self-test mutates each surface to prove every check can fail. Like the clone-level hook it guards MECHANICS only; attribution, change class, and rulebook compliance are judged once by the fresh pr-validator at merge.

DeepWiki (architecture questions). Every harness declares the deepwiki remote MCP server (https://mcp.deepwiki.com/mcp: .mcp.json, opencode.json, .codex/config.toml). Where a grep cannot answer how a repo is put together, ask it (read_wiki_structure, read_wiki_contents, ask_wiki_question) about the repo’s GitHub name instead of guessing — and treat the answer as a pointer to read, never as truth: when the wiki and the code disagree, the code wins.

A plugin resolves DIFFERENT PIECES FROM DIFFERENT REFS — verify the pin its BACKEND resolves from (R1 2026-09-29)

Section titled “A plugin resolves DIFFERENT PIECES FROM DIFFERENT REFS — verify the pin its BACKEND resolves from (R1 2026-09-29)”

A plugin is not one artifact and does not resolve from one ref. Its code may load from the workspace / origin checkout, while a backend script it shells out to resolves from a pinned submodule (a gitlink) or a pinned @github ref. Those two refs advance independently, so a plugin can be HALF-USABLE: one tool works, another fails inside the same plugin, in the same session.

The rule. Before assuming a plugin works, verify the pin its BACKEND resolves from — not only that the plugin file loaded. A tool failure of the form “X not found — sync the pin” is a PIN-LAG, not a plugin defect: the tool is present in the plugin, but the script/submodule it dispatches to is older than the commit that added it. Do NOT debug the plugin’s code for it, and do NOT reimplement or hand-copy the missing script into the pinned checkout (that is an R3 workaround and a generated-copy edit).

Remedy. Identify the submodule / ref the backend script resolves from; compare the pinned gitlink (or @github ref) against the commit that merged the script; then advance the pin the sanctioned way — charly task sync for an umbrella gitlink (a wave: it advances EVERY pin the umbrella declares), or a refs-list bump + charly marketplace generate for the marketplace corpus — never a hand-pin and never a checkout into the submodule.

Concrete case. In one session coord_comment failed while coord_watch worked. Both are registered by the SAME plugin, loaded from the workspace — but coord_comment shells out to marketplace/scripts/coord.sh while coord_watch shells out to marketplace/scripts/gh_watch.sh. gh_watch.sh was already in the pinned marketplace gitlink; coord.sh had merged LATER and the gitlink had not advanced, so the tool reported “coord.sh not found — sync the marketplace pin”. The failure was a stale marketplace gitlink, not a defect in the plugin that bound the script.

Monitoring AND controlling agents in OpenCode (the agent_progress + agent_control tools)

Section titled “Monitoring AND controlling agents in OpenCode (the agent_progress + agent_control tools)”

In OpenCode the orchestrator does not guess at a subagent’s health — it reads the TRANSCRIPT and, when needed, controls the session. The .opencode/plugins/agent-progress.ts plugin registers TWO native tools: agent_progress (monitor) and agent_control (control). Both are pure TypeScript (the coord.ts directive: no .sh, no marketplace pin).

A “message” is an ASSISTANT TURN, never a stall signal. session_message rows with type='assistant' are turns; counting them penalises an agent for WORKING. A high turn count is NOT a stall — the monitor shows the count and never keys a verdict on it.

The four verdicts (artifact / cadence / loop / idle). agent_progress reports, per session: turns, span, last-turn/last-event age, the tool mix, the last action (the text/reasoning snippet + the last tool + its input), artifact hints (owner/repo#N, gh pr merge, merged, v… tags, pushed), and a verdict:

  • WORKING — recent turns + steady tool cadence (give it room);
  • IDLE — no turns past windowMin (default 15) AND no artifact;
  • LOOP — the same tool+input repeated (no artifact change) — the pathology;
  • DONE — a final report (finish=stop) with no pending action.

Diagnose from the transcript (session_message, type='assistant'). Rotate/take over ONLY on >2 orchestrator re-briefs of the SAME task, idle-past-window with no artifact, or a loop — never on turn count (see the project rulebook’s subagent-lifecycle section).

Stop = interrupt / delete, then CONFIRM it stopped. agent_control exposes list (enumerate sessions + verdicts), interrupt (stop a running session WITHOUT deleting it — ctx.session.interrupt({sessionID, continue:false})), delete (opencode session delete — the session AND its child sessions), wait (bounded block until idle), and confirm_stopped (re-read the transcript and ASSERT no new turns — the “CONFIRM it stopped” rule). interrupt/delete/wait require an EXPLICIT session id (never all) and report what they stopped, so a session another slug owns is never silently killed. A rotation is STOP + spawn: stop the predecessor (interrupt, or delete if it is dead weight) and confirm no new turns BEFORE spawning the successor — an announced-but-unperformed rotation leaves two agents on one scope.

In OpenCode the control primitives are first-class (ctx.session.interrupt / ctx.session.wait; opencode session delete; the API equivalents POST /api/session/{id}/interrupt, DELETE /api/session/{id}, POST /api/experimental/session/{id}/wait). Never claim “opencode cannot stop a subagent” — it can; an unverified assumption here is the same failure the RCA rule forbids.

Durable in-flight state lives in the session’s structured todo ledger — the harness’s todo primitive. It is the ONE session-scoped record of what is in flight, and it preserves work across an interruption only when it is treated as durable. Full procedure and the harness-binding detail: references/hooks-and-lifecycle.md (“Todo ledger & interruption safety”).

  • The ledger IS the todo primitive. Maintain it as the durable, session-scoped ledger of in-flight work, not a scratch list rewritten each turn.

  • Reconcile FIRST, then act. On ANY interrupting input — a fresh user message, an automated watcher alert, a delegated/subagent report, or a compaction — FIRST reconcile the ledger (keep every in-flight item; ADD the new one; never drop), THEN act. An interruption is an ADDITION, not a reset; never start a new list from scratch.

  • Long operations are ledger-first. Before starting any long-running operation, ensure the ledger reflects it, so a later interruption resumes from STATE, not memory.

  • One writer per artefact, and a VERSION GUARD on every read-modify-write. Two sessions appending to the same ledger or editing the same file is a lost update, not a merge: the second writer read a stale version, its write overwrites the first’s, and nothing reports an error - the surviving artefact is one of the two, chosen by timing. The guard is the write’s PRECONDITION: read the artefact with its version, and refuse the write if the version moved (a stale-version error IS this guard firing - re-read, rebase the intended change onto the new content, retry). Where no version is exposed, the durable substitute is a single writer: one session owns the artefact and the rest hand work over through a coordination comment instead of writing it themselves.

  • A status written from memory is a claim about the WRONG MOMENT. Re-read the artefact before reporting on it. This is the lost update read one step earlier: the reader held a stale version and reported it as current - and it is the same mechanism that presents downstream as a concurrent writer presents as a flaky test.

The umbrella rulebook states this as a Part II rule (AGENTS.md, “Ledger and interruption safety”) and points here for the detail. Where this guidance itself belongs is owned by the sibling /charly-internals:skills (“Where guidance belongs”): mandate in AGENTS.md, detail in the skill.

Three occurrences of ONE defect, all measured in a single night, on three levels: a check that did not run — or that ran against a narrower object than the claim — was read as a pass.

  1. A suite proved the FEATURE, not the CONTRACT. A plugin’s schema documented two edge shapes and the suite exercised one, so nothing could fail when the code ignored the shape the prose promised.
  2. A live gate drove only the already-guarded shape. The same change’s gate arm used the shape the code already handled, so the behaviour the change ADDED had no live execution at all and the gate still reached its PASS line.
  3. A control SKIPPED two of its five assertions and printed OK. They were skipped for want of uninitialized inputs; initializing them turned two SKIPs into PASSes and disclosed a fifth check that had also been quietly not running.

Read a control’s OUTPUT, never its exit code or its headline. A SKIP, an uninitialized input, an empty result set, and a narrowed arm all render as green. Before trusting any gate: count the assertions it CLAIMS, count the ones it RAN, and make the two numbers agree — or re-run it with its inputs materialized. The same discipline governs a monitor: a watcher armed over a stale item list reports silence, and silence is the very thing it exists to detect.