How the engine works, and why. Living document. The day-to-day guide is user-guide.md.
Script names below are written bin/<name> (paths in this repo). Run them as pdo <name> … once
bin/install --bin has put the pdo dispatcher on PATH, or by absolute path from an engine checkout.
project-system-manager skill; it reads
lessons-learned.md and runs bin/sysreport. Its workspace name is the project’s
SYSTEM_MANAGER_WORKSPACE.Orchestrator (<project>),
running /loop over the project-orchestrate skill. It ranks the queue, claims issues, spawns command
agents, routes events and moves board cards. It never edits product code.<Repo>-<issue#> <2-3 word brief>)
and its own git worktree. It runs a lifecycle skill and orchestrates its own sub-agents.QC (<project>) workspace (project-qc-review). It serves a PR on the
project’s QC servers for the human’s hands-on QC, and relays feedback to the PR’s command agent.intake workspace (project-intake). It turns reports into typed,
prioritised issues. Other agents queue reports with bin/intake-submit; the human drains the queue.project-portfolio-manager
skill). It observes every project and the machine they share, gives the human one ranked “waiting on
you” list, brokers cross-project requests and dependencies, and advises how resources are split. It
never does project work. See Portfolio manager.bin/spawn <repo> <n> re-reads the issue live, picks the route, creates a worktree and branch, writes
the dispatch brief, opens a herdr workspace and launches the agent there with Remote Control on and the
route’s lifecycle skill as its first prompt. Then it claims the issue (bin/claim: the managed-by:
label, a claim comment and the board status; see Coordination between managers)
and records metadata in $PDO_STATE_DIR/agents/<Repo>-<n>.json.CLAIM_ASSIGNS=1 the gh login’s own assignment is a legacy
claim and doesn’t count), and issues carrying another manager’s managed-by: label. It rolls back on failure. Every
failure except --dry-run is logged, and bin/queue skips that issue for 24 h.launch_agent in bin/lib.sh, the one seam for other agent CLIs (AGENT_CLI;
only claude today). It runs the CLI through herdr pane run, accepts the “trust this folder?” dialog
only when it names the worktree it just created, and returns 2 when the agent is blocked on anything else.identity_wrapper: bin/with-gh-user --user $GH_USER when the project sets
GH_USER (gh’s active account is machine-wide and other sessions switch it), plus whatever words the
project’s IDENTITY_HOOK prints for that checkout (e.g. a bot identity for one repo).with-gh-user pins both gh (GH_TOKEN) and git over HTTPS: it appends two GIT_CONFIG_KEY_<i>/VALUE_<i>
entries after the existing GIT_CONFIG_COUNT (reset the github.com credential helpers, then
!gh auth git-credential, which answers with GH_TOKEN), so git push and fetch use the same login and no
config file changes. If entries already in the environment set a github.com helper, it adds none and the outer
chain stays in charge. An IDENTITY_HOOK wrapper runs inside it, so it should append the same way rather than
set GIT_CONFIG_COUNT outright. A person-started session pins one call with
pdo with-gh-user --user <login> git push ….PDO_DISPATCHED=1 in the agent’s environment (agent_command in bin/lib.sh),
so the agent knows it was started by the engine and skips the collaborator’s one-time guide check.BRIEF_TEMPLATE, else the first prompts/dispatch-brief.md in the config dirs,
else the engine’s prompts/dispatch-brief.md. Its placeholders (upper-case names in double braces) are
filled in: REF, URL, NAME, WORKTREE, BRANCH, BASE, ROUTE, SKILL, TITLE, TYPE, LABELS, BODY, DIRECTIVES, NUMBER, ENGINE.| Signal | Route |
|---|---|
route:small / route:design / route:build label |
that route |
needs-design label |
design |
Epic whose body links a READY docs/design/*.md |
build (base: the epic’s docs/*-design branch) |
| Other Epic | design |
| Bug, Task, single Feature | small |
ROUTE_SKILL maps a route to its skill. A command agent may push back: a “small” issue that turns out to
need a migration or a cross-cutting UX decision stops and asks to be re-routed.
Epic phases. Phase sub-issues carry epic-phase. The queue excludes them and spawn refuses them
without --allow-phase: the epic’s build agent works every phase. Independent follow-ups carry no
epic-phase label and dispatch normally.
bin/queue ranks open issues in the managed repos (REPO_ROOT minus SELF_MANAGED_REPOS):
CWT_STATUS) first, then P-level (P_LABELS, best first;
unlabelled sorts last as P-).Security-labelled, then Bugs, then issues whose parent epic is In Progress, then
oldest first (a “created YYYY-MM-DD by” footer in the body wins over created_at, for migrated issues).drain.bin/floor P2 (persisted per project) stops anything below it from auto-spawning,
CWT included.FLEET_ADVICE=honour and a fresh fleet-advice.json, a hold
makes everything but the urgent bypass auto_spawn: false, and suggested_max_active caps
max_active (see Portfolio manager).drain.--all): assigned, another manager’s managed-by:<slug> label (reason
managed-by:<slug>; this project’s own label shows as claimed), on-hold, likely-fixed, epic-phase,
a spawn failure in the last 24 h, needs-triage (unless on CWT), GitHub’s native “blocked by” an open issue
(reason blocked-by:<ref>[,<ref>]; see below), already In Progress / In
Review / Done, an open workspace, Epics without a route:* label, and orchestrator-system work (the
System Manager owns it).issue_dependencies_summary.blocked_by (open blockers
only). For the issues where it is non-zero, one GraphQL query per repo (aliases, 50 issues each, about one
point) reads blockedBy to name them. If that read fails the reason falls back to the count
(blocked-by:2). GitHub updates the summary a few seconds after a dependency is added, so a brand-new
link can take one queue refresh to show.--limit and fails when the count equals it (silent truncation).MAX_ACTIVE (default 3) active command agents. Parked agents (PR at ready for QC or QC
complete, likely-fixed, on-hold, a design handed to build, idle at awaiting-ux-gate) don’t count.WEEKLY_CEILING % of the weekly usage budget (default 80, keeping 20% for manual and
hot work), and pauses at FIVE_HOUR_CEILING % of the 5-hour window (default 90). Usage comes from a
cache file (USAGE_CACHE; the oh-my-claudecode HUD’s cache by default). Missing data never blocks
silently: it is reported as unknown.<project>-pr-cap (default 10) open PRs are waiting on the human, nothing new spawns,
so the team’s pace follows the human’s merge rate.Claude only acts when a turn is triggered, so the orchestrator isn’t self-sustaining. bin/watch is a
token-free probe; the model only reads what changed.
waiting_on_you, usage, the PR cap, conflicting PRs, the queue top (refreshed
every 5 min) and the intake inbox count.slot_freed, urgent, qc_feedback, pr_conflict,
pr_cap_reached, design_ready, qc_complete, pr_merged, agent_gone, stalled,
new_claude_work_this, usage_ceiling, heartbeat. Quiet events (hibernated, closed_out,
hibernate_failed, event_hook_failed, and hook events marked "quiet": true) ride along with the
next wake.WATCH_EVENT_HOOK adds project-specific events to each snapshot (contract under
Hooks, “Watcher event hook”).--until-event polls (default every 300 s) until an event, or emits heartbeat at --max. The
orchestrator runs it in the background with --max 21600, and an hourly scheduled wakeup is the
fallback heartbeat, so the two don’t both wake it every hour.--arm-status reports whether this session owns a live watcher and kills every other one, so a
restarted orchestrator replaces an orphan instead of trusting it. For a read-only check use
pgrep -f '[b]in/watch --project'; --peek takes a snapshot without saving it.intake, orchestrator-<project>, qc-<project>, <repo>-<n>.A parked agent holds several hundred MB (the agent CLI, browser automation, MCP servers), and enough of them push the machine into swap, where low-memory reapers start killing background shells (including the watcher).
bin/hibernate <repo> <n> requires idle, no question pending, ready for QC on the PR (or
awaiting-ux-gate with --ux-gate) and a resumable session on disk. It stops the processes whose cwd or
command line lies in the agent’s worktree or session scratchpad (plus descendants), closes the workspace
and records hibernated_at. It never touches the agent CLI’s ancestors (the pane shell), the QC worktree
or anything on QC_PORTS. --servers-only keeps the session and stops just the dev stack and browser.--peek) hibernates agents idle at ready for QC for 5+ min,
and closes out finished ones (--closed-out): PR merged, or QC complete plus “wrap-up done” on screen,
or the issue closed with no open PR.hibernated, so qc_complete and pr_merged still
fire. bin/resume-agent brings one back with the same session (--resume <id>), recreating the
worktree from the branch if needed; it prompts a live agent in place, and refuses (exit 3) while a
question is pending on its screen.bin/qc start <pr#> (or --ref <branch>) checks out the PR in one shared, reused
worktree (<repo root>/$QC_WORKTREE, detached at the head), so dependency installs and builds stay
incremental and agents’ worktrees are never touched. The project’s QC_HOOK owns the servers.bin/resume-agent; ready for QC comes off, and the agent re-applies it after fixing.QC complete - ready to review (LABEL_QC_DONE); the agent wraps up and
says “wrap-up done”, and the watcher closes it out.ready for QC (LABEL_QC_READY) means the PR waits on the human.
The human adding QC complete - ready to review (LABEL_QC_DONE) is the pass: the watcher’s qc_complete
fires on that label, whoever added it. A PR carrying both labels counts as passed.CHANGES_REQUESTED)
from a trusted author (association OWNER, MEMBER or COLLABORATOR). The watcher reads the newest reviews
of each open PR in its existing PR query (no extra call), and raises each new one once as qc_feedback
(source: "review"), which the orchestrator routes to the PR’s agent. A reviewer who only uses a browser
can therefore drive the whole loop. Agents answer remote reviewers on the PR, never only in their
session. GitHub doesn’t let a PR’s author request changes on it, so the author gives feedback in the
agent’s session (or with local QC) instead.WATCH_EVENT_HOOK turns them into the same events, and the profile’s
## Project extras tells humans and agents how to use them.bin/qc-fixture-push <pr#> <file> stores test
data on a never-merged branch (FIXTURE_BRANCH, default qc-fixtures) with git plumbing (no checkout),
and prints the link for the checklist. Fixtures never go on the PR branch, or they merge with it.bin/shots <pr#> opens the PR’s newest screenshot folder under
<worktree>/$SCREENSHOT_DIR.QC_PORTS; their own headless QC uses per-issue ports from the
profile.bin/pr-priority gives each open PR the best P-label among the issues it closes
(closingIssuesReferences, or the body’s Closes/Fixes/Resolves/Part of #N for stacked and epic PRs,
which GitHub doesn’t link). It also copies CARRY_LABELS (default client-requested), add-only. It never
downgrades: a higher P-label already on the PR stays. It runs after gh pr create and on every queue
refresh.
The intake skill classifies each report, runs one bounded, fact-only look (duplicates, related docs and
memories, candidate files and entry points) into a “Starting points (unverified)” section with no
root-cause guesses, asks at most one clarification round, and files through bin/file-issue (typed,
labels validated, parent sub-issue attached, board status set). Agents never prompt it directly; they
queue reports with bin/intake-submit, and only an explicit “inbox” run triages the queue.
bin/lib.sh)PDO_CONFIG_DIRS: colon-separated config dirs, default ${XDG_CONFIG_HOME:-~/.config}/project-dev-orchestration.
Each may hold projects/<name>.env, prompts/, hooks/ and config.env. The first match wins.config.env files are sourced when lib.sh loads (the first dir wins); write : "${KEY:=value}" so a
real environment variable still wins. A project env, sourced later, overrides plain assignments.PDO_STATE_DIR: default ${XDG_STATE_HOME:-~/.local/state}/project-dev-orchestration.PDO_INBOX: the intake inbox, default $PDO_STATE_DIR/intake-inbox; INTAKE_INBOX is still honoured.--project, else $PDO_PROJECT, else the project whose REPO_ROOT holds the cwd’s repo
(matched on the git common dir, so every worktree of a bare root matches; else by path prefix). No match
fails with the list of known projects. There is no built-in default.$HERE is the config dir that holds it. Relative hook, template and
CONTROL_DIR paths resolve against that dir.bin/pdo-config --json (bin/pdo_config.py), and
bin/project-env [--project P] [KEY] prints it for skills and humans. project-env --board looks up the
board’s node id, Status field id and column option ids live, so the profiles of several repos sharing one
board cite it instead of copying ids.| Key | Default | Meaning |
|---|---|---|
ORG |
(required) | GitHub owner of the repos and the board |
PROJECT_NUMBER |
(none) | org project board number; the queue and claim need it |
REPO_ROOT |
(required) | declare -A: repo name → local clone root |
SELF_MANAGED_REPOS |
() |
repos another manager owns; queue and watch skip them |
REPO_MANAGER |
() |
declare -A: self-managed repo → its manager’s workspace/session name (fleet) |
MANAGER |
Orchestrator (<p>) with a board, else empty |
the project manager’s workspace/session name; empty = the human |
MANAGER_SLUG |
a slug of MANAGER (else of the project name), e.g. orchestrator-acme |
the claim label is managed-by:<slug>; cut to 39 characters |
CLAIM_ASSIGNS |
0 |
1 = claim also assigns the gh login (the pre-0.6 lock); 0 leaves assignees to humans |
PORTFOLIO_PRIORITY |
3 |
rank across projects, 1 = highest (fleet advice) |
RESOURCE_PROFILE |
medium |
light / medium / heavy: MB and usage weight per active agent |
AGENT_MB / AGENT_USAGE_WEIGHT |
from the profile | override the profile’s per-agent MB / usage weight |
FLEET_ADVICE |
ignore |
honour: queue and watch apply fresh fleet-advice.json |
ROUTE_SKILL |
(required for spawn) | declare -A: route → first-prompt skill |
DEFAULT_MODEL |
opus |
model alias for launched agents |
BRIEF_TEMPLATE |
see Spawning | dispatch brief template |
PRIMARY_REPO |
(none) | bare #n means this repo; default --repo for qc, shots, pr-agent, fixtures |
GH_USER |
(none) | pin agents’ gh to this login; unset = no pin |
IDENTITY_HOOK |
(none) | hook <dir> prints extra wrapper words for an agent in that checkout |
TEAM_UP_HOOK |
(none) | extra team-up steps; gets team-up’s unknown options |
SYSREPORT_HOOK |
(none) | extra sysreport lines |
QC_HOOK |
(none) | the QC servers (contract below) |
QC_REPO |
PRIMARY_REPO |
repo bin/qc serves |
QC_WORKTREE |
qc |
QC worktree dir name under the repo root |
QC_PORTS |
(none) | ports hibernation never stops |
FIXTURE_EXT |
(none) | the project’s fixture extension (a mismatch warns) |
FIXTURE_EXT_BY_REPO |
() |
declare -A: repo → its fixture extension, for repos whose format differs; qc-fixture-push --repo uses it before FIXTURE_EXT |
WATCH_EVENT_HOOK |
(none) | executable the watcher runs each snapshot for project-specific events (contract below) |
WATCH_EVENT_HOOK_TIMEOUT |
120 |
seconds before the watcher abandons a hook run |
FIXTURE_BRANCH |
qc-fixtures |
never-merged fixture branch |
SCREENSHOT_DIR |
.omc/qa |
screenshot root inside a worktree |
CARRY_LABELS |
client-requested |
issue labels copied onto PRs |
CWT_STATUS |
Claude Work This |
board status that jumps the queue |
P_LABELS |
P1-urgent P2-high P3-medium P4-low |
priority labels, best first |
LABEL_QC_READY / LABEL_QC_DONE |
ready for QC / QC complete - ready to review |
QC state labels |
SYSTEM_MANAGER_WORKSPACE |
System Manager |
named in the queue’s orchestrator-system reason |
CONTROL_DIR |
the env’s config dir | cwd of the standing orchestrator, QC and intake agents |
ORCHESTRATE_SKILL / QC_SKILL / INTAKE_SKILL |
${PDO_SKILL_PREFIX}project-orchestrate / …project-qc-review / …project-intake |
standing agents’ skills |
AGENT_CLI |
claude |
agent CLI (only claude today) |
MAX_ACTIVE, WEEKLY_CEILING, FIVE_HOUR_CEILING |
3, 80, 90 | concurrency and usage limits |
PDO_SKILL_PREFIX (environment or config.env) defaults to /project-dev-orchestration:, the Claude
Code plugin namespace. Set it to / when the skills are linked un-namespaced (bin/install --link).
Example project env (<config dir>/projects/acme.env, plain bash):
ORG=acme-co
PROJECT_NUMBER=3
declare -A REPO_ROOT=([acme-app]="$HOME/src/acme-app.git" [acme-engine]="$HOME/src/acme-engine")
PRIMARY_REPO=acme-app
declare -A ROUTE_SKILL=(
[small]="${PDO_SKILL_PREFIX}project-small-feature-or-bug-fix"
[design]="${PDO_SKILL_PREFIX}project-feature-design"
[build]="${PDO_SKILL_PREFIX}project-feature-build"
)
QC_HOOK=hooks/acme-qc-hook # relative: resolved against this config dir
QC_PORTS="3000 8080"
Hooks are executables. They get PDO_HOME, PDO_PROJECT, PDO_ENV_DIR, PDO_STATE_DIR, ORG and
PRIMARY_REPO in the environment, and may source "$PDO_HOME/bin/lib.sh" for helpers such as
pdo_free_port.
QC_HOOK <stop|start|wait|status> [options]. bin/qc start runs: stop, then fetch + checkout +
clean, then start, then writes $PDO_QC_STATE/current.json (merging $PDO_QC_STATE/hook-current.json
if start wrote one), then wait. Options bin/qc doesn’t know (e.g. --fresh-db) are passed through.
Environment: PDO_QC_STATE, PDO_QC_REPO, PDO_QC_ROOT, PDO_QC_WORKTREE, PDO_QC_PR, PDO_QC_REF,
PDO_QC_SHA, PDO_QC_BASE, PDO_QC_PREV_SHA (the worktree’s previous head, to skip an unchanged
dependency install) and PDO_QC_KEY (pr:<n> or ref:<branch>, e.g. to decide when to reseed a QC
database). The hook should never use a database that command agents start and stop, and should clone a
seed per served PR rather than roll migrations back.
Watcher event hook (WATCH_EVENT_HOOK). The extension point for project-specific signals (PR-comment
commands, another service’s state). bin/watch runs it once per snapshot, after the PR index is read,
with no arguments and a JSON context on stdin:
{"version": 1, "project": "acme", "org": "acme", "repos": ["widget"],
"since": "2026-10-16T18:00:00Z", "now": "2026-10-16T18:05:00Z", "peek": false,
"trusted_associations": ["COLLABORATOR", "MEMBER", "OWNER"], "state_dir": "/home/u/.local/state/pdo",
"labels": {"qc_ready": "ready for QC", "qc_done": "QC complete - ready to review"},
"prs": [{"repo": "widget", "number": 5, "url": "https://github.com/acme/widget/pull/5", "head": "feat/5",
"head_sha": "abc123", "base": "main", "labels": ["ready for QC"], "draft": false,
"mergeable": "MERGEABLE"}]}
repos are the managed repos (REPO_ROOT minus SELF_MANAGED_REPOS); prs are their open PRs.since is where the previous successful run’s now left off (10 minutes ago on a first run, never more
than 7 days back). It advances only after a clean run, so a failed run is retried over the same window:
the hook must dedupe by its own ids (kept under state_dir, in a file named after the hook).peek: true (also PDO_WATCH_PEEK=1) is watch --peek: report what you find, but change nothing (no
labels, comments or state writes).trusted_associations authors should count, the same rule the watcher applies to reviews.PDO_STATE_DIR, PDO_PROJECT, …) and the watcher’s gh login.It prints a JSON array of events on stdout, in the watcher’s own shapes, e.g.
{"type": "qc_feedback", "repo": "widget", "number": 5, "id": 901, "author": "boss", "url": "…", "body": "…"}.
The watcher merges them as if it had found them: a wake event ends --until-event, and an event with
"quiet": true (or a quiet type) rides along with the next wake. Hook events skip the snapshot diff,
so each must be emitted once. Items that aren’t objects with a string type are dropped with a warning;
at most 200 are taken per run.
Robustness: a run is killed after WATCH_EVENT_HOOK_TIMEOUT seconds (default 120). A timeout, a non-zero
exit, output that isn’t a JSON array, a missing file, or a GitHub outage (the hook is skipped when the PR
index can’t be read) prints a warning on stderr and adds the quiet event event_hook_failed; the watcher
carries on. The hook’s last few stderr lines are echoed with a watch: hook: prefix.
The engine env above is for scripts. What agents need to know about a product repo lives in that repo,
in .agents/project-profile.md on the default branch; the skills cite its sections as §<Section>.
git rev-parse --show-toplevel); for a bare-root layout, the
default branch’s copy (git show origin/HEAD:.agents/project-profile.md against
git rev-parse --git-common-dir). With no profile, a skill detects the obvious build tooling
(package.json scripts, *.sln, pyproject, Cargo, go.mod), carries on with that, and offers to write one
from templates/project-profile.md.## headings): Identity, Layout, Build & checks, Tests, Dev servers,
QC data, Changelog, Review roles, Docs, Conventions, Project extras (optional), Do not. What each holds is
in profile.md and the template.AGENTS.md gets a short “How work flows here” section from
templates/AGENTS-workflow-section.md; CLAUDE.md can be
@AGENTS.md plus any Claude-only notes.One level above the per-project managers. “Observe and coordinate, observe the system’s constraints, and prioritise the managers’ allocations; not a strict slot queue, since projects need different amounts of resources.” It never does project work.
bin/fleet (read-only) enumerates every project env along PDO_CONFIG_DIRS and reports, per
project: its manager (MANAGER; REPO_MANAGER for SELF_MANAGED_REPOS) and how to reach it
(peer messaging, else the herdr agent, else the human); active, parked, hibernated (split by
whether an open PR remains), stalled and standing agents (agents in a SELF_MANAGED_REPOS repo on a
separate “self-managed (owner: go, slow or hold, a reason and a soft
suggested_max_active. Hard limits first (disk under 5 GB, weekly usage over the ceiling = only
PORTFOLIO_PRIORITY 1, 5-hour ceiling, memory under 3 GB available = hold heavy projects, swap
half full, fewer than 600 GraphQL points); then the spare memory (available minus a 2 GB reserve)
is shared out in priority order, each new agent costing its RESOURCE_PROFILE MB (light 400,
medium 900, heavy 1800; usage weights 0.5 / 1 / 2). The full rules and thresholds are in
skills/project-portfolio-manager/references/allocation.md.fleet --write-advice writes fleet-advice.json (into each project’s state
dir). A project with FLEET_ADVICE=honour and advice under 2 h old: queue makes hold rows
auto_spawn: false (fleet_hold) except the urgent bypass, and bounds max_active by
suggested_max_active; watch computes free_slots with that bound. The default, ignore,
changes nothing; queue shows the advice in its summary line either way. Stale advice is ignored.fleet deps add|list|done "<org/repo#n>" blocks "<org/repo#m>"
in $PDO_STATE_DIR/fleet-deps.json, mirrored as GitHub’s native dependency (below). fleet lists
the open ones, flags any whose blocker has closed or merged, and also lists native cross-repo
dependencies that exist only on GitHub.managed-by:<slug> label, and stale
claims: an issue labelled by one of this install’s managers with no live workspace and no
hibernated agent still in progress (needs herdr; without it, stale claims are unknown).Several managers (project orchestrators, leads that self-manage a repo, people’s own agents) can work the same repos. Three GitHub-side mechanisms keep them from colliding; anyone’s agent can read them without the engine.
bin/claim adds managed-by:<MANAGER_SLUG> (it creates the label, colour c5def5,
description “Worked by agents of CLAIM_ASSIGNS=0 (the default) claim never assigns, and any
assignee blocks claim, spawn and the queue. CLAIM_ASSIGNS=1 keeps the pre-0.6 behaviour (claim assigns
the gh login, whose own assignment then doesn’t block) for installs whose live issues are still assigned
that way; switch it off after relabelling them.bin/hibernate --closed-out (merged, QC passed and wrapped up, or closed) removes the label;
plain hibernation keeps it (still in progress). The watcher (not under --peek) also releases finished
agents whose metadata still records the label: merged or closed after they hibernated, or a release that
failed. pdo claim --release <repo> <n> [--unassign] releases by hand.pdo fleet deps add A blocks B adds GitHub’s “B is blocked by A”:
POST /repos/{o}/{r}/issues/{B}/dependencies/blocked_by with issue_id = A’s numeric issue id (the REST
id, not its number); an existing link (HTTP 422 “already been taken”) counts as added. deps done
calls DELETE …/dependencies/blocked_by/{A's id}. GitHub refuses a pull request as blocker (“Target issue
may only be an issue”), and a repo or blocker the login can’t read is a 404: then the pair stays local, a
coordination comment on B says so, and deps add warns; deps done posts the matching “no longer
blocked” comment. The queue holds natively blocked issues (blocked-by:<ref>).pdo fleet note <org/repo#n> "<text>" [--as NAME] posts a comment headed
**Coordination (<NAME>):** (NAME defaults to $PDO_COORDINATOR, else portfolio) and appends it to
$PDO_STATE_DIR/coordination-log.jsonl. The portfolio manager logs every cross-manager agreement
(ordering, hand-off, ownership) this way on each issue involved.gh calls for another org run as the GH_USER of a project whose ORG owns the repo (gh auth token -u;
never a switch).
A private overlay is a config dir that sits first on PDO_CONFIG_DIRS: its project envs, prompts, hooks,
config.env and any project-only scripts. An overlay can keep old script paths working with a shim per
name that sets PDO_CONFIG_DIRS (and, if it wants the old default, PDO_PROJECT) and execs the engine’s
script, so prompts, briefs and memories that name the old paths still work.
A command agent runs until its PR is labelled ready for QC, then idles. After QC it wraps the PR up:
fixes, final push and comment. Merging stays manual.
ready for QC
nor merged (fails open; gives up after 3 blocks).--limit and fail when the result count equals it.