qq is qqp-dev's operator-owned harness for agentic development. This repository is the source of its shared methodology, skills, project knowledge, and cockpit preferences.
qq uses seven descriptive entities:
| entity | owner or surface |
|---|---|
| Actor | the operator and replaceable agents |
| Repository | Git and GitHub |
| Task | Backlog.md |
| Change | branch, commits, and pull request |
| Check | local verification and GitHub Actions |
| Skill | skills/ |
| Knowledge item | CONCEPTS.md, Backlog documents and decisions, and OpenWiki |
Every retained component supports one of these entities or provides the minimum wiring needed to expose it.
AGENTS.mdis the shared operating guidance. Linked Repositories can inherit the same file through a root-level symlink.skills/contains stateless capabilities discovered through each agent runtime's native skill surface.backlog/holds Tasks, authored documents, and decisions managed through the Backlog CLI and its shared search index.CONCEPTS.mdis the shared language agents read before every work item.- The single
IdeasBacklog document is the idea capture surface. - Backlog document categories
plans,research, andsolutionsretain historical designs, cited investigations, and reusable lessons. - herdr provides persistent
mainproject homes, named agents, and direct agent-to-agent messaging. cockpit/contains the operator's terminal configuration.delegation/contains the production pi-subagents role manifests, Completion Envelope schema, and Landstrip role policy map.bin/holds the qq commands — mounted onPATHby the cockpit shell surface — for guarded local OpenWiki updates and Herdr project-home focus and pane movement.
GitHub Flow is the delivery path. The deliver-change Skill owns the agent
procedure for carrying an authorized Change to a green pull request; the
operator merges.
Installation is by construction: every runtime surface mounts this checkout directly, so day-to-day changes — adding, editing, or removing a Skill or a command — are live everywhere with no install step. A machine is bootstrapped once.
Pi 0.80.10 or newer is the accountable runtime in each Repository's project home. Install current Pi and Herdr's native Pi integration, then verify the Pi version is at least 0.80.10:
npm install -g --ignore-scripts @earendil-works/pi-coding-agent@latest
pi --version
herdr integration install piDelegation temporarily uses a qq-owned pi-subagents bridge while T-154.2 builds
the narrow qq runtime. Its authoritative Pi package source is the exact,
immutable fork pin
git:github.com/hypermemetic-ai/pi-subagents@b7c531c238469e43866a1fe6697cb44279158c1c.
The fork commit's sole parent is the exact upstream
nicobailon/pi-subagents base
f1540b09283a1c176a0c721878453c6382ecd399; the exact fork commit is
b7c531c238469e43866a1fe6697cb44279158c1c in
hypermemetic-ai/pi-subagents.
The bridge carries one behavioral delta: a successful terminal
structured_output tool result is a trusted recovery watermark. Failed tool
results, bare calls, missing or invalid captures, and later errors remain
failures under parent schema validation. This closes the observed terminal
recovery hole without trusting unvalidated child prose or changing any other
pi-subagents behavior.
For a new install, use Pi's Git-package syntax with that exact commit. npm
packages, branches, tags, version ranges, moving refs, and local paths are not
authoritative pi-subagents install sources. Install the Landstrip binary
package directly into Pi's operator-owned npm tree. Do NOT
pi install npm:pi-landstrip: registering that extension makes it wrap the
accountable session's own Bash in a sandbox, and unversioned installs drift from
the adapter's pinned Landstrip version (delegation/policies/roles.json).
pi install git:github.com/hypermemetic-ai/pi-subagents@b7c531c238469e43866a1fe6697cb44279158c1c
npm install --prefix ~/.pi/agent/npm --legacy-peer-deps @landstrip/landstrip-linux-x64@0.17.31Migrate an npm install by removing its recorded source before installing the pin (use the exact old settings source instead when migrating another source):
pi remove npm:pi-subagents
pi install git:github.com/hypermemetic-ai/pi-subagents@b7c531c238469e43866a1fe6697cb44279158c1c(On macOS/Windows install the matching @landstrip/landstrip-<platform>-<arch>
package at the same version.) The Landstrip binary then lives beneath
~/.pi/agent/npm. qq-dispatch resolves that operator Pi copy by default, or
the absolute QQ_LANDSTRIP_BIN override when one is set. It does not resolve a
Repository-local .pi/npm copy.
Reconstruct the current bridge from its exact base and verify its provenance before preparing an update:
work="$(mktemp -d)"
base=f1540b09283a1c176a0c721878453c6382ecd399
pin=b7c531c238469e43866a1fe6697cb44279158c1c
git clone https://github.com/nicobailon/pi-subagents.git "$work/pi-subagents"
git -C "$work/pi-subagents" remote add fork https://github.com/hypermemetic-ai/pi-subagents.git
git -C "$work/pi-subagents" fetch fork "$pin"
test "$(git -C "$work/pi-subagents" rev-parse "$pin^")" = "$base"
git -C "$work/pi-subagents" switch --detach "$pin"For a deliberate update, start from the exact reviewed upstream commit, apply only the bridge delta, review the complete staged delta, and run the package's full suite before creating and publishing a new commit:
new_base=<exact-reviewed-upstream-commit>
git -C "$work/pi-subagents" fetch origin "$new_base"
git -C "$work/pi-subagents" switch -c qq-bridge-update "$new_base"
git -C "$work/pi-subagents" cherry-pick -n "$pin"
# Resolve only the deliberate bridge delta, then stage its reviewed paths.
git -C "$work/pi-subagents" add <reviewed-paths>
git -C "$work/pi-subagents" diff --cached --check
git -C "$work/pi-subagents" diff --cached
# Continue only after source review accepts this complete staged delta.
npm --prefix "$work/pi-subagents" ci
test ! -e /var/tmp/.agents
test ! -e /var/tmp/.pi
test_root="$(mktemp -d /var/tmp/pi-subagents-test.XXXXXX)"
trap 'rm -rf "$test_root"' EXIT
env -u PI_SUBAGENT_PI_BINARY -u PI_SUBAGENT_EXTRA_AGENT_DIRS \
-u QQ_DISPATCH_RUNTIME_ROOT -u PI_SUBAGENT_STRUCTURED_OUTPUT_CAPTURE \
-u PI_SUBAGENT_STRUCTURED_OUTPUT_SCHEMA TMPDIR="$test_root" \
npm --prefix "$work/pi-subagents" run test:all
git -C "$work/pi-subagents" commit -m 'fix: preserve terminal structured output'
new_pin="$(git -C "$work/pi-subagents" rev-parse HEAD)"
test "$(git -C "$work/pi-subagents" rev-parse HEAD^)" = "$new_base"
git -C "$work/pi-subagents" push fork "$new_pin:refs/heads/qq-bridge-$new_pin"Publish each accepted commit under a new hash-named ref; never force-update or
delete that publication ref. Update this README's pin and focused Check with
new_pin through the ordinary qq Change. Then remove the old exact source and
install the new exact source; never install the update by branch or tag:
old_source=git:github.com/hypermemetic-ai/pi-subagents@b7c531c238469e43866a1fe6697cb44279158c1c
new_source=git:github.com/hypermemetic-ai/pi-subagents@<new-exact-commit>
pi remove "$old_source"
pi install "$new_source"Verify user settings, every combined user/project package identity, and the installed checkout's Git HEAD and source before reloading:
source=git:github.com/hypermemetic-ai/pi-subagents@b7c531c238469e43866a1fe6697cb44279158c1c
pin=b7c531c238469e43866a1fe6697cb44279158c1c
checkout="$HOME/.pi/agent/git/github.com/hypermemetic-ai/pi-subagents"
jq -e --arg source "$source" '
[
(.packages // [])[]
| (if type == "string" then . else .source? // empty end)
| select(. == $source)
] == [$source]
' "$HOME/.pi/agent/settings.json"
SOURCE="$source" PI_PACKAGE_LIST="$(FORCE_COLOR=0 pi list --approve)" python3 - <<'PY_VERIFY_PI_SUBAGENTS'
import json
import os
from pathlib import Path
expected = os.environ["SOURCE"]
settings_paths = [Path.home() / ".pi" / "agent" / "settings.json", Path.cwd() / ".pi" / "settings.json"]
for settings_path in settings_paths:
if not settings_path.is_file():
continue
settings = json.loads(settings_path.read_text())
for entry in settings.get("packages", []):
package_source = entry if isinstance(entry, str) else entry.get("source")
if (
not isinstance(package_source, str)
or not package_source
or package_source != package_source.strip()
or not package_source.isprintable()
or package_source.endswith(" (filtered)")
):
raise SystemExit(f"ambiguous package source in {settings_path}")
records = []
for line in os.environ["PI_PACKAGE_LIST"].splitlines():
if line.startswith(" ") and not line.startswith(" "):
records.append([line.strip().removesuffix(" (filtered)"), None])
elif line.startswith(" ") and records:
records[-1][1] = line.strip()
authorities = []
for package_source, installed_path in records:
package_name = None
if package_source != expected:
if not installed_path:
raise SystemExit(f"unresolved package identity: {package_source}")
manifest = Path(installed_path) / "package.json"
try:
document = json.loads(manifest.read_text())
package_name = document.get("name")
except (OSError, json.JSONDecodeError) as error:
raise SystemExit(f"invalid package identity for {package_source}: {error}")
if not isinstance(package_name, str) or not package_name.strip():
raise SystemExit(f"invalid package name for {package_source}")
if package_source == expected or package_name == "pi-subagents":
authorities.append(package_source)
if authorities != [expected]:
raise SystemExit(f"unexpected pi-subagents authorities: {authorities}")
PY_VERIFY_PI_SUBAGENTS
test "$(git -C "$checkout" rev-parse HEAD)" = "$pin"
test -z "$(git -C "$checkout" status --porcelain)"
test "$(git -C "$checkout" remote get-url origin)" = https://github.com/hypermemetic-ai/pi-subagentsRelaunch Pi or run /reload. Moving refs and pi update or other automatic
package movement are forbidden for this bridge: delegation is production
infrastructure, so movement without a deliberate source review breaks the
provenance and invalidates the test evidence bound to the installed commit.
Retire the bridge only when T-154.2's implementation-neutral contract suite passes, production Skills and observer assembly use the qq runtime, and the installed fork pin is removed.
Mount the qq Skill root directly into Pi. This is one root mount, so Skill membership stays live by construction:
mkdir -p ~/.pi/agent
ln -sT "$HOME/projects/qq/skills" "$HOME/.pi/agent/skills"The project-local pi extension .pi/extensions/qq-subagent-env.ts sets the two
delegation variables and the structured-output runtime root in-process for any
pi session in this repository (and its worktrees), resolved from the checkout
the session runs against:
PI_SUBAGENT_PI_BINARY=<checkout>/bin/qq-dispatchPI_SUBAGENT_EXTRA_AGENT_DIRS=<checkout>/delegation/manifests/agentsQQ_DISPATCH_RUNTIME_ROOT=<temporary-directory>/pi-subagents-uid-<uid>
Pi auto-discovers the extension once the project is trusted, so delegates
dispatch confined by construction — no shell exports or launcher wrappers to
remember. Variables already set in the environment are left untouched, and
sessions in other projects never load the extension and keep the vanilla
dispatcher. Relaunch pi (or /reload) after install or upgrade.
Set the dispatcher-side pi-subagents config at
~/.pi/agent/extensions/subagent/config.json to include:
{
"intercomBridge": {
"mode": "off"
},
"defaultSessionDir": "/tmp/pi-subagent-sessions"
}qq delegate visibility uses run artifacts and status, so the intercom bridge
stays off instead of adding bridge tools to the staged child configuration.
defaultSessionDir keeps child session transcripts under a Landstrip-granted
temp root; without it, pi-subagents nests child sessions inside the parent
session tree, which the confinement policy deliberately does not grant. The
config is required: the adapter refuses dispatch when it is missing or
malformed. The configured path must be a direct pi-subagent-* child of the
launcher temp directory ($TMPDIR or /tmp). The project extension
pre-creates the root (mode 700) at session start and tightens an
operator-owned loose root; at dispatch the adapter enforces the contract and
fails closed on a symlink, foreign ownership, or any mode other than 700
rather than widening the grant.
The extension resolves the adapter and manifests from the checkout the
session runs in — a session in a Change worktree uses that worktree's
copies, which travel with the branch. Explicit environment variables always
win, including empty values (pi-subagents reads those as its vanilla
fallback); when overriding manually, point at the primary main checkout.
Pi-subagents inherits the one-time setup for every spawn and supplies the child
role, while its cwd selects the assigned worktree. The canonical adapter
serves any worktree from that Repository, refuses unrelated repositories,
renders that worktree's Landstrip grants, and starts the real Pi child under
bounded descendant cleanup. The three role manifests pin delegates to
openai-codex/gpt-5.6-sol:xhigh independently of the accountable session's
default model, and the adapter loads extensions/qq-codex-fast.ts into every
child so delegate GPT-5.6 requests run on the priority service tier (fast
mode).
Start Pi and use /login to configure both providers: select Kimi For Coding
for the accountable session's dedicated pi-qq credential, then select
openai-codex and complete its OAuth login for delegates. Pi writes the
credentials to ~/.pi/agent/auth.json; never commit or report their values,
and keep the file private:
chmod 600 ~/.pi/agent/auth.jsonMerge these defaults into ~/.pi/agent/settings.json:
{
"defaultProvider": "kimi-coding",
"defaultModel": "k3",
"defaultThinkingLevel": "max"
}These defaults select the native kimi-coding/k3 route with max thinking.
The accountable agent creates the global qq extension link once per machine:
mkdir -p "$HOME/.pi/agent/extensions"
ln -sfn "$HOME/projects/qq/extensions" "$HOME/.pi/agent/extensions/qq"That one link mounts the Repository extension set, which is live in every Pi
session from then on. settings.json no longer carries extension paths.
The Repository extension gives local feedback when Pi's built-in write or
edit targets the normalized backlog/ path of the checkout containing
Pi's current directory. It leaves reads, Bash, ordinary paths, and Backlog CLI
commands alone. This path-only drift-net is not a security boundary and does
not parse shell commands.
The pull-request extension provides the session-scoped qq_pr_watch tool. It
polls one exact pull request and sends one follow-up when it reaches MERGED
or CLOSED, or when inspection fails.
The operator-stage extension provides the operator_stage tool. It stages an
operator-only command, without executing it, in a focused right-hand herdr pane
with low- or high-danger confirmation and pane-read-back outcome validation.
The accountable Pi session stays in the Repository project home and owns
alignment, Task and Change judgment, work orders, verdicts, UAT, and handoff.
Bounded implementation, fresh review, and research run through pi-subagents;
qq-dispatch is only its fail-closed Landstrip adapter.
For an existing aligned Change, /handoff <Task-ID> is the standard transfer
to a fresh accountable Pi tab. It resolves the Task's unique linked checkout,
verifies its durable plan and ownership rails, starts the receiver in the
persistent project home, and restores caller focus. This transfers accountable
ownership; it is distinct from bounded child delegation through pi-subagents.
qq-observe writes append-only JSONL spans to
${XDG_STATE_HOME:-$HOME/.local/state}/qq/spans/<repo-name>/spans.jsonl.
It refuses a store that resolves inside any Git worktree. There is no daemon,
network export, or tracked runtime state. Record an engine span directly, or
import the timestamp range of a Pi session JSONL file:
qq-observe record --name execute_tool --phase implementation --actor engine \
--start 2026-07-21T10:00:00Z --end 2026-07-21T10:00:01Z
qq-observe read-session ~/.pi/agent/sessions/--path--/session.jsonl \
--phase orientation --actor accountable-sessionAt each delegate spawn, qq-dispatch records an invoke_agent span and injects
QQ_TRACE_ID, PI_ROOT_SPAN_ID, and its new span ID as
PI_PARENT_SPAN_ID. A policy-path experiment confirms these arbitrary parent
environment variables reach the confined Pi child, so nested pi-subagents runs
correlate automatically when the accountable session supplies root context.
Observation failures are reported but never change the child exit status.
qq-dispatch maps the child exit code to a raw span status at write time. At
read time, qq-observe summarize resolves raw errors for dispatch spans ending
with teardown signal status 143, 130, or 129 from pi-subagents' run outcome at
<runtime-root>/async-subagent-runs/<run.id>/status.json. The runtime root is
$QQ_DISPATCH_RUNTIME_ROOT when set, otherwise
${TMPDIR:-/tmp}/pi-subagents-uid-<uid>. A complete run resolves to ok;
failed and stopped remain error, and a missing, unreadable, malformed, or
other run state leaves the raw error in place as unresolved. Summarization does
not modify the append-only store, and summarize --json exposes every span's
raw_status, resolved status, and any outcome resolution note in
span_statuses.
The project extension .pi/extensions/qq-trace-context.ts establishes one
root trace context when an accountable interactive Pi session loads. When the
variables are absent, it mints QQ_TRACE_ID and a session-root span ID, sets
both PI_ROOT_SPAN_ID and PI_PARENT_SPAN_ID to that root, and records a
zero-duration, phase-less invoke_workflow structural marker. Explicitly set
values always win; delegate sessions inherit all three variables through
qq-dispatch, so the extension is a no-op there. With no pre-set span context,
each top-level dispatch is therefore a direct child of the accountable session
root. The extension logs its IDs once as [qq-trace-context] trace_id=… root_span_id=…; observation failure is non-fatal.
Use those logged IDs to import the session JSONL's coarse wall-time span into the same trace after the session:
qq-observe read-session <session.jsonl> --trace-id <trace> --parent-span-id <root>The accountable session's own phases remain one coarse span, not per-phase splits.
On a machine with the retired Skill mount, remove it if it exists (after
checking it points into this checkout): rm -r ~/.codex/skills.
Source the shell surface from .bashrc; it prepends bin/ to PATH and
provides the cockpit navigation helpers:
. "$HOME/projects/qq/cockpit/shell/file-navigation.bash"qqcd moves the shell to the focused Herdr worktree, falling back to
QQ_HOME; qqcd <pattern> selects another directory beneath HOME through
fzf. File browsing lives inside Pi through @tmustier/pi-files-widget.
Outside Pi, the system's xdg-open associations own MIME opening.
Link the cockpit configurations whose tools read fixed ~/.config paths:
mkdir -p ~/.config/glow ~/.config/herdr
ln -s "$HOME/projects/qq/cockpit/glow/glow.yml" ~/.config/glow/glow.yml
ln -s "$HOME/projects/qq/cockpit/glow/tuned.json" ~/.config/glow/tuned.json
ln -s "$HOME/projects/qq/cockpit/herdr/config.toml" ~/.config/herdr/config.tomlThese file links are day-0 bootstrap, not a sync surface: content is live through each link, and the set changes only when a new cockpit tool is adopted. Nothing needs re-running when Skills or commands change.
Bootstrap does not manage repository instructions. A linked Repository can
point its root AGENTS.md symlink directly to qq's AGENTS.md, keeping one
source of truth without adding global guidance to unrelated Repositories.
OpenWiki is an upstream tool, not a vendored qq subsystem. Install and update it through its own package mechanism.
OpenWiki uses local ChatGPT OAuth and writes the Repository's current-system
documentation under openwiki/:
qq-openwiki --init
qq-openwiki --updateIn a restricted fresh-agent or service environment, set QQ_OPENWIKI_BIN to the
OpenWiki executable's absolute path. The wrapper validates and invokes that
path directly; when it is unset, the shared resolver checks PATH and known
Homebrew locations. It does not use a login shell for executable discovery.
The same QQ_<TOOL>_BIN convention applies to Herdr, GitHub CLI, and Git where
qq resolves those tools.
Temporary debt (2026-07-10): ChatGPT OAuth merged in OpenWiki PR #151 after the
0.1.0 npm release. The operator machine is therefore built from upstream commit
90e8b22f562a5c8cf3c7377e081710084db1689f. Replace that source build with
npm install -g openwiki@latest and remove this note as soon as a published
release contains PR #151; installing 0.1.0 from npm before then removes OAuth
support.
Its credentials stay under ~/.openwiki/, uncommitted.
OpenWiki is a local single-writer derived surface owned by a separate maintainer
Actor, not by source-change agents. Refresh is explicitly assigned on demand or
by an optional schedule; source Changes do not trigger or perform it. The
openwiki-maintainer Skill owns generation, independent verification, and
delivery from its dedicated worktree; OpenWiki's internal generator owns
wiki authorship. qq-openwiki supplies deterministic branch, freshness,
process-lock, and root-instruction restoration guards.
Make qq-reap a weekly operator habit; for example:
0 9 * * 1 cd <repo> && bin/qq-reap scanRead the latest report, delete nomination lines to veto them, then run
qq-reap apply <report>. Every scan and apply writes a dated report, even
when empty; a missing report is the failure signal.
Keep one long-lived openwiki/update worktree per linked Repository. For an
assigned refresh, fetch origin, reset that worktree to the fresh origin/main,
and run qq-openwiki --update (--init only for first setup). Review the
complete generated diff through code-review, and open an ordinary
documentation-only pull request. The operator reviews and merges it.
Temporary debt (2026-07-10): upstream code mode unconditionally writes a
scheduled GitHub Actions workflow and scheduled-workflow agent guidance.
qq-openwiki removes that generated workflow and restores the pre-run root
instruction state after every local run. Remove this compatibility behavior
when OpenWiki supports local-only code recurrence without managing agent files.