mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-09 11:51:37 -10:00
feat(daemon): copy openrig-core plugin tree to vendored assets location (slice 3.2 Phase 1)
SC-29 EXCEPTION #9 declared.
Per IMPL-PRD §2 + DESIGN.md §5.5 + plugin-primitive Phase 3a slice 3.2.
Makes the 25 TDD-red tests in commit 059825d pass.
Asset copy: substrate/.../missions/plugin-primitive/openrig-core/ →
packages/daemon/assets/plugins/openrig-core/
Vendored tree shape (per HG-2.1 + HG-2.2):
- LICENSE (Apache 2.0; public marketplace target)
- README.md
- .claude-plugin/plugin.json (Claude manifest; name + version + description
+ skills='./skills' + hooks='./hooks/claude.json' + repository
github:mvschwarz/openrig-plugins)
- .codex-plugin/plugin.json (Codex manifest; required name+version+description;
skills='./skills' + hooks='./hooks/codex.json')
- skills/ (11 canonical OpenRig skills, each with SKILL.md frontmatter:
agent-startup-and-context-ingestion, claude-compact-in-place,
claude-compaction-restore [+ scripts/], forming-an-openrig-mental-model,
openrig-architect, openrig-operator, openrig-user, permission-posture,
queue-handoff, seat-continuity-and-handover, session-compaction-and-restore)
- hooks/claude.json (4 events: SessionStart, UserPromptSubmit, Stop,
Notification — Claude Code's full event surface)
- hooks/codex.json (3 events: SessionStart, UserPromptSubmit, Stop —
Codex doesn't expose Notification)
- hooks/scripts/activity-relay.cjs (canonical relay script that POSTs
to /api/activity/hooks endpoint; consumes the endpoint preserved in
batch-1 D.2 rip per IMPL-PRD §1.2 endpoint discipline)
Cross-runtime path-substitution conventions used per runtime:
- Claude hooks reference ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs
- Codex hooks reference ${CODEX_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs
Repository / publishing: vendored copy is the source of truth at v0.
Auto-fetch from github.com/mvschwarz/openrig-plugins (slice 3.2 Phase 2)
will tolerate 404 + fall back to vendored. Repo currently empty (LICENSE
only) per founder authorization 2026-05-10; separate publish authorization
deferred until post-batch-2 + post-0.3.0.
SC-29 EXCEPTION #9: this commit ships ~20 new vendored asset files at
packages/daemon/assets/plugins/openrig-core/ as the source-of-truth
plugin distribution surface. The plugin tree is daemon source-tree
inclusion (npm package ships these); not a ConfigStore key addition or
new HTTP endpoint or new SQL migration. Boundary: no daemon endpoint /
no event type / no DB migration; just packaged assets the daemon copies
to ~/.openrig/plugins/openrig-core/ on first launch (PluginVendorService
in next commit). Cumulative SC-29 ledger: #1-4 + project-observability
#5/#6 + redo-driver-2 declares #8 (slice 3.3 plugin-discovery-service +
GET routes) + this commit declares #9 (slice 3.2 vendored plugin assets +
PluginVendorService). #10 may be declared by slice 3.4 if CLI adds new
ConfigStore keys.
Verification:
- openrig-core-plugin.test.ts: 25/25 pass (TDD green)
- Tree contains 21 files (LICENSE + README + 2 manifests + 13 skill SKILL.md
+ 2 skill scripts + 2 hook configs + 1 relay script)
Per IMPL-PRD §2 plugin-primitive Phase 3a slice 3.2 + per banked
feedback_tdd_scope + feedback_sc29_declarations_in_commit_body.
This commit is contained in:
1 parent
059825d704
commit
b98294b75e
20 files changed
+3604
No files matched your search
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "openrig-core",
|
||||
"version": "0.1.0",
|
||||
"description": "OpenRig canonical skills and hooks for cross-runtime agent topology coordination, continuity, and operating discipline.",
|
||||
"author": {
|
||||
"name": "Mike Schwarz",
|
||||
"url": "https://github.com/mvschwarz"
|
||||
},
|
||||
"homepage": "https://github.com/mvschwarz/openrig-plugins",
|
||||
"repository": "github:mvschwarz/openrig-plugins",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": ["openrig", "agent", "topology", "rig", "continuity", "coordination", "compaction", "handoff"],
|
||||
"skills": "./skills",
|
||||
"hooks": "./hooks/claude.json"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "openrig-core",
|
||||
"version": "0.1.0",
|
||||
"description": "OpenRig canonical skills and hooks for cross-runtime agent topology coordination, continuity, and operating discipline.",
|
||||
"author": {
|
||||
"name": "Mike Schwarz",
|
||||
"url": "https://github.com/mvschwarz"
|
||||
},
|
||||
"homepage": "https://github.com/mvschwarz/openrig-plugins",
|
||||
"repository": "github:mvschwarz/openrig-plugins",
|
||||
"license": "Apache-2.0",
|
||||
"keywords": ["openrig", "agent", "topology", "rig", "continuity", "coordination", "compaction", "handoff"],
|
||||
"skills": "./skills",
|
||||
"hooks": "./hooks/codex.json",
|
||||
"interface": {
|
||||
"displayName": "OpenRig Core",
|
||||
"shortDescription": "Canonical OpenRig skills and hooks",
|
||||
"longDescription": "Cross-runtime agent topology primitive shipping coordination, continuity, and operating-discipline skills, plus activity-tracking hooks. Use as the baseline plugin for any OpenRig-managed agent.",
|
||||
"developerName": "Mike Schwarz",
|
||||
"category": "Productivity",
|
||||
"capabilities": ["Read"],
|
||||
"websiteURL": "https://github.com/mvschwarz/openrig-plugins"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Support. While redistributing the Work or Derivative Works thereof,
|
||||
You may choose to offer, and charge a fee for, acceptance of support,
|
||||
warranty, indemnity, or other liability obligations and/or rights
|
||||
consistent with this License. However, in accepting such obligations,
|
||||
You may act only on Your own behalf and on Your sole responsibility,
|
||||
not on behalf of any other Contributor, and only if You agree to
|
||||
indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or support.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2026 Mike Schwarz
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,43 @@
|
||||
# openrig-core
|
||||
|
||||
Canonical OpenRig skills and hooks for cross-runtime agent topology coordination, continuity, and operating discipline.
|
||||
|
||||
## What this plugin ships
|
||||
|
||||
**Skills (11):** the canonical OpenRig operating knowledge — how to use the `rig` CLI, how to design rig topologies, how to recover from compaction, how to hand work off durably between agents, how to reason about permission posture, and more.
|
||||
|
||||
**Hooks:** activity-tracking hooks (Claude Code: 4 events; Codex: 3 events) that POST agent state to the OpenRig daemon for real-time UI seat-status updates.
|
||||
|
||||
## Runtimes
|
||||
|
||||
This plugin ships dual-manifest packaging — both `.claude-plugin/` and `.codex-plugin/`. Use it with:
|
||||
|
||||
- **Claude Code** — install via `/plugin install openrig-core@mvschwarz/openrig-plugins` (or via the OpenRig daemon's auto-vendor on first run)
|
||||
- **Codex CLI** — install via `/plugins` (or via OpenRig daemon)
|
||||
|
||||
Two skills target Claude Code's compaction behavior specifically:
|
||||
- `claude-compaction-restore` — used to rebuild a Claude Code agent's working mental model after `/compact`, from JSONL transcripts and touched files
|
||||
- `claude-compact-in-place` — verification + recovery SOP for an in-place compacted Claude Code seat
|
||||
|
||||
These are NOT Codex-self-targeting (Codex's compaction is handled internally and doesn't need rebuild SOPs). But Codex agents acting as orchestrators frequently invoke these skills when restoring a peer Claude that has compacted — that's exactly when they're needed. Both runtimes ship them.
|
||||
|
||||
All other skills are cross-runtime by design.
|
||||
|
||||
## Distribution
|
||||
|
||||
This plugin is published two ways:
|
||||
|
||||
1. **Vendored inside the OpenRig npm package.** When you install `@openrig/cli` (or run the daemon), `openrig-core/` is copied to `~/.openrig/plugins/openrig-core/` on first run. This is the offline-safe baseline.
|
||||
|
||||
2. **GitHub auto-fetch.** When network is available, the OpenRig daemon checks `github.com/mvschwarz/openrig-plugins` for a newer version of `openrig-core` and updates the local copy. This is the "hot update" path — skill improvements ship without bumping the OpenRig CLI version.
|
||||
|
||||
You can also install directly via Claude Code or Codex's own plugin commands if you prefer to manage plugins outside OpenRig.
|
||||
|
||||
## License
|
||||
|
||||
Apache License 2.0. See `LICENSE`.
|
||||
|
||||
## Source
|
||||
|
||||
- Plugin repo: https://github.com/mvschwarz/openrig-plugins
|
||||
- OpenRig CLI: https://github.com/mvschwarz/openrig
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Notification": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CODEX_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"UserPromptSubmit": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CODEX_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Stop": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node \"${CODEX_PLUGIN_ROOT}/hooks/scripts/activity-relay.cjs\"",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
// OpenRig activity-relay hook script.
|
||||
// Reads a hook event payload from stdin, normalizes it, and POSTs to the
|
||||
// OpenRig daemon's /api/activity/hooks endpoint for real-time UI seat-status.
|
||||
// Best-effort only: 1.5s timeout, errors swallowed, never blocks the agent loop.
|
||||
//
|
||||
// Required environment (injected by the OpenRig daemon when launching the agent):
|
||||
// OPENRIG_SESSION_NAME or RIGGED_SESSION_NAME - tmux session id
|
||||
// OPENRIG_NODE_ID or RIGGED_NODE_ID - node id in the rig topology
|
||||
// OPENRIG_RUNTIME or RIGGED_RUNTIME - "claude-code" | "codex" | etc.
|
||||
// OPENRIG_URL or RIGGED_URL - daemon base URL
|
||||
// OPENRIG_ACTIVITY_HOOK_TOKEN or RIGGED_ACTIVITY_HOOK_TOKEN - bearer auth
|
||||
|
||||
async function readStdin() {
|
||||
return new Promise((resolve) => {
|
||||
let data = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
data += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => resolve(data));
|
||||
process.stdin.on("error", () => resolve(""));
|
||||
});
|
||||
}
|
||||
|
||||
function parseJson(value) {
|
||||
try {
|
||||
const parsed = JSON.parse(value || "{}");
|
||||
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
||||
} catch {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function firstString(...values) {
|
||||
for (const value of values) {
|
||||
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildOpenRigPayload(providerPayload, env = process.env, now = () => new Date()) {
|
||||
const sessionName = firstString(env.OPENRIG_SESSION_NAME, env.RIGGED_SESSION_NAME);
|
||||
const nodeId = firstString(env.OPENRIG_NODE_ID, env.RIGGED_NODE_ID);
|
||||
const runtime = firstString(env.OPENRIG_RUNTIME, env.RIGGED_RUNTIME);
|
||||
const hookEvent = firstString(
|
||||
providerPayload.hookEvent,
|
||||
providerPayload.hookEventName,
|
||||
providerPayload.hook_event_name,
|
||||
providerPayload.event,
|
||||
providerPayload.eventName
|
||||
);
|
||||
|
||||
if ((!sessionName && !nodeId) || !runtime || !hookEvent) return null;
|
||||
|
||||
const subtype = firstString(
|
||||
providerPayload.subtype,
|
||||
providerPayload.notification_type,
|
||||
providerPayload.notificationType,
|
||||
providerPayload.tool_name,
|
||||
providerPayload.toolName,
|
||||
providerPayload.source,
|
||||
providerPayload.matcher
|
||||
);
|
||||
|
||||
return {
|
||||
sessionName,
|
||||
nodeId,
|
||||
runtime,
|
||||
hookEvent,
|
||||
subtype,
|
||||
occurredAt: now().toISOString(),
|
||||
};
|
||||
}
|
||||
|
||||
async function postHookPayload(payload, env = process.env) {
|
||||
const baseUrl = firstString(env.OPENRIG_URL, env.RIGGED_URL);
|
||||
const token = firstString(env.OPENRIG_ACTIVITY_HOOK_TOKEN, env.RIGGED_ACTIVITY_HOOK_TOKEN);
|
||||
if (!baseUrl || !token || !payload || typeof fetch !== "function") return;
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), 1500);
|
||||
try {
|
||||
await fetch(new URL("/api/activity/hooks", baseUrl).toString(), {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
"authorization": `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
signal: controller.signal,
|
||||
});
|
||||
} catch {
|
||||
// Provider hooks must not block the agent loop if OpenRig is unavailable.
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const providerPayload = parseJson(await readStdin());
|
||||
const payload = buildOpenRigPayload(providerPayload);
|
||||
await postHookPayload(payload);
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch(() => {});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
buildOpenRigPayload,
|
||||
parseJson,
|
||||
postHookPayload,
|
||||
};
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
---
|
||||
name: agent-startup-and-context-ingestion
|
||||
description: Use when designing or auditing how an agent becomes useful after launch — AGENTS.md overlays, role files, skills, rig specs, workflow specs, startup checklists, refocus messages, "rig context" surface. Covers the 4 failure modes that make startup context fail (old rig spec misses current operating mode; current agents never told about new guidance; startup file as dumping ground; orchestrator transmits implementation without preserving product intent).
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Translated from openrig-work/primitives/knowledge-and-context/agent-startup-and-context-ingestion.md (68 lines, design-consolidation). Cross-references the cross-runtime restore/reentry packet standard v0 + externalized memory surfaces convention.
|
||||
sibling_skills:
|
||||
- claude-compaction-restore
|
||||
- mental-model-ha
|
||||
- scope-recovery
|
||||
- session-compaction-and-restore
|
||||
- agent-starters
|
||||
- composable-priming-packs
|
||||
- session-source-fork
|
||||
- seat-continuity-and-handover
|
||||
- claude-compact-in-place
|
||||
- pre-maintenance-agent-preservation
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Agent Startup and Context Ingestion
|
||||
|
||||
How an agent becomes useful after launch: **AGENTS.md overlays, role
|
||||
files, skills, rig specs, workflow specs, startup checklists, refocus
|
||||
messages**, and any future "rig context" command or skill-librarian
|
||||
surface.
|
||||
|
||||
It is the current concrete startup path inside the broader
|
||||
context-engineering-and-retrieval primitive. Startup gets a seat into
|
||||
the right initial shape; context engineering is the larger question of
|
||||
how a seat gets the right context for the work it is doing right now.
|
||||
|
||||
**Most coordination failures are not tool failures; they are context
|
||||
failures.** Agents need to know their role, operating mode, coordination
|
||||
convention, boundaries, and current product intent. If startup context
|
||||
is scattered or stale, agents execute the wrong thing very efficiently.
|
||||
|
||||
## Use this when
|
||||
|
||||
- Authoring a new agent's startup files (role / culture / startup-context)
|
||||
- Refreshing a seat that's been running on stale guidance
|
||||
- Auditing whether agents got the current operating mode (not just what's in old files)
|
||||
- Designing the orchestrator → next-agent context-transmission shape
|
||||
- Building a startup map for OpenRig-building rigs
|
||||
|
||||
## Don't use this when
|
||||
|
||||
- The agent is being created via Agent Starter — the starter's manifest carries startup context
|
||||
- The work is artifact-backed mental-model rebuild from a packet — that's `session-compaction-and-restore`
|
||||
- The intent is to ship reusable startup content as a skill — that's `writing-skills-for-openrig`
|
||||
|
||||
## Failure modes (4)
|
||||
|
||||
1. **A new agent starts from an old rig spec and misses the current operating mode.** Specs go stale; current state must be visible at startup, not just historical config.
|
||||
2. **Guidance is written to a file that future agents read, but current agents are never told.** File edits don't propagate to running sessions. Cultural rollout (broadcast + fleet-changes-feed) is needed alongside file edits.
|
||||
3. **A startup file becomes a dumping ground and loses the map-to-canonical-sources role.** Startup should point AT canonical sources; it shouldn't TRY to be one.
|
||||
4. **The orchestrator transmits implementation instructions without preserving product intent.** Instructions decay; intent travels.
|
||||
|
||||
## Proof standard
|
||||
|
||||
Startup proof should:
|
||||
|
||||
1. **Launch or refresh** a seat
|
||||
2. **Inspect** what it actually read
|
||||
3. **Verify** it can state the current role, mode, active constraints, and next handoff convention **in its own words**
|
||||
|
||||
## Cross-runtime startup paths (5; do not collapse)
|
||||
|
||||
When a seat's startup is artifact-backed mental-model rebuild (active-work
|
||||
reentry case, distinct from reusable Agent Starters / priming packs):
|
||||
|
||||
- See the **cross-runtime restore/reentry packet standard v0**
|
||||
- Source-trust ranking applies: **`rig whoami` > target rigspec > bounded latest transcript > full transcript > touched-files > `restore-summary.json`**
|
||||
|
||||
## Memory surfaces consumed at startup
|
||||
|
||||
Per the externalized-memory-surfaces convention's 13-row inventory:
|
||||
|
||||
- **Row 7** — AGENTS/role/CULTURE/startup overlays (primary)
|
||||
- **Row 3** — startup replay context (primary)
|
||||
- **Row 12** — restore/reentry packets and Agent Starters (cross-runtime startup path)
|
||||
|
||||
The umbrella's authority-rank + permission-posture columns govern which
|
||||
surfaces a startup ingestion path can write vs only read.
|
||||
|
||||
## Startup files vs skills (the distinction)
|
||||
|
||||
Per `agent-startup-guide.md` (product reference doc) and the team handbook:
|
||||
|
||||
| Startup files | Skills |
|
||||
|---|---|
|
||||
| Rig-specific, role-specific identity | Reusable SOPs / methodology / knowledge |
|
||||
| Tell agent WHO it is, WHAT it's working on, HOW this team operates | Tell agent HOW to do something (transferable across rigs) |
|
||||
| Examples: `role.md`, `CULTURE.md`, `startup/context.md` | Examples: `openrig-user`, `test-driven-development`, `vault-user` |
|
||||
| Authored per-rig | Authored once, used everywhere |
|
||||
|
||||
Don't put skill content in startup files. Don't put identity content
|
||||
in skills. The 7-layer additive startup model (agent / profile / rig /
|
||||
culture / pod / member / operator) handles the layering.
|
||||
|
||||
## See also
|
||||
|
||||
- `writing-skills-for-openrig` skill — authoring discipline for skill content (what doesn't belong in startup)
|
||||
- `forming-an-openrig-mental-model` skill — orientation for new agents
|
||||
- `session-compaction-and-restore` skill — restore-time startup ingestion
|
||||
- `agent-starters` skill — reusable starter manifests that compose startup context
|
||||
- `composable-priming-packs` skill — manifests that produce primed sessions
|
||||
- `externalized-memory-surfaces` skill — umbrella convention for memory surfaces this primitive consumes
|
||||
- `openrig/docs/reference/agent-startup-guide.md` (product reference doc; not a skill) — the 7-layer additive startup model + delivery hints
|
||||
- `agent-startup-and-context-ingestion` skill — primitive dossier with current-state notes + next-slice scope
|
||||
@@ -0,0 +1,218 @@
|
||||
---
|
||||
name: claude-compact-in-place
|
||||
description: Use when a Claude Code seat in an OpenRig topology needs compact-in-place recovery, auto-compaction policy, or verification that a compacted Claude actually rebuilt its mental model.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Graduated 2026-05-04 from openrig-work/skills/workspace/from-substrate-wip/claude-compact-in-place/.
|
||||
Substrate-WIP triage was deferred per founder direction; this skill is graduated out-of-band as part of Phase 3d N2 expansion (substrate-WIP graduation; out-of-band per dogfood).
|
||||
sibling_skills:
|
||||
- claude-compaction-restore
|
||||
- mental-model-ha
|
||||
- scope-recovery
|
||||
- session-compaction-and-restore
|
||||
- agent-startup-and-context-ingestion
|
||||
- agent-starters
|
||||
- composable-priming-packs
|
||||
- session-source-fork
|
||||
- seat-continuity-and-handover
|
||||
- pre-maintenance-agent-preservation
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Claude Compact In Place
|
||||
|
||||
## Purpose
|
||||
|
||||
Compact-in-place means keeping the same Claude seat/session role alive while
|
||||
refreshing its context, then rebuilding its working mental model from durable
|
||||
evidence. It is not succession, not a fresh spawn, and not a vibe-based
|
||||
"continue from summary" reset.
|
||||
|
||||
The safe pattern has two separate layers:
|
||||
|
||||
- **Restore mechanism:** PreCompact seed, JSONL transcript curation,
|
||||
touched-file inventory, queue/session-log/root-doc re-prime, and
|
||||
SessionStart/compact context injection when installed.
|
||||
- **Acceptance mechanism:** a separate marshal or peer challenges the restored
|
||||
Claude with a read-depth audit before accepting "RESTORED."
|
||||
|
||||
Do not collapse these. The live H41 compaction wave showed Claude will often
|
||||
self-report a good restore while key files were only partially read, injected by
|
||||
context, or not read at all.
|
||||
|
||||
## When To Use
|
||||
|
||||
Use this for Claude Code seats that:
|
||||
|
||||
- have just run `/compact`
|
||||
- are about to be compacted in place
|
||||
- are under an auto-compact policy
|
||||
- claim they restored after compaction
|
||||
- need a marshal to verify restore quality
|
||||
|
||||
Do not use this for Codex context management. Codex compaction behavior is
|
||||
different and normally does not need this marshal protocol.
|
||||
|
||||
## Operating Policy
|
||||
|
||||
Default OpenRig target:
|
||||
|
||||
- Preemptively compact Claude around **400k tokens** on a 1M-context session
|
||||
rather than waiting for a context wall.
|
||||
- If Claude Code cannot trigger directly at a token threshold, use an external
|
||||
watchdog/monitor that reads `rig whoami --json` or equivalent context usage
|
||||
and sends `/compact` at the threshold.
|
||||
- Only enable unattended auto-compact where the restore hook and audit path are
|
||||
installed and dogfooded.
|
||||
- Do not compact multiple Claude seats at once unless the topology has spare
|
||||
non-Claude or already-restored marshal capacity.
|
||||
|
||||
The 400k threshold is a reliability posture, not a magic number. Adjust it by
|
||||
model/context size and the amount of high-value work in-flight.
|
||||
|
||||
## Required Artifacts
|
||||
|
||||
Before accepting a restored Claude, the marshal needs these surfaces:
|
||||
|
||||
- `rig whoami --json` for canonical identity
|
||||
- Claude JSONL transcript under `~/.claude/projects/.../<session-id>.jsonl`
|
||||
- restore output from the `claude-compaction-restore` skill:
|
||||
`node ~/.claude/skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs --out /tmp/claude-compaction-restore --json`
|
||||
- generated `/tmp/claude-compaction-restore/.../restore-instructions.md`
|
||||
- generated `/tmp/claude-compaction-restore/.../touched-files.md`
|
||||
- the seat queue file and active queue packets
|
||||
- recent `rig transcript <seat>` or session logs
|
||||
- rig bootstrap files such as `AGENTS.md`, `CLAUDE.md`, `CULTURE.md`, role guidance
|
||||
- relevant OpenRig as-built docs and codemaps
|
||||
- any workstream tracker that defines the current arc
|
||||
|
||||
For OpenRig product/control-plane work, these are mandatory full-read anchors:
|
||||
|
||||
- `~/code/projects/openrig/docs/as-built/architecture.md`
|
||||
- `~/code/projects/openrig/docs/as-built/codemap.md`
|
||||
- `~/code/projects/openrig/docs/as-built/cli-reference.md`
|
||||
|
||||
## Compact-In-Place Flow
|
||||
|
||||
1. **Preflight the seat.** Confirm the target is the intended canonical session:
|
||||
`rig whoami --session <seat> --json`. Capture the pane and read its latest
|
||||
queue/checkpoint state.
|
||||
2. **Create or locate a restore seed.** Prefer the PreCompact hook plus the
|
||||
JSONL restore script. If no pre-save exists, use JSONL transcript, queue
|
||||
state, and touched files as the seed.
|
||||
3. **Send `/compact`.** Use `rig send <seat> "/compact" --verify --force` only
|
||||
when the human/policy permits compaction for that seat. Watch the pane until
|
||||
the command has landed and compaction completes.
|
||||
4. **Send the restore prompt.** Tell the compacted Claude to run the restore
|
||||
script, read the generated instructions and touched-files list, read the
|
||||
mandatory anchors, reconcile active queue items, and return a durable proof.
|
||||
5. **Reject vague proof.** Do not accept "all docs read", "restored cleanly", or
|
||||
"I read the important files" without an exact read-depth ledger.
|
||||
6. **Ask follow-up questions.** Force the target to distinguish full reads from
|
||||
partial reads, injected context, grep/tail evidence, and unread files.
|
||||
7. **Accept or correct.** A seat is restored only after the marshal accepts the
|
||||
corrected ledger and records caveats. If gaps remain, send a targeted
|
||||
correction and keep the seat in restore verification.
|
||||
|
||||
## Required Restore Proof
|
||||
|
||||
The target Claude must return a durable queue packet or otherwise persisted
|
||||
proof containing this table shape:
|
||||
|
||||
| Surface | Source Of Ask | Read Depth | Evidence |
|
||||
|---|---|---|---|
|
||||
| `rig whoami --json` | marshal | executed/full output parsed | canonical seat confirmed |
|
||||
| `<path>` | hook/marshal/touched-files | FULL / TARGETED / GREP / INJECTED / NOT-READ / NOT-PRESENT | lines/chunks/status |
|
||||
|
||||
Allowed read-depth terms:
|
||||
|
||||
- `FULL`: the whole file was read after compaction using the Read tool or
|
||||
equivalent full line-range passes.
|
||||
- `TARGETED`: only specific ranges, head/tail, or selected sections were read.
|
||||
- `GREP`: only search excerpts were read.
|
||||
- `INJECTED`: content was present from Claude/system/hook context but was not
|
||||
freshly read from disk.
|
||||
- `NOT-READ`: file exists but was not read.
|
||||
- `NOT-PRESENT`: requested surface does not exist.
|
||||
|
||||
The proof must also include:
|
||||
|
||||
- canonical identity and session name
|
||||
- restore packet path or JSONL path
|
||||
- active queue items and exact next action
|
||||
- recent-work files selected from `touched-files.md`
|
||||
- files intentionally not read and why they are not restore-critical
|
||||
- blockers, if any
|
||||
- status: `awaiting marshal acceptance`, `blocked`, or `restored`
|
||||
|
||||
The target should not claim `RESTORED` until the marshal accepts the proof.
|
||||
|
||||
## Follow-Up Questions
|
||||
|
||||
Ask these after the first proof, especially if it sounds clean:
|
||||
|
||||
1. Which files were you explicitly asked to read, and which of those did you read
|
||||
in full after compaction?
|
||||
2. Which files did you only receive through hook/session/system injection?
|
||||
3. Which files did you read partially? Give exact line ranges or chunks.
|
||||
4. Which generated `touched-files.md` entries did you classify as important, and
|
||||
which did you skip?
|
||||
5. Did any Read tool call fail due to file size? If yes, did you retry with
|
||||
offset/range reads until full coverage was achieved?
|
||||
6. Which queue items changed between your checkpoint and now? Cite the durable
|
||||
evidence that reconciles the difference.
|
||||
7. Name one non-obvious detail from each mandatory anchor that affects your next
|
||||
action.
|
||||
8. What would be unsafe for you to work on before additional reads?
|
||||
|
||||
If the answer reveals partial or injected-only coverage for a mandatory anchor,
|
||||
reject the proof and ask for a replacement audit.
|
||||
|
||||
## Acceptance Standard
|
||||
|
||||
Accept the restore only when:
|
||||
|
||||
- the seat identity matches the preflight target
|
||||
- the JSONL/restore packet was used or the absence is explicitly explained
|
||||
- mandatory anchors are FULL or explicitly waived by a human/marshal
|
||||
- important recent-work files are either FULL or explicitly classified as
|
||||
non-critical with rationale
|
||||
- every injected/partial/not-read surface is named, not hidden
|
||||
- queue state is reconciled against durable evidence
|
||||
- the target has not resumed real work before acceptance
|
||||
|
||||
Partial restore can be acceptable for standby if the unread items are honestly
|
||||
classified and not needed for the next action. Partial restore is not acceptable
|
||||
for architecture decisions, code review, product direction, or release calls.
|
||||
|
||||
## Live H41 Lessons
|
||||
|
||||
Observed during the 2026-04-23 rolling Claude compaction wave:
|
||||
|
||||
- `platform-architect2@kernel` initially claimed restore, then audit v2 admitted
|
||||
the first proof missed OpenRig as-built docs, skill docs, `touched-files.md`,
|
||||
and several recent-work files.
|
||||
- `spec-writer@openrig-pm` needed a final replacement audit before acceptance;
|
||||
earlier reports conflated full reads, injected context, and unread files.
|
||||
- `research-synthesizer@openrig-pm` had its first proof rejected because
|
||||
`architecture.md`, `codemap.md`, the compaction manifest, and `progress.md`
|
||||
were partial/injected; the corrected proof closed those gaps and waited for
|
||||
marshal acceptance before claiming restored.
|
||||
|
||||
Conclusion: hook injection and Claude's compaction summary are useful restore
|
||||
inputs, but the reliable product primitive is **restore plus adversarial
|
||||
read-depth audit**.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- Accepting a generic "all docs read" statement.
|
||||
- Treating context injection as equivalent to a fresh full read.
|
||||
- Reading only the first chunk of a large codemap and calling it full.
|
||||
- Skipping `touched-files.md` because it is large.
|
||||
- Letting the compacted Claude resume real work before marshal acceptance.
|
||||
- Compacting several Claude seats at once and losing marshal capacity.
|
||||
- Applying this protocol to Codex without evidence of Codex-specific failure.
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
---
|
||||
name: claude-compaction-restore
|
||||
description: Use when a Claude Code session has just compacted, is about to compact, reached context limit, resumed after /compact, or needs to rebuild its working mental model from Claude JSONL transcripts and touched files.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Graduated 2026-05-04 from openrig-work/skills/workspace/from-home-skills/claude-compaction-restore/
|
||||
(which was a verbatim copy of ~/.agents/skills/claude-compaction-restore/).
|
||||
Originally authored as a personal skill; promoted as load-bearing for any
|
||||
OpenRig seat that may compact. PreCompact hook is actively wired at
|
||||
~/.claude/settings.json on the primary host.
|
||||
transfer_test: practical-passed-2026-05-04
|
||||
transfer_test_notes: |
|
||||
Skill was actively used during the 2026-05-04 conversation by the
|
||||
compacted skills-architect@skill-library seat. The PreCompact hook
|
||||
generated the restore packet; the post-compaction agent ran
|
||||
restore-from-jsonl.mjs, read restore-instructions.md and touched-files.md,
|
||||
identified important files, and reported "restored from packet at <path>;
|
||||
resumed at step <X>." That's a real-work transfer-test pass; formal
|
||||
pressure-scenario sub-agent test still pending per writing-skills TDD
|
||||
discipline.
|
||||
sibling_skills:
|
||||
- mental-model-ha
|
||||
- scope-recovery
|
||||
- session-compaction-and-restore
|
||||
- agent-startup-and-context-ingestion
|
||||
- agent-starters
|
||||
- composable-priming-packs
|
||||
- session-source-fork
|
||||
- seat-continuity-and-handover
|
||||
- claude-compact-in-place
|
||||
- pre-maintenance-agent-preservation
|
||||
---
|
||||
|
||||
# Claude Compaction Restore
|
||||
|
||||
## Situation
|
||||
|
||||
You have just been compacted, or you are preparing a Claude Code session
|
||||
for compaction. Treat memory after compaction as unreliable. Do not
|
||||
continue real work from vibes.
|
||||
|
||||
Restore from durable evidence:
|
||||
- the Claude JSONL transcript
|
||||
- files touched during the session, especially Markdown state/planning files
|
||||
- project root docs and as-built documentation
|
||||
- repo code maps before code/review work
|
||||
|
||||
## Fast Path
|
||||
|
||||
Run the restore script from this skill:
|
||||
|
||||
```bash
|
||||
node ~/.claude/skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs --out /tmp/claude-compaction-restore
|
||||
```
|
||||
|
||||
If you know the JSONL path, pass it explicitly:
|
||||
|
||||
```bash
|
||||
node ~/.claude/skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs /path/to/session.jsonl --out /tmp/claude-compaction-restore
|
||||
```
|
||||
|
||||
The script writes:
|
||||
- `transcript.txt` — readable transcript reconstructed from JSONL
|
||||
- `touched-files.md` — ranked file list, with Markdown and written files highlighted
|
||||
- `restore-instructions.md` — checklist for the compacted agent
|
||||
- `restore-summary.json` — machine-readable summary
|
||||
|
||||
## Restore Protocol
|
||||
|
||||
1. Read `restore-instructions.md`.
|
||||
2. Read `touched-files.md`.
|
||||
3. Pause and answer: which files do you recognize as important to the work and project state?
|
||||
4. Read each important file in full. Prioritize Markdown files that were written or tracked by file-history snapshots.
|
||||
5. Read project root docs in full when present: `CLAUDE.md`, `AGENTS.md`, `README.md`.
|
||||
6. Read as-built docs and code maps in full before product work, code review, or architecture decisions. Common paths: `docs/as-built/`, `docs/codemap*`, `docs/architecture*`.
|
||||
7. Only resume work after stating: "restored from packet at <path>; resumed at step <X>."
|
||||
|
||||
## Hook Usage
|
||||
|
||||
Claude Code currently has a `PreCompact` hook, not a dependable
|
||||
post-compact hook. Use the pre-compact hook to prepare the packet
|
||||
before compaction and inject the packet path into the compacted
|
||||
context:
|
||||
|
||||
```bash
|
||||
node ~/.claude/skills/claude-compaction-restore/scripts/precompact-hook.mjs
|
||||
```
|
||||
|
||||
The hook reads Claude hook JSON from stdin, creates a restore packet
|
||||
under `/tmp/claude-compaction-restore/`, and returns a `systemMessage`
|
||||
telling the compacted agent what to read.
|
||||
|
||||
To wire the hook, add to `~/.claude/settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"PreCompact": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "node ~/.claude/skills/claude-compaction-restore/scripts/precompact-hook.mjs"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Do not silently launch fresh after compaction.
|
||||
- Do not trust fuzzy recollection over files.
|
||||
- Do not skip as-built docs or codemaps before code/review work.
|
||||
- Do not treat a generated touched-file list as perfect. It is a triage aid; the agent must still recognize and select important files.
|
||||
- Do not skip the explicit "restored from packet at <path>; resumed at step <X>" announcement. It's the sentinel that signals genuine restoration vs performed restoration.
|
||||
|
||||
## Common Failure Modes (load-bearing — read these)
|
||||
|
||||
1. **Confidently-wrong restoration**: agent claims to have restored but actually only read the touched-files list, not the files themselves. The "restored from packet" announcement should be backed by specific file content cited in subsequent work. If the agent's first post-restore turn is generic / doesn't reference specific file content, restoration didn't actually happen.
|
||||
2. **Skipping the project root docs**: agent reads only the touched-files list; misses `CLAUDE.md` / `AGENTS.md` / `README.md` which carry persistent context. The restore-instructions.md checklist explicitly names these — follow it.
|
||||
3. **Reading partial then continuing**: agent reads 2-3 files and starts working before reading all important ones. Better to over-read than to drift on partial recovery.
|
||||
4. **Treating the restore packet as exhaustive**: the packet is a triage aid. Important state may live in files NOT in the touched-files list (e.g., recently-relevant skills, doctrine docs, mission state). Use the packet as a starting point, not a complete inventory.
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const skillRoot = path.resolve(new URL("..", import.meta.url).pathname);
|
||||
const restoreScript = path.join(skillRoot, "scripts", "restore-from-jsonl.mjs");
|
||||
const outRoot = "/tmp/claude-compaction-restore";
|
||||
|
||||
function emit(payload) {
|
||||
process.stdout.write(`${JSON.stringify(payload)}\n`);
|
||||
}
|
||||
|
||||
function readHookInput() {
|
||||
const raw = fs.readFileSync(0, "utf8").trim();
|
||||
if (!raw) return {};
|
||||
return JSON.parse(raw);
|
||||
}
|
||||
|
||||
try {
|
||||
const input = readHookInput();
|
||||
const args = [restoreScript, "--out", outRoot, "--json"];
|
||||
if (input.cwd) args.push("--cwd", input.cwd);
|
||||
if (input.transcript_path && input.transcript_path.endsWith(".jsonl") && fs.existsSync(input.transcript_path)) {
|
||||
args.push(input.transcript_path);
|
||||
}
|
||||
|
||||
const result = spawnSync("node", args, { encoding: "utf8" });
|
||||
if (result.status !== 0) {
|
||||
emit({
|
||||
continue: true,
|
||||
systemMessage: `Claude compaction restore packet generation failed: ${(result.stderr || result.stdout || "unknown error").trim()}. After compaction, load the claude-compaction-restore skill and run restore-from-jsonl manually.`,
|
||||
});
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(result.stdout);
|
||||
emit({
|
||||
continue: true,
|
||||
systemMessage: `Pre-compaction restore seed packet prepared at ${parsed.outputDir}. After compaction, immediately restore before doing any other work: load/read the claude-compaction-restore skill, run "node ~/.claude/skills/claude-compaction-restore/scripts/restore-from-jsonl.mjs --out /tmp/claude-compaction-restore --json" yourself, read the generated restore-instructions.md, read the generated touched-files.md, identify remembered important files, read those files in full, read root/as-built/codemap docs before real work, then report exactly "restored from packet at <path>; resumed at step <X>" with the files you read in full. If any step fails, report the failure explicitly.`,
|
||||
});
|
||||
} catch (error) {
|
||||
emit({
|
||||
continue: true,
|
||||
systemMessage: `Claude compaction restore hook errored: ${error.message}. After compaction, load the claude-compaction-restore skill and run restore-from-jsonl manually.`,
|
||||
});
|
||||
}
|
||||
+433
@@ -0,0 +1,433 @@
|
||||
#!/usr/bin/env node
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const TEXT_LIMIT = 4000;
|
||||
const DEFAULT_OUT = "/tmp/claude-compaction-restore";
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = {
|
||||
jsonl: null,
|
||||
out: DEFAULT_OUT,
|
||||
cwd: process.cwd(),
|
||||
json: false,
|
||||
};
|
||||
|
||||
for (let i = 0; i < argv.length; i += 1) {
|
||||
const arg = argv[i];
|
||||
if (arg === "--jsonl") args.jsonl = argv[++i];
|
||||
else if (arg === "--out") args.out = argv[++i];
|
||||
else if (arg === "--cwd") args.cwd = argv[++i];
|
||||
else if (arg === "--json") args.json = true;
|
||||
else if (!arg.startsWith("-") && !args.jsonl) args.jsonl = arg;
|
||||
else throw new Error(`unknown argument: ${arg}`);
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
function readJsonLines(file) {
|
||||
const raw = fs.readFileSync(file, "utf8");
|
||||
return raw
|
||||
.split(/\r?\n/)
|
||||
.filter(Boolean)
|
||||
.map((line, index) => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch (error) {
|
||||
return { type: "parse-error", line: index + 1, error: String(error), raw: line };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function findLatestJsonl(cwd) {
|
||||
const projectsRoot = path.join(os.homedir(), ".claude", "projects");
|
||||
if (!fs.existsSync(projectsRoot)) return null;
|
||||
|
||||
const encoded = cwd ? cwd.replaceAll("/", "-") : null;
|
||||
const candidates = [];
|
||||
const roots = [];
|
||||
|
||||
if (encoded) {
|
||||
const exact = path.join(projectsRoot, encoded);
|
||||
if (fs.existsSync(exact)) roots.push(exact);
|
||||
}
|
||||
roots.push(projectsRoot);
|
||||
|
||||
for (const root of roots) {
|
||||
const result = spawnSync("find", [root, "-name", "*.jsonl", "-maxdepth", root === projectsRoot ? "4" : "2"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (result.status !== 0 && !result.stdout) continue;
|
||||
for (const file of result.stdout.split(/\r?\n/).filter(Boolean)) {
|
||||
try {
|
||||
const stat = fs.statSync(file);
|
||||
candidates.push({ file, mtimeMs: stat.mtimeMs });
|
||||
} catch {
|
||||
// Ignore raced/deleted transcript files.
|
||||
}
|
||||
}
|
||||
if (candidates.length) break;
|
||||
}
|
||||
|
||||
candidates.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
||||
return candidates[0]?.file ?? null;
|
||||
}
|
||||
|
||||
function stringifyContent(content) {
|
||||
if (typeof content === "string") return content;
|
||||
if (Array.isArray(content)) {
|
||||
return content
|
||||
.map((block) => {
|
||||
if (typeof block === "string") return block;
|
||||
if (block?.type === "text") return block.text ?? "";
|
||||
if (block?.type === "tool_use") return `[tool_use:${block.name}] ${JSON.stringify(block.input ?? {})}`;
|
||||
if (block?.type === "tool_result") return `[tool_result] ${truncate(stringifyContent(block.content), TEXT_LIMIT)}`;
|
||||
return JSON.stringify(block);
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
if (content == null) return "";
|
||||
return JSON.stringify(content);
|
||||
}
|
||||
|
||||
function truncate(text, max) {
|
||||
const s = String(text ?? "");
|
||||
if (s.length <= max) return s;
|
||||
return `${s.slice(0, max)}\n[...truncated ${s.length - max} chars...]`;
|
||||
}
|
||||
|
||||
function walkStrings(value, acc = []) {
|
||||
if (typeof value === "string") acc.push(value);
|
||||
else if (Array.isArray(value)) value.forEach((item) => walkStrings(item, acc));
|
||||
else if (value && typeof value === "object") Object.values(value).forEach((item) => walkStrings(item, acc));
|
||||
return acc;
|
||||
}
|
||||
|
||||
function normalizeFile(candidate, cwd) {
|
||||
let file = candidate.trim().replace(/[),.;:'"`\]\}>]+$/g, "").replace(/^[('"`<\[]+/g, "");
|
||||
if (!file || file.includes("\n")) return null;
|
||||
if (/\.(?:md|mdx|txt|json|jsonl|yaml|yml|toml|ts|tsx|js|jsx|mjs|cjs|py|sh|rs|go|sql|css|scss|html|xml|svg|csv|log|lock)\//i.test(file)) return null;
|
||||
if (file.startsWith("~")) file = path.join(os.homedir(), file.slice(1));
|
||||
let normalized = null;
|
||||
if (file.startsWith("/")) normalized = path.normalize(file);
|
||||
else if (file.startsWith("./") || file.startsWith("../")) normalized = path.normalize(path.resolve(cwd, file));
|
||||
else if (/^[A-Za-z0-9_@.+-][A-Za-z0-9_@.+/\-:]*\.[A-Za-z0-9]+$/.test(file)) {
|
||||
normalized = path.normalize(path.resolve(cwd, file));
|
||||
} else if (/^(CLAUDE|AGENTS|README|DESIGN|CULTURE|MEMORY)\.md$/.test(file)) {
|
||||
normalized = path.normalize(path.resolve(cwd, file));
|
||||
}
|
||||
|
||||
if (!normalized) return null;
|
||||
try {
|
||||
if (fs.existsSync(normalized) && fs.statSync(normalized).isDirectory()) return null;
|
||||
} catch {
|
||||
// Keep the candidate if stat races; the restore agent can decide.
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function extractPaths(text, cwd) {
|
||||
const paths = new Set();
|
||||
const s = String(text ?? "");
|
||||
const absolute = /(?:^|[\s"'(<\[])(\/(?:Users|private|tmp|var|opt|Volumes)\/[^\s"'()\]<>]+)/g;
|
||||
const relative = /(?:^|[\s"'(<\[])((?:\.{1,2}\/)?[A-Za-z0-9_@.+-][A-Za-z0-9_@.+/\-:]*\.(?:md|mdx|txt|json|jsonl|yaml|yml|toml|ts|tsx|js|jsx|mjs|cjs|py|sh|rs|go|sql|css|scss|html|xml|svg|csv|log|lock))(?:$|[\s"')>\],.;:])/g;
|
||||
const namedMarkdown = /(?:^|[\s"'(<\[])((?:CLAUDE|AGENTS|README|DESIGN|CULTURE|MEMORY)\.md)(?:$|[\s"')>\],.;:])/g;
|
||||
|
||||
for (const regex of [absolute, relative, namedMarkdown]) {
|
||||
let match;
|
||||
while ((match = regex.exec(s)) !== null) {
|
||||
const normalized = normalizeFile(match[1], cwd);
|
||||
if (normalized) paths.add(normalized);
|
||||
}
|
||||
}
|
||||
return [...paths];
|
||||
}
|
||||
|
||||
function createRegistry() {
|
||||
const files = new Map();
|
||||
let sequence = 0;
|
||||
|
||||
return {
|
||||
add(file, kind, source) {
|
||||
if (!file) return;
|
||||
const existing = files.get(file) ?? {
|
||||
path: file,
|
||||
kinds: {},
|
||||
sources: new Set(),
|
||||
firstSeen: sequence,
|
||||
lastSeen: sequence,
|
||||
};
|
||||
existing.kinds[kind] = (existing.kinds[kind] ?? 0) + 1;
|
||||
existing.sources.add(source);
|
||||
existing.lastSeen = sequence;
|
||||
files.set(file, existing);
|
||||
},
|
||||
tick() {
|
||||
sequence += 1;
|
||||
},
|
||||
values() {
|
||||
return [...files.values()].map((entry) => ({
|
||||
...entry,
|
||||
sources: [...entry.sources].slice(0, 8),
|
||||
}));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function toolKind(name, input) {
|
||||
if (["Write", "Edit", "MultiEdit", "NotebookEdit"].includes(name)) return "written";
|
||||
if (name === "Read") return "read";
|
||||
if (["Grep", "Glob", "LS"].includes(name)) return "discovered";
|
||||
if (name === "Bash") {
|
||||
const command = input?.command ?? "";
|
||||
if (/\b(apply_patch|tee|touch|mkdir|mv|cp|rm)\b|(^|[^>])>{1,2}[^>]/.test(command)) return "shell-write";
|
||||
return "shell-mentioned";
|
||||
}
|
||||
return "mentioned";
|
||||
}
|
||||
|
||||
function scoreFile(entry) {
|
||||
const ext = path.extname(entry.path).toLowerCase();
|
||||
let score = 0;
|
||||
if (entry.kinds.written) score += 40;
|
||||
if (entry.kinds["shell-write"]) score += 30;
|
||||
if (entry.kinds["file-history"]) score += 35;
|
||||
if (ext === ".md" || ext === ".mdx") score += 20;
|
||||
if (entry.kinds.read) score += 10;
|
||||
if (entry.kinds.discovered) score += 4;
|
||||
if (/\/docs\/as-built\//.test(entry.path)) score += 25;
|
||||
if (/(codemap|architecture|CLAUDE|AGENTS|README|DESIGN|CULTURE|session\.log|queue\.md)/i.test(entry.path)) score += 15;
|
||||
score += Math.min(10, Object.values(entry.kinds).reduce((a, b) => a + b, 0));
|
||||
return score;
|
||||
}
|
||||
|
||||
function discoverDocs(cwd) {
|
||||
const candidates = [
|
||||
"CLAUDE.md",
|
||||
"AGENTS.md",
|
||||
"README.md",
|
||||
"docs/as-built",
|
||||
"docs",
|
||||
];
|
||||
const docs = new Set();
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const full = path.resolve(cwd, candidate);
|
||||
if (!fs.existsSync(full)) continue;
|
||||
const stat = fs.statSync(full);
|
||||
if (stat.isFile()) docs.add(full);
|
||||
if (stat.isDirectory()) {
|
||||
const result = spawnSync("find", [full, "-maxdepth", candidate === "docs" ? "2" : "1", "-type", "f", "-name", "*.md"], {
|
||||
encoding: "utf8",
|
||||
});
|
||||
for (const file of result.stdout.split(/\r?\n/).filter(Boolean)) {
|
||||
if (/as-built|codemap|architecture|README|overview/i.test(file)) docs.add(path.normalize(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...docs].sort();
|
||||
}
|
||||
|
||||
function analyze(jsonlPath, cwd) {
|
||||
const records = readJsonLines(jsonlPath);
|
||||
const registry = createRegistry();
|
||||
const transcript = [];
|
||||
const cwdCounts = new Map();
|
||||
let sessionId = null;
|
||||
|
||||
for (const record of records) {
|
||||
registry.tick();
|
||||
if (record.sessionId || record.session_id) sessionId = record.sessionId ?? record.session_id;
|
||||
if (record.cwd) {
|
||||
cwdCounts.set(record.cwd, (cwdCounts.get(record.cwd) ?? 0) + 1);
|
||||
cwd = record.cwd;
|
||||
}
|
||||
|
||||
if (record.type === "file-history-snapshot") {
|
||||
const backups = record.snapshot?.trackedFileBackups ?? {};
|
||||
for (const file of Object.keys(backups)) registry.add(path.normalize(file), "file-history", "file-history-snapshot");
|
||||
}
|
||||
|
||||
const message = record.message;
|
||||
if (!message) continue;
|
||||
const role = message.role ?? record.type ?? "unknown";
|
||||
const content = message.content;
|
||||
|
||||
if (typeof content === "string") {
|
||||
transcript.push(`\n## ${role}\n\n${content}`);
|
||||
for (const file of extractPaths(content, cwd)) registry.add(file, "mentioned", `${role}:text`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Array.isArray(content)) continue;
|
||||
const parts = [];
|
||||
for (const block of content) {
|
||||
if (block?.type === "text") {
|
||||
parts.push(block.text ?? "");
|
||||
for (const file of extractPaths(block.text ?? "", cwd)) registry.add(file, "mentioned", `${role}:text`);
|
||||
} else if (block?.type === "tool_use") {
|
||||
const name = block.name ?? "unknown";
|
||||
const input = block.input ?? {};
|
||||
parts.push(`\n[tool_use:${name}]\n${JSON.stringify(input, null, 2)}`);
|
||||
const kind = toolKind(name, input);
|
||||
if (input.file_path) registry.add(normalizeFile(input.file_path, cwd), kind, `tool:${name}:file_path`);
|
||||
if (input.path) registry.add(normalizeFile(input.path, cwd), kind, `tool:${name}:path`);
|
||||
if (input.notebook_path) registry.add(normalizeFile(input.notebook_path, cwd), kind, `tool:${name}:notebook_path`);
|
||||
for (const text of walkStrings(input)) {
|
||||
for (const file of extractPaths(text, cwd)) registry.add(file, kind, `tool:${name}:input`);
|
||||
}
|
||||
} else if (block?.type === "tool_result") {
|
||||
const resultText = stringifyContent(block.content);
|
||||
parts.push(`\n[tool_result]\n${truncate(resultText, TEXT_LIMIT)}`);
|
||||
for (const file of extractPaths(resultText, cwd)) registry.add(file, "mentioned", "tool_result");
|
||||
}
|
||||
}
|
||||
if (parts.length) transcript.push(`\n## ${role}\n\n${parts.join("\n")}`);
|
||||
}
|
||||
|
||||
const rankedFiles = registry
|
||||
.values()
|
||||
.map((entry) => ({ ...entry, score: scoreFile(entry), markdown: [".md", ".mdx"].includes(path.extname(entry.path).toLowerCase()) }))
|
||||
.sort((a, b) => b.score - a.score || a.path.localeCompare(b.path));
|
||||
|
||||
const cwdRanked = [...cwdCounts.entries()].sort((a, b) => b[1] - a[1]);
|
||||
const effectiveCwd = cwdRanked[0]?.[0] ?? cwd;
|
||||
|
||||
return {
|
||||
sessionId,
|
||||
jsonlPath,
|
||||
cwd: effectiveCwd,
|
||||
records: records.length,
|
||||
transcript: transcript.join("\n"),
|
||||
files: rankedFiles,
|
||||
docs: discoverDocs(effectiveCwd),
|
||||
};
|
||||
}
|
||||
|
||||
function markdownFileList(files) {
|
||||
const lines = [];
|
||||
lines.push("# Touched Files");
|
||||
lines.push("");
|
||||
lines.push("Files are ranked for restore relevance. Markdown and written files are the first candidates to read in full.");
|
||||
lines.push("");
|
||||
|
||||
const sections = [
|
||||
["Highest-priority Markdown/state files", (f) => f.markdown && (f.kinds.written || f.kinds["shell-write"] || f.kinds["file-history"])],
|
||||
["Other written/tracked files", (f) => !f.markdown && (f.kinds.written || f.kinds["shell-write"] || f.kinds["file-history"])],
|
||||
["Read/discovered/mentioned files", (f) => !(f.kinds.written || f.kinds["shell-write"] || f.kinds["file-history"])],
|
||||
];
|
||||
|
||||
for (const [title, predicate] of sections) {
|
||||
const group = files.filter(predicate);
|
||||
if (!group.length) continue;
|
||||
lines.push(`## ${title}`);
|
||||
lines.push("");
|
||||
for (const file of group) {
|
||||
const kinds = Object.entries(file.kinds).map(([kind, count]) => `${kind}:${count}`).join(", ");
|
||||
lines.push(`- score ${file.score} — \`${file.path}\` — ${kinds}`);
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function restoreInstructions(summary, outputPaths) {
|
||||
const lines = [];
|
||||
lines.push("# Claude Compaction Restore Instructions");
|
||||
lines.push("");
|
||||
lines.push("You have just been compacted or are restoring a compacted Claude Code seat. Your mental model is compressed and unreliable.");
|
||||
lines.push("");
|
||||
lines.push(`- JSONL transcript: \`${summary.jsonlPath}\``);
|
||||
lines.push(`- Reconstructed transcript: \`${outputPaths.transcript}\``);
|
||||
lines.push(`- Touched-file triage: \`${outputPaths.touchedFiles}\``);
|
||||
lines.push(`- Working directory inferred: \`${summary.cwd}\``);
|
||||
lines.push("");
|
||||
lines.push("## Required Sequence");
|
||||
lines.push("");
|
||||
lines.push("1. Read this file and `touched-files.md`.");
|
||||
lines.push("2. Ask yourself: which files do I recognize as important to the work and project state?");
|
||||
lines.push("3. Mark those files mentally or in a short note.");
|
||||
lines.push("4. Read every important Markdown/state/planning file in full.");
|
||||
lines.push("5. Read project root docs in full when present: `CLAUDE.md`, `AGENTS.md`, `README.md`.");
|
||||
lines.push("6. Read as-built docs and codemaps in full before product work, code review, or architecture decisions.");
|
||||
lines.push("7. State exactly: `restored from packet at <path>; resumed at step <X>`.");
|
||||
lines.push("");
|
||||
lines.push("## Documentation Candidates");
|
||||
lines.push("");
|
||||
if (summary.docs.length) {
|
||||
for (const doc of summary.docs) lines.push(`- \`${doc}\``);
|
||||
} else {
|
||||
lines.push("- No root/as-built/codemap candidates were found automatically. Search manually before resuming code/review work.");
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("## Top File Candidates");
|
||||
lines.push("");
|
||||
for (const file of summary.files.slice(0, 30)) {
|
||||
lines.push(`- \`${file.path}\` — score ${file.score}`);
|
||||
}
|
||||
lines.push("");
|
||||
lines.push("Do not continue from fuzzy memory alone.");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function writeOutputs(summary, outRoot) {
|
||||
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
||||
const base = path.join(outRoot, `${summary.sessionId ?? "unknown-session"}-${stamp}`);
|
||||
fs.mkdirSync(base, { recursive: true });
|
||||
|
||||
const outputPaths = {
|
||||
dir: base,
|
||||
transcript: path.join(base, "transcript.txt"),
|
||||
touchedFiles: path.join(base, "touched-files.md"),
|
||||
instructions: path.join(base, "restore-instructions.md"),
|
||||
summary: path.join(base, "restore-summary.json"),
|
||||
};
|
||||
|
||||
fs.writeFileSync(outputPaths.transcript, summary.transcript || "(no message transcript reconstructed)\n");
|
||||
fs.writeFileSync(outputPaths.touchedFiles, markdownFileList(summary.files));
|
||||
fs.writeFileSync(outputPaths.instructions, restoreInstructions(summary, outputPaths));
|
||||
fs.writeFileSync(outputPaths.summary, JSON.stringify({ ...summary, transcript: undefined, outputPaths }, null, 2));
|
||||
|
||||
return outputPaths;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const args = parseArgs(process.argv.slice(2));
|
||||
const cwd = path.resolve(args.cwd);
|
||||
const jsonlPath = args.jsonl ? path.resolve(args.jsonl) : findLatestJsonl(cwd);
|
||||
if (!jsonlPath) throw new Error("could not find a Claude JSONL transcript; pass one explicitly");
|
||||
if (!fs.existsSync(jsonlPath)) throw new Error(`JSONL transcript not found: ${jsonlPath}`);
|
||||
|
||||
const summary = analyze(jsonlPath, cwd);
|
||||
const outputPaths = writeOutputs(summary, path.resolve(args.out));
|
||||
const result = {
|
||||
ok: true,
|
||||
jsonlPath,
|
||||
outputDir: outputPaths.dir,
|
||||
transcript: outputPaths.transcript,
|
||||
touchedFiles: outputPaths.touchedFiles,
|
||||
instructions: outputPaths.instructions,
|
||||
fileCount: summary.files.length,
|
||||
topFiles: summary.files.slice(0, 12).map((file) => file.path),
|
||||
};
|
||||
|
||||
if (args.json) console.log(JSON.stringify(result, null, 2));
|
||||
else {
|
||||
console.log(`Restore packet: ${result.outputDir}`);
|
||||
console.log(`Instructions: ${result.instructions}`);
|
||||
console.log(`Touched files: ${result.touchedFiles}`);
|
||||
console.log(`Transcript: ${result.transcript}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
main();
|
||||
} catch (error) {
|
||||
console.error(`claude-compaction-restore failed: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
---
|
||||
name: forming-an-openrig-mental-model
|
||||
description: |
|
||||
Use when an agent is newly oriented to OpenRig and needs to form an accurate mental model of the system fast — what rigs are, how skills load, what the topology shapes mean, what the product loop is. For agents booting into a new seat or returning to OpenRig work after time away. NOT for HA pair compaction recovery (use mental-model-ha for that) or for specific operational procedures.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Authored 2026-05-02 in response to founder-observed pattern: agents
|
||||
reaching for mental-model-ha on the words "mental model" when they're
|
||||
actually trying to form an initial mental model of OpenRig. This skill
|
||||
serves the legitimate need that mental-model-ha was being mis-routed for.
|
||||
See lab/skill-management-research/cycles/2026-05-02-forming-an-openrig-mental-model.md.
|
||||
sibling_skills:
|
||||
- openrig-user
|
||||
- openrig-operator
|
||||
- openrig-builder
|
||||
- openrig-architect
|
||||
- openrig-upgrade
|
||||
- ai-dev-workflows
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Forming an OpenRig Mental Model
|
||||
|
||||
You're new to OpenRig — or returning after time away — and you need to
|
||||
quickly understand what kind of system this is, what your seat is, and what
|
||||
the moves are. This skill is the fast on-ramp.
|
||||
|
||||
For depth, read the canonical reference docs the skill points to. This
|
||||
skill's job is to get you *oriented* — accurate enough to operate, fast
|
||||
enough to be useful — not to replace the canonical docs.
|
||||
|
||||
---
|
||||
|
||||
## The 60-second mental model
|
||||
|
||||
OpenRig is a **local control plane for multi-agent coding topologies**. You
|
||||
declare a topology of agents in YAML, boot it with one command, and OpenRig
|
||||
manages tmux sessions, harness lifecycles, transcripts, snapshots, and
|
||||
restoration. When the system goes down, OpenRig snapshots; when it comes
|
||||
back, agents resume their conversations.
|
||||
|
||||
The product loop:
|
||||
|
||||
```
|
||||
down (auto-snapshot) → up <rig-name> (auto-restore) → work → repeat
|
||||
```
|
||||
|
||||
The unit of work is the **rig** — a topology of agents working together as
|
||||
a single system.
|
||||
|
||||
---
|
||||
|
||||
## The four-layer model (where you live)
|
||||
|
||||
Everything in agent engineering happens at one of four layers. **OpenRig
|
||||
operates at Layer 3.**
|
||||
|
||||
| Layer | Name | Analogy | What it is |
|
||||
|---|---|---|---|
|
||||
| L0 | Model | CPU | Foundation model — Claude, GPT, Gemini. Stateless tokens-in/tokens-out. |
|
||||
| L1 | Agent Core | Process loop | The reason-and-act cycle: observe, plan, choose, act, repeat. |
|
||||
| L2 | Harness | Container / OS | Tools, memory, lifecycle around the model. Examples: Claude Code, Codex CLI. |
|
||||
| L3 | Rig | Docker Compose / Terraform | Multi-agent topology — what agents exist, how they relate. **OpenRig.** |
|
||||
|
||||
You are an agent at L1 inside an L2 harness, configured by L3 OpenRig.
|
||||
OpenRig manages your harness; the harness wraps the model; the model
|
||||
generates your tokens.
|
||||
|
||||
---
|
||||
|
||||
## Three pillars of context
|
||||
|
||||
OpenRig is built on three context-engineering pillars. When you're oriented,
|
||||
you should know which pillar you're operating in:
|
||||
|
||||
| Pillar | What it is | Where it lives |
|
||||
|---|---|---|
|
||||
| **Ontology** | What exists. Curated knowledge — facts, code maps, as-built docs. | Corpus (planned future system). Today: substrate prose docs. |
|
||||
| **Epistemology** | Why an agent believes what it believes — reasoning, instincts, decisions. | Transcripts (auto-captured). Session logs. ADRs. |
|
||||
| **Topology** | How agents are connected — pods, edges, communication paths. | OpenRig itself. RigSpec YAML. |
|
||||
|
||||
OpenRig **manages the topology pillar**. The other two are filled by Corpus
|
||||
(future) and transcripts (now). Most of your work probably touches multiple
|
||||
pillars; knowing which one you're operating in helps you reach for the right
|
||||
artifacts.
|
||||
|
||||
---
|
||||
|
||||
## The core vocabulary (read these terms literally)
|
||||
|
||||
| Term | What it means |
|
||||
|---|---|
|
||||
| **Rig** | A topology of agents working together as a single system. Defined in YAML (RigSpec). The top-level object. |
|
||||
| **Pod** | A bounded context group within a rig. Members of a pod share a context domain and continuity responsibility. Think Kubernetes pod for knowledge. |
|
||||
| **Member / Node** | A single agent (or terminal-node service) within a pod. |
|
||||
| **Edge** | A relationship between members or pods. Kinds: `delegates_to`, `spawned_by`, `can_observe`, `collaborates_with`, `escalates_to`. |
|
||||
| **Topology** | The shape of the rig — how agents are grouped into pods, how edges connect them, how the whole thing fits together. |
|
||||
| **AgentSpec** | A reusable agent blueprint. Defines skills, guidance, hooks, profiles, startup. File: `agent.yaml`. |
|
||||
| **RigSpec** | The topology YAML. Defines pods, members, edges, culture. File: `rig.yaml`. |
|
||||
| **RigBundle** | A portable archive of a RigSpec + vendored AgentSpecs. Move topologies across machines. |
|
||||
| **Agent Starter** | A named, reusable starting context bundle. RigSpec member can declare `starter_ref`. |
|
||||
| **Skill** | A markdown file with frontmatter that an agent loads at boot or on activation. Cross-runtime standard at `agentskills.io`. |
|
||||
| **Profile** | A named configuration within an AgentSpec. The rig spec's member field selects which profile to use. |
|
||||
| **Culture** | Rig-wide constitution — how the team communicates, what "done" means, escalation rules. File: `CULTURE.md`. |
|
||||
| **Snapshot** | Point-in-time capture of a rig — sessions, conversations, state. Restorable. |
|
||||
| **Session name** | `{pod}-{member}@{rig}`. The canonical address for tmux sessions and agent-to-agent messaging. |
|
||||
|
||||
The session-name format `{pod}-{member}@{rig}` is your address. When you
|
||||
run `rig whoami --json`, you get back your full topology context: rig name,
|
||||
pod, member, peers, edges, transcript path.
|
||||
|
||||
---
|
||||
|
||||
## Rig classes (what kind of rig am I in?)
|
||||
|
||||
OpenRig has five rig classes. The class determines authoring discipline,
|
||||
supervision, and lifecycle policy.
|
||||
|
||||
| Class | Purpose | Lifecycle |
|
||||
|---|---|---|
|
||||
| **kernel** | Host-level supervision, intake, authoring. One per host. | Always on; never auto-hibernated. |
|
||||
| **project** | Long-lived team bound to a codebase. | Stays hot when active; hibernates on explicit request. |
|
||||
| **ephemeral** | Short-lived mission (research, build, migration, spike). | Spawn → work → retire. |
|
||||
| **infra-build** | Subclass of ephemeral whose output becomes permanent infrastructure. | Retired only after output verified in place. |
|
||||
| **managed-app** | Services-backed rig with specialist agents (e.g., a vault specialist, a skill librarian). | Long-lived; accessed by other rigs. |
|
||||
|
||||
You're probably in a project rig or managed-app rig if you're doing
|
||||
substantive work. Knowing your class helps you understand the supervisory
|
||||
expectations on your seat.
|
||||
|
||||
---
|
||||
|
||||
## How skills load (the most important thing to get right)
|
||||
|
||||
Skills are an **established cross-runtime standard** at
|
||||
`https://agentskills.io/specification`. Both Claude Code and Codex build on it.
|
||||
|
||||
### The shape
|
||||
|
||||
A skill is a directory containing `SKILL.md` (uppercase). The SKILL.md has
|
||||
YAML frontmatter (`name`, `description`) and a Markdown body. Optional
|
||||
sibling directories: `references/`, `scripts/`, `assets/`.
|
||||
|
||||
### Progressive disclosure (why skills scale)
|
||||
|
||||
The harness reads frontmatter cheaply at boot — names + descriptions of all
|
||||
available skills. Body content loads only when a skill activates. This is
|
||||
**ambient awareness** — you know all the skills exist; you only pay token
|
||||
cost when you reach for one.
|
||||
|
||||
### Where skills come from in OpenRig
|
||||
|
||||
- **Per-agent loadout:** your AgentSpec's `profile.uses.skills: [...]`
|
||||
determines what skills get projected into your runtime skill folder
|
||||
(`.claude/skills/` or `.agents/skills/`) before your harness boots. This
|
||||
is the **structural composition** layer.
|
||||
- **Cross-pod sharing:** AgentSpecs can `imports: [shared]` to access a
|
||||
shared skill pool. Built-in agents commonly do this.
|
||||
- **Belt-and-suspenders:** the spec projects skill files; startup guidance
|
||||
also tells you to load specific skills. Both paths matter — if the
|
||||
projection silently fails, the guidance still tells you what to read.
|
||||
|
||||
### Where skills live (sources of truth)
|
||||
|
||||
| Home | Purpose |
|
||||
|---|---|
|
||||
| `<rig-cwd>/.claude/skills/`, `<rig-cwd>/.agents/skills/` | Where the harness actually loads from. Populated by `rig up`. |
|
||||
| `~/.claude/skills/`, `~/.agents/skills/` | Your personal/global skills + OpenRig bootstrap (openrig-user, openrig-operator, openrig-builder, openrig-architect, openrig-upgrade, forming-an-openrig-mental-model, mental-model-ha). |
|
||||
| `openrig/packages/daemon/specs/agents/shared/skills/` | Product built-in skills that ship with OpenRig. |
|
||||
| `~/.openrig/skills/` | Future runtime install home for OpenRig-shipped skills. |
|
||||
| `substrate/shared-docs/openrig-work/skills/` | Skill factory authoring workspace (not runtime-loaded). |
|
||||
|
||||
The harness only sees the first two. Other locations are authoring,
|
||||
shipping, and source-of-truth — they reach the harness via projection or
|
||||
NPM install, not directly.
|
||||
|
||||
---
|
||||
|
||||
## The product loop (your day-to-day)
|
||||
|
||||
```
|
||||
rig up <rig-name> # boot or restore the topology
|
||||
rig ps --nodes # see what's running
|
||||
rig whoami --json # know who you are
|
||||
rig send <session> "msg" # talk to a peer
|
||||
rig capture <session> # see a peer's terminal
|
||||
rig transcript <session> # read a peer's history
|
||||
rig down <rigId> # snapshot and tear down
|
||||
rig up <rig-name> # restore from snapshot
|
||||
```
|
||||
|
||||
The first command in any new seat is `rig whoami --json`. It tells you your
|
||||
rig, pod, member, peers, edges, and transcript path. **Treat it as ground
|
||||
truth — your CLAUDE.md or AGENTS.md startup overlay can be wrong; whoami
|
||||
is authoritative.**
|
||||
|
||||
---
|
||||
|
||||
## Cultural posture (how to behave)
|
||||
|
||||
OpenRig has a few load-bearing cultural principles. Internalize these:
|
||||
|
||||
- **Honesty over convenience.** If resume fails, say so loudly. Don't
|
||||
silently launch fresh.
|
||||
- **The agent is the power user.** The CLI is designed for a 10x staff
|
||||
engineer at the terminal. You're that user.
|
||||
- **CLI is context engineering.** Every error message and help text gives
|
||||
you information to act on. Read errors carefully.
|
||||
- **Convention over invention.** Follow docker/git/kubectl patterns. Agent
|
||||
muscle memory is real.
|
||||
- **Semi-deterministic is OK.** Core contracts are solid; edge cases are
|
||||
agent-handled.
|
||||
- **Pets, not cattle (today).** OpenRig is currently optimized for long-lived
|
||||
agents that develop instincts over sessions. Cattle support is on the
|
||||
roadmap.
|
||||
|
||||
---
|
||||
|
||||
## What you should do in your first 10 minutes
|
||||
|
||||
If you're booting into a new seat in an OpenRig rig:
|
||||
|
||||
1. **`rig whoami --json`** — recover identity. Know your rig, pod, member,
|
||||
peers.
|
||||
2. **Read your role guidance** — typically delivered via startup files.
|
||||
`guidance/role.md` for your specific seat.
|
||||
3. **Read the rig's `CULTURE.md`** if it has one — the team operating
|
||||
manual.
|
||||
4. **Check what skills you have** — list `.claude/skills/` or
|
||||
`.agents/skills/` in your cwd. Each skill has a frontmatter description
|
||||
that tells you when to reach for it.
|
||||
5. **Check your peers** — `rig capture <peer-session>` to see what they're
|
||||
doing.
|
||||
6. **Check the transcripts** if you're returning to an in-flight workstream
|
||||
— `rig transcript <session> --tail 100` for recent context.
|
||||
7. **Ask `rig ask <rig> "<question>"`** if you need cross-cutting evidence
|
||||
from the rig's transcripts and chat.
|
||||
|
||||
You're now oriented enough to start doing useful work.
|
||||
|
||||
---
|
||||
|
||||
## Going deeper (canonical references)
|
||||
|
||||
For real depth, these are the load-bearing canonical docs:
|
||||
|
||||
| Reference | What it covers |
|
||||
|---|---|
|
||||
| `openrig/docs/as-built/architecture.md` | Daemon architecture; system overview; package boundaries |
|
||||
| `openrig/docs/as-built/cli-reference.md` | The full `rig` CLI surface with all subcommands and flags |
|
||||
| `openrig/docs/reference/rig-spec.md` | The RigSpec YAML format — pods, members, edges, all fields |
|
||||
| `openrig/docs/reference/agent-spec.md` | The AgentSpec YAML format — resources, profiles, imports |
|
||||
| `openrig/docs/reference/agent-startup-guide.md` | The 7-layer startup layering model; delivery hints |
|
||||
| `openrig-internal/product/taxonomy.md` | Canonical vocabulary (read literally) |
|
||||
| `substrate/shared-docs/HOST-TOPOLOGY.md` | Host-level doctrine — rig classes, context patterns, lifecycle, authoring SOP |
|
||||
| `substrate/shared-docs/SUBSTRATE-CONVENTIONS.md` | Filesystem layout doctrine for substrate |
|
||||
| `https://agentskills.io/specification` | The cross-runtime skill standard |
|
||||
|
||||
If you're going to be authoring rigs, read `HOST-TOPOLOGY.md` §7 (the
|
||||
authoring SOP) before touching YAML.
|
||||
|
||||
If you're going to be doing skill work, read
|
||||
`substrate/shared-docs/openrig-work/skills/FACTORY-DESIGN.md` and the
|
||||
`operating-the-skill-library` skill that lives in
|
||||
`substrate/shared-docs/rigs/skill-library/agents/shared/skills/`.
|
||||
|
||||
---
|
||||
|
||||
## What this skill is NOT for
|
||||
|
||||
- **HA pair compaction recovery.** That's `mental-model-ha`. Different
|
||||
skill, different scenario.
|
||||
- **Operating a specific rig.** Specific rigs have their own DESIGN.md and
|
||||
CULTURE.md. Read those.
|
||||
- **Authoring a new rig.** Use the `openrig-architect` skill for that.
|
||||
- **Day-to-day OpenRig operation.** Use `openrig-user` for that.
|
||||
- **Administering an OpenRig install.** Use `openrig-operator`.
|
||||
|
||||
This skill exists to **form your initial mental model of OpenRig as a
|
||||
system**. Once oriented, reach for the role-specific or task-specific skills
|
||||
that fit your actual work.
|
||||
|
||||
---
|
||||
|
||||
## Common misorientations to avoid
|
||||
|
||||
| Misorientation | Reality |
|
||||
|---|---|
|
||||
| "OpenRig is a chat interface or assistant" | No. OpenRig is a control plane that *manages* your harness sessions. The chat happens inside the harness; OpenRig is around it. |
|
||||
| "Pods are workflow groups" | No. Pods are **context domains** — agents that share working context. If two agents communicate every turn, they should be in one pod; if they communicate rarely, they shouldn't be. |
|
||||
| "Edges represent reporting hierarchy" | No. Edges describe *coordination shape* — who delegates to whom, who observes whom. Avoid hierarchy interpretations; they distort behavior. |
|
||||
| "I should manage Codex's compaction the way I manage Claude's" | No. Codex auto-compacts cleanly; Claude doesn't. Different runtimes, different lifecycles. |
|
||||
| "MEMORY.md auto-loads, so I don't need to read it" | Maybe. Sometimes MEMORY.md auto-loads via system reminders; sometimes not. Don't assume. If your work touches the topics it covers, read it explicitly. |
|
||||
| "Skills inherit from a parent or compose like classes" | No. Skills are flat artifacts; composition happens via AgentSpec `profile.uses.skills` (structural) or soft cross-references in skill bodies (advisory). Not via OO-style inheritance. |
|
||||
| "The substrate `shared-docs/skills/` folder is the canonical runtime path" | No. The harness doesn't read there. It's an authoring workspace. Runtime loads from `.claude/skills/`, `.agents/skills/`, and product built-in. |
|
||||
|
||||
---
|
||||
|
||||
## Disaster-recovery test for this skill
|
||||
|
||||
If you read only this skill, can you:
|
||||
|
||||
1. State what kind of system OpenRig is, in one sentence?
|
||||
2. Name the four layers and where you live?
|
||||
3. Run `rig whoami --json` and interpret the output?
|
||||
4. Find your role guidance and your peers?
|
||||
5. Identify what kind of rig you're in (kernel / project / ephemeral / etc.)?
|
||||
6. Know where to look for a skill body (which folder)?
|
||||
7. Know what to read next for depth (the canonical references)?
|
||||
|
||||
If yes — you're oriented. If no — tell your peer or the human; missing
|
||||
context is fixable, but only if surfaced.
|
||||
@@ -0,0 +1,375 @@
|
||||
---
|
||||
name: openrig-architect
|
||||
description: Use when designing multi-agent topologies that run ON OpenRig — authoring RigSpec and AgentSpec files for new rigs, creating agent startup content (guidance / skills / culture), or diagnosing why a launched rig's agents aren't behaving as intended. NOT for changing OpenRig itself (use openrig-builder); NOT for ordinary CLI operation of an existing rig (use openrig-user). Covers the full authoring lifecycle from user intent to validated, launchable rig.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Imported 2026-05-02 from openrig/packages/daemon/specs/agents/shared/skills/rig-architect/SKILL.md (349 lines).
|
||||
Product built-in is the only source — substrate/shared-docs/skills/rig-architect/ does not exist.
|
||||
Bootstrap skill — NPM install lands this in personal homes (~/.claude/skills/, ~/.agents/skills/) for users authoring their own rigs.
|
||||
sibling_skills:
|
||||
- openrig-user
|
||||
- openrig-operator
|
||||
- openrig-builder
|
||||
- openrig-upgrade
|
||||
- forming-an-openrig-mental-model
|
||||
- ai-dev-workflows
|
||||
transfer_test: pending
|
||||
notes: |
|
||||
Description was already correct (starts with "Use when..."; lists specific authoring triggers including the diagnose-bad-rig case). Final sentence ("Covers the full authoring lifecycle from user intent to validated, launchable rig.") is workflow-summary-adjacent; on next iteration, consider trimming. For factory-adoption cycle, kept as-is to match product source-of-truth.
|
||||
naming_note: |
|
||||
Founder noted (2026-05-02 conversation): "openrig-architect is actually called rig-architect but it's meant to be openrig-architect." The factory/canonical-home skill name is now `openrig-architect` (renamed during Phase 3c graduation; sibling_skills lists across N1 reflect this). Product source path remains `openrig/packages/daemon/specs/agents/shared/skills/rig-architect/` until a coordinated product-source rename — that change requires updating product source + AgentSpec uses.skills references + downstream consumers; defer to a dedicated coordination cycle.
|
||||
---
|
||||
|
||||
# OpenRig Architect
|
||||
|
||||
You are now an OpenRig architect. You design, author, validate, and diagnose multi-agent topologies for OpenRig.
|
||||
|
||||
Your job is to take a user's intent — "I need a team that does X" — and produce a complete, functioning rig: the topology spec, the agent specs, the guidance files, the culture, the startup content, and everything else needed for the rig to boot and the agents to know what to do.
|
||||
|
||||
You also diagnose problems when a rig launches but agents aren't behaving as intended.
|
||||
|
||||
## Before You Design: Required Reading
|
||||
|
||||
Load these before starting any design work. The quality of your output depends on the depth of knowledge you bring.
|
||||
|
||||
**Required (read all of these):**
|
||||
|
||||
1. **`openrig-user` skill** — full OpenRig CLI surface. You must know the operator primitives. If your runtime supports skills, load it by name. Otherwise, look for it at `~/.openrig/skills/openrig-user/SKILL.md` (the runtime install dogfood mirror — packaged skills land here) or inside the OpenRig installation under `packages/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md`. The `~/.openrig/reference/` directory holds reference docs (rig-spec.md, agent-spec.md, etc.), NOT skills.
|
||||
|
||||
2. **OpenRig reference docs** — these are installed at `~/.openrig/reference/` when the daemon starts. Read all of them:
|
||||
- `~/.openrig/reference/rig-spec.md` — canonical RigSpec YAML reference. Every field, validation rule, default.
|
||||
- `~/.openrig/reference/agent-spec.md` — canonical AgentSpec YAML reference. Same depth.
|
||||
- `~/.openrig/reference/agent-startup-guide.md` — how to think about what goes into agent startup. Context loading vs deterministic config, when to use skills vs guidance, the layering model, current support matrix.
|
||||
- `~/.openrig/reference/edge-types.md` — what edges do today vs what they're intended to do.
|
||||
|
||||
If `~/.openrig/reference/` doesn't exist yet, start the daemon first (`rig daemon start`) — it copies the reference docs on startup.
|
||||
|
||||
**Read as worked examples:**
|
||||
|
||||
3. **Shipped starter specs** — the OpenRig installation includes proven starter topologies. Find them by running `rig specs ls`. Read the ones that are relevant to your design task, especially:
|
||||
- `implementation-pair` — the smallest effective development unit (2 agents)
|
||||
- `secrets-manager` — a managed-app rig with services integration and a specialist agent
|
||||
|
||||
**Read if present on this host:**
|
||||
|
||||
4. **Host-level doctrine** at `~/code/substrate/shared-docs/HOST-TOPOLOGY.md` (or wherever substrate lives on this host). If this file exists, it supersedes the baseline process below for complex or high-stakes rigs. Specifically:
|
||||
- §3 defines the canonical rig classes (kernel, project, ephemeral, infra-build, managed-app). Classify before designing.
|
||||
- §4 defines context-sharing patterns (pods as context domains, pair pattern, HA via mental-model-ha skill + substrate session logs, terminal nodes as common-room, chatroom/substrate/transcripts/Corpus).
|
||||
- **§7 is the canonical 12-step rig-spec authoring SOP for high-stakes rigs — use it instead of the baseline "Design Process" below when the rig is ≥4 members, uses HA, is a managed-app, or will be shared/copied.** The baseline below still applies to small/focused rigs.
|
||||
- §10 contains the host's bootstrap sequence so you know where your new rig fits.
|
||||
- §12 has the naming canon and vocabulary mapping to shipped terms.
|
||||
5. **`building-agent-software` skill** if available in your skill catalog. Design principles for agent-facing tools and surfaces. Relevant when your rig ships a new CLI, service, or managed app.
|
||||
|
||||
**Load as needed:**
|
||||
- Domain-specific skills when designing specialist agents — find shipped skills inside the OpenRig installation under the `specs/agents/` tree
|
||||
- If the design session is long and you're running inside a managed rig, use `rig whoami --json` to recover your identity after compaction
|
||||
|
||||
Do not skip the required reading. A rig architect who doesn't know the spec format will produce specs that don't validate. An architect who doesn't know the startup layering model will produce agents that boot without knowing their role. An architect who doesn't check for host-level doctrine will reinvent conventions the host has already established.
|
||||
|
||||
## The Design Process
|
||||
|
||||
### Step 1: Understand the User's Intent
|
||||
|
||||
Before touching YAML, understand what the user actually needs:
|
||||
|
||||
- **What is the goal?** Not "I need 5 agents" but "I need to build and ship a web application" or "I need to research a technical question deeply" or "I need a team that can operate and monitor a running service."
|
||||
- **What are the workflows?** How does work flow from intent to completion? Who does what? Where are the handoffs?
|
||||
- **What is the project?** What codebase, what tech stack, what domain? This shapes agent specialization and startup content.
|
||||
- **What runtimes are available?** Does the user have Claude Code? Codex? Both? Runtime availability constrains topology design.
|
||||
- **How autonomous should it be?** Does the user want to direct every step, or should the rig be mostly self-driving with occasional human checkpoints?
|
||||
|
||||
Ask clarifying questions if the intent is ambiguous. A well-understood intent produces a dramatically better topology than a guess.
|
||||
|
||||
### Step 2: Identify Bounded Contexts → Pods
|
||||
|
||||
Every rig is organized into pods — bounded context groups where members share a workflow concern. The question is: what are the natural groupings?
|
||||
|
||||
**Common pod patterns:**
|
||||
|
||||
| Pod | Purpose | When to use |
|
||||
|-----|---------|-------------|
|
||||
| Orchestration | Coordination, dispatch, monitoring | Almost always — any rig with 3+ agents needs an orchestrator |
|
||||
| Development | Implementation, testing, quality | Any rig that writes code |
|
||||
| Review | Independent code review, architecture review | When quality gates matter (production code, security-sensitive work) |
|
||||
| Research | Deep investigation, analysis, synthesis | When the work requires research before implementation |
|
||||
| Design | UX, interaction design, product decisions | When the work has a user-facing interface |
|
||||
| Specialist | Domain-specific operations (Vault, DB, infra) | When a specific technology needs dedicated expertise |
|
||||
|
||||
**Sizing principles:**
|
||||
|
||||
- **Solo agent:** Only when the task is genuinely single-person (quick script, simple question). No rig needed.
|
||||
- **Pair (2 agents):** The minimum effective unit for quality work. One does, one verifies. The `implementation-pair` pattern.
|
||||
- **Small team (3-5 agents):** Orchestrator + one or two working pods. Good starting point for focused projects.
|
||||
- **Full team (6-10 agents):** Multiple bounded contexts with orchestration, development, review, and potentially research or design.
|
||||
- **Large team (10-40+ agents):** Complex projects with many concerns. Include pods for development, review, research, documentation, release management, strategy, and any other bounded context the project needs.
|
||||
|
||||
**Important:** Agents do NOT all need to be busy at the same time. A rig is a network, not an assembly line. Some pods will be highly active (dev, review) while others are available on-demand (research, documentation, release management). An idle agent has near-zero cost but is immediately available when any other agent in the rig needs it — for quick questions, lookups, delegation, or specialized work. Design for availability, not constant utilization.
|
||||
|
||||
**Start small to increase the likelihood of success,** not because large rigs are wasteful. A 3-agent rig that boots and works correctly validates your spec authoring before you scale to 20 agents. Once the core topology works, expand with additional pods as needed.
|
||||
|
||||
### Step 3: Design Agent Roles → Members
|
||||
|
||||
Each pod member needs a clear role. The role determines:
|
||||
- What agent spec to reference (builtin or custom)
|
||||
- What profile to use
|
||||
- What guidance and startup content to provide
|
||||
|
||||
**Builtin agents shipped with OpenRig:**
|
||||
|
||||
| Agent | agent_ref (in shipped starters) | Purpose |
|
||||
|-------|-------------------------------|---------|
|
||||
| orchestrator | `local:agents/orchestration/orchestrator` | Rig orchestration lead |
|
||||
| implementer | `local:agents/development/implementer` | TDD implementation agent |
|
||||
| qa | `local:agents/development/qa` | Quality assurance agent |
|
||||
| independent-reviewer | `local:agents/review/independent-reviewer` | Independent code reviewer |
|
||||
| product-designer | `local:agents/design/product-designer` | Product designer |
|
||||
| pm | `local:agents/product-management/pm` | Product manager |
|
||||
| analyst | `local:agents/research/analyst` | Research analyst |
|
||||
| synthesizer | `local:agents/research/synthesizer` | Research synthesizer |
|
||||
| vault-specialist | `local:agents/apps/vault-specialist` | Vault domain specialist |
|
||||
|
||||
To verify the current builtin set on this host, run `rig specs ls` and look for entries with type `agent` and source `builtin`.
|
||||
|
||||
**Path resolution:** The `local:` prefix means relative to the rig spec file's directory. In shipped starters, these paths resolve against the builtin specs directory inside the OpenRig installation. When authoring a custom rig spec outside the installation, you have two options:
|
||||
- **Reference your own agent specs** with `local:` paths relative to your rig spec file
|
||||
- **Use `path:` with an absolute path** to reference builtins inside the OpenRig installation (look under the `specs/agents/` directory near where `rig` is installed)
|
||||
|
||||
**When to create a custom agent spec:**
|
||||
- The builtin doesn't match the role (e.g., you need a documentation specialist, a security auditor, a data scientist)
|
||||
- The role needs domain-specific skills that no builtin carries
|
||||
- The role needs custom guidance that goes beyond what startup files can provide
|
||||
|
||||
**When to reuse a builtin:**
|
||||
- The role maps cleanly to an existing builtin (most implementation, QA, review, and orchestration roles)
|
||||
- You can customize behavior through startup files and culture without changing the agent spec
|
||||
|
||||
### Step 4: Choose Runtimes and Models
|
||||
|
||||
Each member needs a `runtime` and optionally a `model`.
|
||||
|
||||
**Runtime selection:**
|
||||
- `claude-code` — Claude Code. Best for: complex reasoning, architecture, code review, orchestration. Supports `/loop` for recurring tasks, rich hooks system, MCP servers.
|
||||
- `codex` — Codex. Best for: parallel work, implementation, testing. Different approval model. Less reliable for recurring tasks.
|
||||
- `terminal` — Infrastructure nodes. Servers, log tails, build watchers. Not an agent — a process.
|
||||
|
||||
**Runtime diversity is valuable.** Using both Claude Code and Codex in the same rig gives you different reasoning perspectives. The `product-team` starter uses Claude Code for the lead/impl/design/r1 roles and Codex for peer/qa/r2 roles. This is deliberate — model diversity catches different classes of issues.
|
||||
|
||||
**Model selection** is optional. The runtime's default model is usually fine. Override only when you have a specific reason (e.g., a complex architecture agent might benefit from a specific model).
|
||||
|
||||
### Step 5: Design Edge Topology
|
||||
|
||||
Edges define relationships between members. See `~/.openrig/reference/edge-types.md` for the full reference.
|
||||
|
||||
**Practical rules:**
|
||||
- Every working pod should have at least one `delegates_to` edge from the orchestrator
|
||||
- Review pods should have `can_observe` edges to the pods they review
|
||||
- Within a pod, the primary workflow direction should be expressed as `delegates_to` (e.g., impl → qa)
|
||||
- `delegates_to` and `spawned_by` affect launch order. Use them for dependency chains.
|
||||
- `can_observe`, `collaborates_with`, `escalates_to` are informational — they help agents understand the topology but don't constrain launch.
|
||||
|
||||
**Start simple.** You can always add edges later. A rig with only `delegates_to` edges from the orchestrator to working pods is perfectly functional.
|
||||
|
||||
### Step 6: Design Startup Content Strategy
|
||||
|
||||
This is where most rigs succeed or fail. The topology is mechanical; the startup content is what makes agents actually useful. See `~/.openrig/reference/agent-startup-guide.md` for the full guide.
|
||||
|
||||
**Minimum for every rig:**
|
||||
1. Each agent has a `guidance/role.md` — who they are, what they do
|
||||
2. The rig has a `CULTURE.md` — how the team works together
|
||||
3. Each agent gets `openrig-user` skill — so they know how to use the rig primitives
|
||||
|
||||
**For serious rigs, also include:**
|
||||
4. `startup/context.md` per agent — boot-time grounding (project info, environment details)
|
||||
5. Pod SOP skills — how each pod operates (implementation-pair SOP, review-pair SOP, etc.)
|
||||
6. Project-specific documentation in rig-level startup files
|
||||
|
||||
**The key principle:** An agent that boots without knowing its role, its team's culture, and its project context will produce generic, unhelpful work. The startup content IS the product value. Invest in it.
|
||||
|
||||
### Step 7: Services Integration (If Needed)
|
||||
|
||||
If the rig needs managed software (databases, API servers, etc.), add a `services` block. See `~/.openrig/reference/rig-spec.md` for the full services reference.
|
||||
|
||||
**When to add services:**
|
||||
- The agents operate ON software (not just write code)
|
||||
- The project needs a local dev environment (Postgres, Redis, etc.)
|
||||
- You're building a managed-app rig (software + specialist agent)
|
||||
|
||||
**Services boot before agents.** If health checks fail, no agents start. This is the hard gate — the environment must be healthy before agents can work.
|
||||
|
||||
## Authoring: The File Creation Workflow
|
||||
|
||||
### Directory Layout
|
||||
|
||||
```
|
||||
my-rig/
|
||||
rig.yaml # The RigSpec — required
|
||||
culture/
|
||||
CULTURE.md # Rig-wide culture — strongly recommended
|
||||
agents/
|
||||
my-custom-agent/
|
||||
agent.yaml # AgentSpec — if custom agent needed
|
||||
guidance/
|
||||
role.md # Role guidance
|
||||
startup/
|
||||
context.md # Boot-time context
|
||||
skills/
|
||||
my-skill/
|
||||
SKILL.md # Custom skill if needed
|
||||
docker-compose.yaml # Only if services block is used
|
||||
```
|
||||
|
||||
For rigs that reuse builtin agents, the agents directory is often unnecessary — the rig spec references the builtins directly.
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Write the rig spec** (`rig.yaml`) — define pods, members, edges, optionally services
|
||||
2. **Write or reference agent specs** — builtins for standard roles, custom for specialized roles
|
||||
3. **Write CULTURE.md** — the team operating manual
|
||||
4. **Write role guidance** for each custom agent — who they are, what they do
|
||||
5. **Write startup context** for agents that need environment grounding
|
||||
6. **Validate:** `rig spec validate rig.yaml` and `rig agent validate agents/*/agent.yaml`
|
||||
7. **Confirm the runtime cwd** — do not assume agents should work from the directory where the rig spec is stored. The spec root controls file resolution; the runtime cwd controls trust, project guidance, permissions, and repo context.
|
||||
8. **Launch:** `rig up rig.yaml --cwd /path/to/project`
|
||||
9. **Verify:** `rig ps --nodes` — all agents ready? Check `rig capture` on each agent.
|
||||
|
||||
### Validation Is Non-Negotiable
|
||||
|
||||
Always validate before launching:
|
||||
|
||||
```bash
|
||||
rig spec validate rig.yaml
|
||||
rig agent validate agents/my-agent/agent.yaml
|
||||
```
|
||||
|
||||
If validation fails, fix the errors. Do not try to launch an invalid spec — it will fail with a less helpful error.
|
||||
|
||||
## Diagnosis: When Things Go Wrong
|
||||
|
||||
### Agent doesn't know its role
|
||||
|
||||
**Symptom:** Agent produces generic output, doesn't follow team conventions.
|
||||
**Root cause:** Missing or insufficient `guidance/role.md`.
|
||||
**Fix:** Write a clear role guidance file. Include responsibilities, working rhythm, and principles. Reference it in both `resources.guidance` and `startup.files`.
|
||||
|
||||
### Agent can't coordinate with peers
|
||||
|
||||
**Symptom:** Agent tries raw tmux commands instead of `rig send`, doesn't know peer session names.
|
||||
**Root cause:** Agent didn't receive `openrig-user` skill or `openrig-start` overlay.
|
||||
**Fix:** Ensure the agent's profile `uses.skills` includes `openrig-user`. Verify via `rig ps --nodes` that the agent shows expected startup status; check installed skills via direct startup/capture/transcript evidence or the UI node detail (the `rig ps --nodes` projection does not expose installed-resource counts).
|
||||
|
||||
### Agent hits approval prompts on rig commands
|
||||
|
||||
**Symptom:** Agent stalls on `rig whoami`, `rig send`, etc.
|
||||
**Root cause:** Claude Code permissions not configured for rig commands.
|
||||
**Fix:** Describe the required permissions in startup context. The agent should configure `~/.claude/settings.json` with allowlisted rig commands. See `~/.openrig/reference/agent-startup-guide.md` for the current support matrix.
|
||||
|
||||
### Agents idle — topology doesn't engage the team
|
||||
|
||||
**Symptom:** Orchestrator works with one or two agents, others sit idle.
|
||||
**Root cause:** Missing `CULTURE.md` or pod SOP content that describes how the full team coordinates.
|
||||
**Fix:** Write a culture file that explicitly describes the coordination protocol. Include delegation patterns, review gates, and when each pod should be engaged.
|
||||
|
||||
### Services don't boot
|
||||
|
||||
**Symptom:** `rig up` fails before agents launch with a service health error.
|
||||
**Root cause:** Docker Compose issue, health check failure, or port conflict.
|
||||
**Fix:** Check `docker compose up` manually with the compose file. Verify health check URLs are correct. Check for port conflicts.
|
||||
|
||||
### Agent boots from the wrong project context
|
||||
|
||||
**Symptom:** Agent misses expected guidance, trust settings, permissions, or repo context even though the spec validates.
|
||||
**Root cause:** The rig spec directory was treated as the agent's runtime cwd by assumption.
|
||||
**Fix:** Confirm the intended cwd before launch. The spec can live in a rig/spec shelf while the agent works from the project or hub directory that carries the relevant `AGENTS.md`, `CLAUDE.md`, trust, and permissions. Use member `cwd` or `rig up --cwd` deliberately.
|
||||
|
||||
### Startup content not delivered
|
||||
|
||||
**Symptom:** Agent is missing expected guidance/skills.
|
||||
**Root cause:** File paths in the spec don't resolve, or `delivery_hint` is wrong.
|
||||
**Fix:** Verify file paths resolve relative to their owning artifact — AgentSpec resource paths are relative to the agent spec directory; RigSpec startup, culture, compose, cwd, and `local:` agent-ref paths are relative to the rig root. Check `delivery_hint` — use `guidance_merge` for pre-boot content, `send_text` for post-boot instructions.
|
||||
|
||||
### Agent startup delivered but agent doesn't use skills
|
||||
|
||||
**Symptom:** Skills are projected but agent doesn't invoke them.
|
||||
**Root cause:** Agent wasn't told to load them.
|
||||
**Fix:** In the startup context or role guidance, explicitly tell the agent which skills to load. The belt-and-suspenders pattern: project the skills via the spec AND tell the agent to read them in the guidance.
|
||||
|
||||
## Pattern Catalog
|
||||
|
||||
### The Implementation Pair
|
||||
**2 agents, 1 pod.** The smallest effective development unit. One implements (TDD), one does QA. The implementer proposes, QA approves or rejects, then the implementer commits.
|
||||
|
||||
```yaml
|
||||
pods:
|
||||
- id: dev
|
||||
label: Development
|
||||
members:
|
||||
- id: impl
|
||||
agent_ref: "local:agents/development/implementer"
|
||||
runtime: claude-code
|
||||
profile: default
|
||||
cwd: "."
|
||||
- id: qa
|
||||
agent_ref: "local:agents/development/qa"
|
||||
runtime: codex
|
||||
profile: default
|
||||
cwd: "."
|
||||
edges:
|
||||
- kind: delegates_to
|
||||
from: impl
|
||||
to: qa
|
||||
```
|
||||
|
||||
**Use when:** Focused feature work, bug fixes, small-to-medium implementation tasks.
|
||||
|
||||
### The Orchestrated Team
|
||||
**5-7 agents, 3 pods.** Orchestration + development + review. The orchestrator dispatches work, the dev pair implements, the review pair validates independently.
|
||||
|
||||
**Use when:** Production-quality work that needs coordination and independent review.
|
||||
|
||||
### The Research Team
|
||||
**3 agents, 2 pods.** Orchestrator + research pair (analyst + synthesizer). The analyst investigates deeply, the synthesizer consolidates findings.
|
||||
|
||||
**Use when:** Technical research, competitive analysis, architecture exploration.
|
||||
|
||||
### The Managed App
|
||||
**1+ agents, 1 pod, services block.** Software infrastructure (Docker Compose) plus a specialist agent who knows how to operate it.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
kind: compose
|
||||
compose_file: docker-compose.yaml
|
||||
wait_for:
|
||||
- url: http://127.0.0.1:8200/v1/sys/health
|
||||
|
||||
pods:
|
||||
- id: vault
|
||||
label: Vault
|
||||
members:
|
||||
- id: specialist
|
||||
agent_ref: "local:agents/apps/vault-specialist"
|
||||
runtime: claude-code
|
||||
profile: default
|
||||
cwd: "."
|
||||
edges: []
|
||||
```
|
||||
|
||||
**Use when:** The work involves operating software, not just writing code.
|
||||
|
||||
### The Full Product Team
|
||||
**7 agents, 3 pods.** The kitchen-sink topology: orchestration pair, development pod (impl + qa + design), review pair. See the `product-team` starter spec for the complete worked example.
|
||||
|
||||
**Use when:** Full product development with design, implementation, QA, and independent review. Requires strong culture and SOP content to keep all agents engaged.
|
||||
|
||||
## Final Notes
|
||||
|
||||
**Start simple, add complexity when needed.** A working implementation pair is better than a broken full team. Launch with the minimum viable topology, verify it works, then expand.
|
||||
|
||||
**Culture is not optional for team rigs.** Any rig with 3+ agents needs a CULTURE.md. Without it, agents will default to generic behavior and the topology will underperform.
|
||||
|
||||
**Validate early and often.** Run `rig spec validate` after every change. Run `rig agent validate` after every agent spec edit. Fix errors immediately — don't accumulate them.
|
||||
|
||||
**The startup content IS the product.** The YAML topology is scaffolding. What makes a rig actually useful is the guidance, culture, skills, and startup context that agents receive. Invest your authoring time there.
|
||||
@@ -0,0 +1,220 @@
|
||||
---
|
||||
name: openrig-operator
|
||||
description: |
|
||||
Use when debugging host-side OpenRig runtime issues: daemon reachability, Codex permission or writable-root failures, command approval friction, rate limits/account switches, helper cleanup, or topology health confusion. NOT for ordinary CLI operation (use openrig-user) or for changing OpenRig itself (use openrig-builder).
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Graduated 2026-05-04 from openrig-work/skills/workspace/from-product/openrig-operator/.
|
||||
Originally imported from openrig/packages/daemon/specs/agents/shared/skills/core/openrig-operator/
|
||||
(product built-in). Bootstrap skill — NPM install lands this in personal homes.
|
||||
Companion to openrig-user (daily CLI surface) — this skill is host-side troubleshooting.
|
||||
transfer_test: pending
|
||||
sibling_skills:
|
||||
- openrig-user
|
||||
- openrig-builder
|
||||
- openrig-architect
|
||||
- openrig-upgrade
|
||||
- forming-an-openrig-mental-model
|
||||
- ai-dev-workflows
|
||||
---
|
||||
|
||||
# OpenRig Operator
|
||||
|
||||
## Overview
|
||||
|
||||
This skill covers host/runtime/operator triage around OpenRig itself.
|
||||
Use it when the problem may be the daemon, the shell/runtime surface, or stale helper processes rather than the product workflow you are trying to run.
|
||||
|
||||
## When to Use
|
||||
|
||||
Use this skill when you see:
|
||||
- `rig whoami --json` returning partial identity
|
||||
- `rig ps --nodes --json` failing while some other `rig` commands still work
|
||||
- `Sent to ...` plus `Verified: no`
|
||||
- repeated unified-exec-process warnings
|
||||
- suspicion that stale helper processes are accumulating
|
||||
- Codex seats hit `Operation not permitted`, command approval friction, or stale writable roots
|
||||
- Codex seats report usage-limit/rate-limit or need a ChatGPT account switch
|
||||
|
||||
Do not use this skill for normal product workflow routing, queue handling, or ordinary peer communication. Use `openrig-user` for that.
|
||||
|
||||
Do not use this skill to decide how to change OpenRig behavior. Use `openrig-builder` for build
|
||||
lane selection, feature lifecycle, doctrine rollout, and topology/build-system decisions.
|
||||
|
||||
## First Checks
|
||||
|
||||
Start with the minimum truthful operator read:
|
||||
|
||||
```bash
|
||||
rig whoami --json
|
||||
rig daemon status
|
||||
rig ps --nodes --json
|
||||
```
|
||||
|
||||
Interpret them together, not in isolation:
|
||||
- partial `whoami` can mean identity is inferable while daemon-backed surfaces are degraded
|
||||
- `daemon status` tells you whether the host daemon is up, not whether every seat can reach it cleanly
|
||||
- `ps --nodes --json` is the best machine-readable topology check when it works
|
||||
|
||||
## Verification Drift Vs Send Failure
|
||||
|
||||
For `rig send`:
|
||||
- `Sent to ...` + `Verified: yes` = strong positive delivery evidence
|
||||
- `Sent to ...` + `Verified: no` = ambiguous delivery, not automatic failure
|
||||
- no `Sent to ...` line or a hard error = send failure
|
||||
|
||||
When verification is ambiguous, check:
|
||||
- direct reply
|
||||
- `rig capture <session>`
|
||||
- transcript evidence
|
||||
- queue/outbox state if the message asked for a durable handoff
|
||||
|
||||
Do not blindly retry until you have checked one of those.
|
||||
|
||||
## Unified Exec Warning
|
||||
|
||||
If you see:
|
||||
|
||||
- `Warning: The maximum number of unified exec processes you can keep open is 60 ...`
|
||||
|
||||
treat it first as a host/tooling-layer warning, not as automatic proof that the OpenRig topology is unhealthy.
|
||||
|
||||
This warning can coexist with a healthy live topology.
|
||||
|
||||
## Safe Process Triage
|
||||
|
||||
Inspect the process surface first:
|
||||
|
||||
```bash
|
||||
ps -axo pid,ppid,command | rg 'tmux send-keys|rig queue create|tmux attach|codex|claude'
|
||||
```
|
||||
|
||||
Think in layers:
|
||||
- host/tooling layer: stale one-shot wrappers, session bookkeeping, helper shells
|
||||
- topology layer: live `tmux attach` seats, live `codex` / `claude` runtimes, daemon health
|
||||
|
||||
Do not diagnose topology failure from tooling-layer warnings alone.
|
||||
|
||||
## Safe Cleanup Boundary
|
||||
|
||||
Usually safe to reap when clearly orphaned / one-shot:
|
||||
- `tmux send-keys ...`
|
||||
- short-lived shell wrappers created only to enqueue or send one message
|
||||
|
||||
Do not mass-kill:
|
||||
- `tmux attach ...`
|
||||
- `codex ...`
|
||||
- `claude ...`
|
||||
- other long-lived daemon/runtime processes
|
||||
|
||||
The point is to remove garbage, not workers.
|
||||
|
||||
## Terminal Node Escalation
|
||||
|
||||
Use a terminal node when evidence shows a seat-level sandbox/profile cannot perform required host work, but another approved operator surface can. This is an explicit operator lane, not a silent permission bypass.
|
||||
|
||||
Good fits:
|
||||
- Codex/Claude seat cannot access Tart, SSH, tmux, queue directories, or host files needed for a proof
|
||||
- a VM/test-proof or current-host-operator task needs real host capability
|
||||
- another seat or terminal/sysadmin node has approved access and can return command evidence
|
||||
|
||||
Do not use this lane to bypass repo safety, dirty-worktree boundaries, review gates, or destructive-action approval. If the command would stop live rigs, delete data, reset git state, or mutate VM state beyond the accepted plan, require explicit authorization and record it.
|
||||
|
||||
Protocol:
|
||||
1. Classify the original failure as seat/profile-specific if possible, not product failure.
|
||||
2. Write or cite a packet with objective, lane, exact commands, cwd, expected outputs, stop conditions, and artifact path.
|
||||
3. Route to an existing sysadmin/infrastructure/terminal node when available; otherwise provision a named terminal node through the topology/config layer instead of using a hidden ad hoc shell.
|
||||
4. Terminal node runs only the packeted commands and returns command log, exit codes, cwd/env notes, and artifacts.
|
||||
5. Original agent keeps task ownership and proof classification; terminal node supplies host capability.
|
||||
|
||||
Field lesson: if tester sees `Operation not permitted` for Tart/SSH while driver or another approved seat can reach the VM, treat it as permission/profile variance and route a terminal-node/operator remediation before changing product code.
|
||||
|
||||
## Codex Permission Policy
|
||||
|
||||
Use the `security-and-consequence-boundary-policy` skill for the security model.
|
||||
OpenRig gates consequence boundaries, not ordinary work inside the intended boundary.
|
||||
|
||||
Treat Codex permissions as two independent layers:
|
||||
- command approval rules decide which shell commands can run outside the sandbox
|
||||
- filesystem writable roots decide which paths a `workspace-write` seat can mutate
|
||||
|
||||
Codex `auto_review`, `--full-auto`, and approval-bypass modes are not normal fleet defaults. They
|
||||
can burn quota catastrophically and do not widen filesystem roots for an already-running seat. Full
|
||||
access scope with `approvals_reviewer = "user"` is the intended Codex default here.
|
||||
|
||||
Claude Code auto permissions are different and are the preferred Claude default on this host when
|
||||
the consequence-boundary rules still apply.
|
||||
|
||||
Current host policy:
|
||||
- default profile is `fleet`
|
||||
- `fleet` uses `sandbox_mode = "danger-full-access"`, `approval_policy = "on-request"`, `approvals_reviewer = "user"`
|
||||
- top-level `[sandbox_workspace_write].writable_roots` intentionally includes `~/code` and tool dotdirs: `.codex`, `.claude`, `.openrig`, `.agents`, `.config`, `.cache`, `.local`, `.docker`, `.npm`, `.nvm`, `.pnpm-store`
|
||||
- `permissions.fleet.filesystem` mirrors those writes and denies `.ssh`, `.gnupg`, and project env files
|
||||
- `default.rules` broadly allows `rig` and `rigx`; `rigx` is fully permitted for now because it is the fast-moving config-layer overlay
|
||||
- `default.rules` still prompts for destructive/publishing surfaces such as `rm`, `mv`, `chmod`, `git push`, PR mutation, `sudo`, process kills, daemon lifecycle, and destructive Docker/Brew/rig commands
|
||||
|
||||
## Codex Account / Usage-Limit Refresh
|
||||
|
||||
When Codex seats hit usage limits or need a ChatGPT/OpenAI account rotation, use the focused
|
||||
`codex-seat-auth-refresh` skill instead of improvising.
|
||||
|
||||
Key reminder: host auth can switch while already-running Codex TUIs keep the old account. Refresh
|
||||
only the scoped seats, preserve stable seat names, and record auth-seat registry rows sequentially.
|
||||
|
||||
Known failure modes this policy prevents:
|
||||
- Codex seats assuming a task is impossible when the real issue is stale launch roots
|
||||
- agents creating workaround slices/features for what is actually a sandbox/config problem
|
||||
- writing load-bearing canon into `state/`, `/tmp`, or a nearby writable folder because the intended durable path was blocked
|
||||
- trusting labels like `full access`; verify actual roots and command policy instead
|
||||
- changing `config.toml` and expecting already-running seats to pick it up without restart
|
||||
|
||||
When `Operation not permitted` appears:
|
||||
1. Identify whether it is command approval, filesystem root, macOS privacy, or stale session.
|
||||
2. Check effective roots with `codex -p fleet debug prompt-input <probe-name>`.
|
||||
3. Verify with a tiny direct write probe in an allowed target and a negative probe in a protected target such as `.ssh`.
|
||||
4. If `config.toml` changed, restart one seat and re-run the probes before fleet rollout.
|
||||
5. If the target should be durable and is not writable, stop and escalate; do not invent fallback storage.
|
||||
|
||||
Field note: as of Codex CLI `0.125.0`, top-level `[sandbox_workspace_write]` was the shape reflected by `debug prompt-input`; profile-scoped writable roots did not show in the effective prompt. Re-test this if Codex changes.
|
||||
|
||||
|
||||
## Durable Write Escalation
|
||||
|
||||
If the task requires writing load-bearing knowledge or behavior and the intended target is not writable, stop and escalate. Do not silently write to `state/`, `/tmp`, or a nearby writable folder.
|
||||
|
||||
Use the intended durable home:
|
||||
- `skills/` for operating rules and refocus behavior
|
||||
- `openrig-work/field-notes/` for durable observations and investigations
|
||||
- `openrig-work/lab/` for experiments and external project research
|
||||
- `openrig-work/missions/` for PM specs, roadmap packets, and workstream canon
|
||||
- product repo for shipped OpenRig daemon/CLI/config/spec/test behavior
|
||||
|
||||
A runtime mirror under a rig `state/` path may be used only as a temporary live patch, must be labeled as non-canonical, and must have a canonical sync follow-up.
|
||||
|
||||
## Common Mistakes
|
||||
|
||||
- treating `Verified: no` as if it proves the message did not land
|
||||
- treating the unified-exec warning as if it proves the rig is overloaded
|
||||
- killing live seats when only stale helper wrappers needed cleanup
|
||||
- concluding "daemon down" from one seat's failure without checking host-level daemon status
|
||||
- assuming Codex config changes apply to already-running seats without a restart/probe
|
||||
- confusing command approval with filesystem write permission
|
||||
- calling a VM or product path broken before comparing another approved seat or terminal-node probe
|
||||
- assuming a label like `full access` proves effective write/network capability; verify command approval and filesystem writable-root coverage separately
|
||||
|
||||
## Practical Rule
|
||||
|
||||
Clean the smallest safe surface that matches the evidence.
|
||||
|
||||
If the warning or failure remains after stale-wrapper cleanup, re-check:
|
||||
|
||||
```bash
|
||||
rig daemon status
|
||||
rig ps --nodes --json
|
||||
```
|
||||
|
||||
If those remain healthy, the residual issue may still be in the host/tool/session layer rather than in OpenRig topology state.
|
||||
@@ -0,0 +1,596 @@
|
||||
---
|
||||
name: openrig-user
|
||||
description: Use when operating OpenRig with the `rig` CLI and you need the shipped command surface for identity, inventory, communication, lifecycle, specs, recovery, or agent-facing JSON output. NOT for debugging host-side runtime issues (use openrig-operator) or for changing OpenRig itself (use openrig-builder).
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Synced 2026-05-04 from substrate/shared-docs/skills/openrig-user/SKILL.md (substrate-root canonical, v0.2.0 released by lifecycle team). Originally imported 2026-05-02 from openrig/packages/daemon/specs/agents/shared/skills/core/openrig-user/SKILL.md.
|
||||
Lifecycle team rsync'd the new version into substrate root for their own work; factory's job is to keep the openrig-work/skills/ workspace version in sync as new releases land.
|
||||
Bootstrap skill — NPM install lands this in personal homes (~/.claude/skills/, ~/.agents/skills/) so agents have it at every boot.
|
||||
sibling_skills:
|
||||
- openrig-operator
|
||||
- openrig-builder
|
||||
- openrig-architect
|
||||
- openrig-upgrade
|
||||
- forming-an-openrig-mental-model
|
||||
- ai-dev-workflows
|
||||
transfer_test: pending
|
||||
notes: |
|
||||
Description was already correct (starts with "Use when..."; lists triggering domains without summarizing workflow). No frontmatter rewrite needed.
|
||||
2026-05-04 sync: body content updated for OpenRig v0.2.0 release — adds Runtime-Gated Coordination Primitives section (PL-004 Phase A-D commands: rig stream / rig queue / rig project / rig view / rig watchdog / rig workflow), default posture for daemon vs substrate coordination commands, capture discipline section. Frontmatter metadata.openrig.* preserved across the sync.
|
||||
---
|
||||
|
||||
|
||||
# OpenRig User
|
||||
|
||||
This is an as-built guide to the shipped `rig` CLI.
|
||||
Use current code and `rig ... --help` as ground truth if anything here ever conflicts with older planning docs.
|
||||
|
||||
This is not the config-layer or builder guide. Use `control-plane-capabilities` for `rigx` and
|
||||
experimental overlays. Use `openrig-builder` when changing OpenRig behavior or doctrine.
|
||||
|
||||
## Runtime-Gated Coordination Primitives
|
||||
|
||||
OpenRig v0.2.0 is published publicly as `@openrig/[email protected]` and GitHub Release
|
||||
`v0.2.0`. It includes the bundled PL-004 Coordination Primitive System: Phase A
|
||||
`rig stream` / `rig queue`, Phase B `rig project` / `rig view`, Phase C
|
||||
`rig watchdog`, and Phase D `rig workflow` / `workflow-keepalive`.
|
||||
|
||||
Do not confuse public package truth with this host's active daemon truth. As of 2026-05-04, the
|
||||
human reports the production daemon has been upgraded to v0.2.0; a local check showed `rig
|
||||
--version` and the product package version at `0.2.0`, with the daemon running on port 7433.
|
||||
|
||||
Default posture on this host now:
|
||||
|
||||
- Treat daemon `rig queue`, `rig stream`, `rig project`, `rig view`, `rig watchdog`, and
|
||||
`rig workflow` as active-host coordination surfaces for PL-004-backed work.
|
||||
- Use substrate `rigx queue`, `rigx stream`, `rigx project`, and `rigx view-proto` only where the
|
||||
current workflow is still explicitly operating on the temporary substrate coordination layer or
|
||||
a legacy artifact has not migrated.
|
||||
- If a daemon-backed coordination command fails, debug the command/runtime/schema edge directly;
|
||||
do not fall back to stale pre-upgrade assumptions about this host.
|
||||
- Do not run daemon stop/start, wrapper mutation, production DB copy/mutation, git push, tags, npm
|
||||
publish, or GitHub release unless a human grants the specific bounded gate. The public release
|
||||
already exists; further release operations need explicit scope.
|
||||
|
||||
## Core Loop
|
||||
|
||||
Most work in OpenRig reduces to this loop:
|
||||
- recover identity: `rig whoami --json`
|
||||
- inspect inventory: `rig ps --nodes --json`
|
||||
- read context: `rig transcript ...`, `rig ask ...`, `rig chatroom history ...`
|
||||
- act: `rig send`, `rig capture`, `rig broadcast`, lifecycle commands
|
||||
|
||||
## Identity and Recovery
|
||||
|
||||
Start here after launch, compaction, or confusion:
|
||||
|
||||
```bash
|
||||
rig whoami --json
|
||||
```
|
||||
|
||||
What it gives you today:
|
||||
- identity: rig, logical ID, pod/member, session name, runtime
|
||||
- peers and directional edges
|
||||
- transcript info
|
||||
- `contextUsage` when available
|
||||
|
||||
Flags:
|
||||
```bash
|
||||
rig whoami --session <name>
|
||||
rig whoami --node-id <id>
|
||||
```
|
||||
|
||||
If the daemon is unreachable but identity can still be inferred, `--json` may return a partial result instead of crashing.
|
||||
|
||||
## Inventory and Monitoring
|
||||
|
||||
```bash
|
||||
rig ps
|
||||
rig ps --json
|
||||
rig ps --nodes
|
||||
rig ps --nodes --json
|
||||
```
|
||||
|
||||
Use `rig ps --nodes --json` for the current node inventory across rigs. It is the best machine-readable operator surface for:
|
||||
- session name
|
||||
- runtime
|
||||
- session/startup status
|
||||
- restore outcome
|
||||
- attach/resume commands
|
||||
- latest error
|
||||
|
||||
Other health surfaces:
|
||||
|
||||
```bash
|
||||
rig status
|
||||
rig daemon status
|
||||
rig config
|
||||
rig preflight
|
||||
rig doctor
|
||||
```
|
||||
|
||||
## Transcript and Communication
|
||||
|
||||
### Transcript access
|
||||
|
||||
```bash
|
||||
rig transcript <session> --tail 100
|
||||
rig transcript <session> --grep "pattern"
|
||||
rig transcript <session> --json
|
||||
```
|
||||
|
||||
### Send to one session
|
||||
|
||||
```bash
|
||||
rig send <session> "message"
|
||||
rig send <session> "message" --verify
|
||||
rig send <session> "message" --force
|
||||
rig send <session> "message" --json
|
||||
```
|
||||
|
||||
Use `--verify` when you want delivery evidence. Use `--force` only when you intentionally want to bypass activity-risk checks.
|
||||
|
||||
Observed operator nuance for `--verify`:
|
||||
- `Sent to ...` + `Verified: yes` = strong positive delivery evidence.
|
||||
- `Sent to ...` + `Verified: no` = ambiguous delivery, not automatic failure. We have repeatedly seen the message still land; treat this as verification drift until disproven.
|
||||
- no `Sent to ...` line or a hard error = send failure.
|
||||
|
||||
When you get `Verified: no`, do not immediately retry blindly. First check one of:
|
||||
- a direct reply from the target
|
||||
- `rig capture <session>`
|
||||
- transcript evidence
|
||||
- queue/outbox state if the message asked for a durable handoff
|
||||
|
||||
### Capture terminal output
|
||||
|
||||
```bash
|
||||
rig capture <session>
|
||||
rig capture <session> --lines 50
|
||||
rig capture --rig <name>
|
||||
rig capture --pod <name> --rig <name>
|
||||
rig capture --rig <name> --json
|
||||
```
|
||||
|
||||
Capture discipline:
|
||||
- Default to bounded captures: `rig capture <session> --lines 80`.
|
||||
- Use `--lines 120` when the latest activity is ambiguous.
|
||||
- Use `--lines 200` or more only for explicit recovery/debug reconstruction, such as post-compaction restore, context-wall diagnosis, or a failed handoff.
|
||||
- Prefer targeted `rig transcript <session> --tail ...`, `rig transcript <session> --grep ...`, queue files, or status commands over large pane captures.
|
||||
- Do not use large captures as routine monitoring; they waste operator context and can hide the actual latest state in old scrollback.
|
||||
|
||||
### Broadcast
|
||||
|
||||
```bash
|
||||
rig broadcast --rig <name> "message"
|
||||
rig broadcast --pod <name> "message"
|
||||
rig broadcast "message"
|
||||
rig broadcast --rig <name> "message" --json
|
||||
```
|
||||
|
||||
Without `--rig` or `--pod`, broadcast targets all running sessions.
|
||||
|
||||
### Chatroom
|
||||
|
||||
```bash
|
||||
rig chatroom send <rig> <message> [--sender <name>]
|
||||
rig chatroom history <rig> [--topic <name>] [--after <id>] [--since <ts>] [--sender <name>] [--limit <n>] [--json]
|
||||
rig chatroom wait <rig> [--after <id>] [--topic <name>] [--sender <name>] [--timeout <seconds>] [--json]
|
||||
rig chatroom clear <rig>
|
||||
rig chatroom topic <rig> <topic-name> [--body <text>] [--sender <name>]
|
||||
rig chatroom watch <rig> [--tmux]
|
||||
```
|
||||
|
||||
**Key commands:**
|
||||
- `send` — post a message
|
||||
- `history` — retrieve with composable filters (sender, since, after, topic)
|
||||
- `wait` — block until new matching messages arrive (polls history, times out honestly)
|
||||
- `clear` — delete all messages for the rig (destructive, rig-scoped)
|
||||
- `topic` — set a topic marker
|
||||
- `watch` — SSE or tmux-based live stream
|
||||
|
||||
**Roundtable protocol:**
|
||||
1. Inspect old room: `rig chatroom history my-rig --limit 5`
|
||||
2. Save if needed: `rig chatroom history my-rig --json > /tmp/old-room.json`
|
||||
3. Clear if needed: `rig chatroom clear my-rig`
|
||||
4. Set topic: `rig chatroom topic my-rig "ROUND START"`
|
||||
5. Post: `rig chatroom send my-rig "position..." --sender <session>`
|
||||
6. Monitor: `rig chatroom wait my-rig --timeout 120`
|
||||
7. Close: `rig chatroom topic my-rig "ROUND CLOSED"`
|
||||
|
||||
See `docs/planning/roadmaps/chatroom-roundtable-protocol.md` for the full protocol.
|
||||
|
||||
### `rig ask`
|
||||
|
||||
```bash
|
||||
rig ask <rig> "question"
|
||||
rig ask <rig> "question" --json
|
||||
```
|
||||
|
||||
Current shipped behavior:
|
||||
- queries the daemon for evidence
|
||||
- returns rig summary
|
||||
- returns transcript excerpts
|
||||
- may return chat excerpts
|
||||
- returns insufficiency state and optional guidance
|
||||
|
||||
This is an evidence/context command. It is not a hidden second-LLM call.
|
||||
|
||||
## Lifecycle
|
||||
|
||||
### Bring a rig up
|
||||
|
||||
```bash
|
||||
rig up <source>
|
||||
rig up <source> --plan
|
||||
rig up <source> --yes
|
||||
rig up <source> --json
|
||||
```
|
||||
|
||||
`<source>` can be:
|
||||
- a rig spec path
|
||||
- a `.rigbundle` path
|
||||
- a bare name
|
||||
|
||||
Bare names are special:
|
||||
- if they match a library spec, `rig up` launches from the spec library
|
||||
- if they do not match a library spec, `rig up` treats the name as an existing-rig restore/power-on target
|
||||
- if both exist, `rig up` fails loudly on ambiguity
|
||||
|
||||
Current behavior notes:
|
||||
- `--target <root>` is only for `.rigbundle` / package installation. It does not change agent cwd.
|
||||
- `local:` `agent_ref` values resolve relative to the rig spec directory, not your shell cwd.
|
||||
- if you copy a built-in spec elsewhere, keep its `agents/` tree beside the YAML or rewrite those refs to `path:/absolute/path`
|
||||
- `rig up --cwd <path>` exists as a launch working-directory override applied to all members for that run (verify with `rig up --help`)
|
||||
- Source at OpenRig `6af2754` adds member-level `starter_ref` for named Agent
|
||||
Starter registry entries. Active host availability still depends on the
|
||||
running daemon; `rig-real-17812d5` does not resolve `starter_ref`.
|
||||
|
||||
Legacy/spec-specific surfaces still ship too:
|
||||
|
||||
```bash
|
||||
rig bootstrap <spec> [--plan] [--yes] [--json]
|
||||
rig requirements <spec> [--json]
|
||||
```
|
||||
|
||||
### Agent Starter `starter_ref`
|
||||
|
||||
Source `>= 6af2754` lets a RigSpec member set:
|
||||
|
||||
```yaml
|
||||
starter_ref:
|
||||
name: openrig-builder-base--claude-code
|
||||
```
|
||||
|
||||
The daemon resolves the named entry from the Agent Starter registry, applies the
|
||||
credential scan, and prepends the starter content as `guidance_merge` on fresh
|
||||
launch. It supports Claude and Codex members, rejects terminal members, rejects
|
||||
`starter_ref + session_source.mode: fork`, and allows
|
||||
`starter_ref + session_source.mode: rebuild`.
|
||||
|
||||
Runtime-truth rule: verify active daemon/runtime provenance before depending on
|
||||
this. If the live daemon is still `rig-real-17812d5`, `starter_ref` is source
|
||||
truth only and should not be used against the active control plane. After a
|
||||
runtime at or after `6af2754` is active, `rig up --plan --json` should expose a
|
||||
`resolve_starter` stage with `detail.starterContent`.
|
||||
|
||||
### Tear a rig down
|
||||
|
||||
```bash
|
||||
rig down <rigId>
|
||||
rig down <rigId> --snapshot
|
||||
rig down <rigId> --delete
|
||||
rig down <rigId> --force
|
||||
rig down <rigId> --json
|
||||
```
|
||||
|
||||
Known v0.2.0 public-release caveat: `rig down <name> --delete` can return HTTP 404 even when
|
||||
`rig ps --json` exposes the rig name. Use `rig ps` to find the rig ID, then run
|
||||
`rig down <rigId> --delete` until `rig-down-name-lookup-404` is fixed.
|
||||
|
||||
If `--snapshot` succeeds, human output includes the restore hint.
|
||||
|
||||
### Release management without killing live claimed sessions
|
||||
|
||||
```bash
|
||||
rig release <rigId>
|
||||
rig release <rigId> --delete
|
||||
rig release <rigId> --json
|
||||
```
|
||||
|
||||
Use `rig release` for adopted/claimed-session rigs when you want OpenRig to stop managing the rig but leave the tmux sessions alive.
|
||||
This is the safe recovery/reset surface for the "sessions still exist, management is broken or stale" case.
|
||||
If the rig contains OpenRig-launched nodes, `rig release` refuses loudly instead of pretending the mixed rig is safe to detach.
|
||||
|
||||
### Snapshots and restore
|
||||
|
||||
```bash
|
||||
rig snapshot <rigId>
|
||||
rig snapshot list <rigId>
|
||||
rig restore <snapshotId> --rig <rigId>
|
||||
```
|
||||
|
||||
`rig restore` requires `--rig <rigId>`.
|
||||
|
||||
Claude Code autonomy note:
|
||||
- unattended `rig whoami` on boot may require the local permission allow list to include `Bash(rig:*)`
|
||||
|
||||
### Import/export and bundles
|
||||
|
||||
```bash
|
||||
rig export <rigId> -o rig.yaml
|
||||
rig import <path> [--instantiate] [--materialize-only] [--preflight] [--target-rig <rigId>] [--rig-root <root>]
|
||||
rig bundle create <spec> -o out.rigbundle
|
||||
rig bundle inspect <bundle>
|
||||
rig bundle install <bundle> [--plan] [--yes] [--target <root>] [--json]
|
||||
```
|
||||
|
||||
### Legacy package surface
|
||||
|
||||
This still ships, but is explicitly marked legacy:
|
||||
|
||||
```bash
|
||||
rig package validate <path>
|
||||
rig package plan <path> [--target <dir>] [--runtime <runtime>] [--role <name>]
|
||||
rig package install <path> [--target <dir>] [--runtime <runtime>] [--role <name>] [--allow-merge]
|
||||
rig package list
|
||||
rig package rollback <installId>
|
||||
```
|
||||
|
||||
## Discovery and Topology Mutation
|
||||
|
||||
### Discover unmanaged tmux sessions
|
||||
|
||||
```bash
|
||||
rig discover
|
||||
rig discover --json
|
||||
rig discover --draft
|
||||
```
|
||||
|
||||
### Bind a discovered session
|
||||
|
||||
```bash
|
||||
rig bind <discoveredId> --rig <rigId> --node <logicalId>
|
||||
rig bind <discoveredId> --rig <rigId> --pod <namespace> --member <name>
|
||||
```
|
||||
|
||||
There is no shipped top-level `rig claim` command.
|
||||
The current adoption surface is `discover`, `bind`, `adopt`, and `unclaim`.
|
||||
|
||||
### Self-attach the current shell or agent
|
||||
|
||||
```bash
|
||||
rig attach --self --rig <rigId> --node <logicalId>
|
||||
rig attach --self --rig <rigId> --node <logicalId> --print-env
|
||||
rig attach --self --rig <rigId> --pod <namespace> --member <name> --runtime <runtime>
|
||||
```
|
||||
|
||||
Use `rig attach --self` when the current agent should attach itself directly instead of going through `discover` + `bind`.
|
||||
|
||||
Current proven behavior:
|
||||
- inside `tmux`: attaches as a normal tmux-backed node, preserving inbound `rig send` / `rig capture`
|
||||
- outside `tmux`: attaches as `external_cli`
|
||||
- `--print-env` prints the `OPENRIG_NODE_ID` and `OPENRIG_SESSION_NAME` exports for the current shell
|
||||
|
||||
Recommended flow:
|
||||
|
||||
```bash
|
||||
rig attach --self --rig <rigId> --node <logicalId> --print-env > /tmp/openrig-self-attach.env
|
||||
. /tmp/openrig-self-attach.env
|
||||
rig whoami --json
|
||||
```
|
||||
|
||||
Notes:
|
||||
- for tmux-backed self-attach, `rig whoami --json` is the right verification
|
||||
- for raw/external self-attach, `rig ps --nodes --json` is currently the more reliable verification surface
|
||||
- if the current shell is outside tmux, pass `--display-name <name>` when you want a stable human session label recorded
|
||||
|
||||
### Adopt a topology and bind live sessions
|
||||
|
||||
```bash
|
||||
rig adopt <path> --bind <logicalId=tmuxSessionOrDiscoveryId>
|
||||
rig adopt <path> --bind <logicalId=...> --bind <logicalId=...> --json
|
||||
rig adopt <path> --bindings-file <bindings.yaml>
|
||||
rig adopt <path> --bind <logicalId=...> --target-rig <rigId> --rig-root <root>
|
||||
```
|
||||
|
||||
Use `rig adopt` when the sessions already exist and you want OpenRig to start managing them.
|
||||
|
||||
A bindings file is the durable map from authored logical IDs to live sessions. Shape:
|
||||
|
||||
```yaml
|
||||
bindings:
|
||||
dev1.impl2: dev1-impl2@rigged-buildout
|
||||
dev1.qa: dev1-qa@rigged-buildout
|
||||
```
|
||||
|
||||
Spec + bindings is the proven recovery pair for adopted rigs.
|
||||
Spec gives OpenRig the intended topology. Bindings tells OpenRig which discovered live session belongs in each logical node.
|
||||
|
||||
### Proven adopted-rig recovery workflow
|
||||
|
||||
This workflow is proven for the case where the external tmux sessions are still alive:
|
||||
|
||||
```bash
|
||||
rig release <rigId> --delete
|
||||
rig discover --json
|
||||
rig adopt <spec.yaml> --bindings-file <bindings.yaml>
|
||||
```
|
||||
|
||||
What this does:
|
||||
- removes OpenRig management without killing the sessions
|
||||
- re-discovers those same sessions as unmanaged
|
||||
- re-attaches them to the topology defined by the spec + bindings
|
||||
|
||||
Important limits:
|
||||
- this is for `sessions still alive`
|
||||
- spec alone is not enough for adopted rigs; you also need bindings
|
||||
- this does not yet mean OpenRig can recreate dead external sessions from nothing
|
||||
|
||||
### Add unmanaged pods into an existing rig
|
||||
|
||||
This is the proven workflow when a rig is already managed, but a new pod was created outside OpenRig and you want to add it later:
|
||||
|
||||
```bash
|
||||
rig adopt <pod-fragment.yaml> --bindings-file <pod.bindings.yaml> --target-rig <rigId>
|
||||
```
|
||||
|
||||
Use this when:
|
||||
- the target rig already exists
|
||||
- the new sessions are live and visible in `rig discover --json`
|
||||
- you want additive topology growth, not a full rebuild
|
||||
|
||||
What to prepare:
|
||||
- a pod fragment spec with only the new pod
|
||||
- a bindings file mapping the new logical IDs to the live session names
|
||||
|
||||
Verification loop:
|
||||
|
||||
```bash
|
||||
rig discover --json
|
||||
rig adopt <fragment.yaml> --bindings-file <bindings.yaml> --target-rig <rigId>
|
||||
rig ps --nodes --json
|
||||
rig export <rigId> -o rig.yaml
|
||||
```
|
||||
|
||||
Success looks like:
|
||||
- the new sessions stop appearing in `rig discover`
|
||||
- the new logical IDs appear in `rig ps --nodes --json`
|
||||
- `rig export` includes the new pod
|
||||
|
||||
### Mixed-origin rigs are allowed
|
||||
|
||||
One rig can contain both:
|
||||
- adopted nodes bound from already-running sessions
|
||||
- OpenRig-launched nodes created later with `rig expand` / `rig launch`
|
||||
|
||||
Current safety rule:
|
||||
- `rig release` is for claimed/adopted-only rigs
|
||||
- if a rig contains launched nodes, `rig release` fails with `contains_launched_nodes`
|
||||
|
||||
### Manager-assisted recovery
|
||||
|
||||
The proven operator pattern is:
|
||||
- keep one OpenRig manager session outside the rig it manages
|
||||
- address the target by rig name, not cached rig ID
|
||||
- resolve the current owner from fresh `rig ps --nodes --json`
|
||||
- send the manager the spec path, bindings path, and verification steps with `rig send`
|
||||
|
||||
This lets ordinary agents ask the manager for OpenRig help instead of every agent needing to be an OpenRig expert.
|
||||
|
||||
### Add/remove running topology parts
|
||||
|
||||
```bash
|
||||
rig expand <rig-id> <pod-fragment-path> [--rig-root <path>] [--json]
|
||||
rig launch <rigId> <nodeRef> [--json]
|
||||
rig remove <rigId> <nodeRef> [--json]
|
||||
rig shrink <rigId> <podRef> [--json]
|
||||
rig unclaim <sessionRef> [--json]
|
||||
```
|
||||
|
||||
## Specs and Validation
|
||||
|
||||
### Validate specs
|
||||
|
||||
```bash
|
||||
rig spec validate <path> [--json]
|
||||
rig spec preflight <path> [--rig-root <root>] [--json]
|
||||
rig agent validate <path> [--json]
|
||||
```
|
||||
|
||||
### Spec library
|
||||
|
||||
```bash
|
||||
rig specs ls [--kind <kind>] [--json]
|
||||
rig specs show <name-or-id> [--json]
|
||||
rig specs preview <name-or-id> [--json]
|
||||
rig specs add <yaml-or-directory> [--json]
|
||||
rig specs sync [--json]
|
||||
rig specs remove <name-or-id> [--json]
|
||||
rig specs rename <name-or-id> <new-name> [--json]
|
||||
```
|
||||
|
||||
`rig specs add <directory>` installs a full spec tree when the directory contains `rig.yaml` or `agent.yaml`.
|
||||
|
||||
## MCP
|
||||
|
||||
```bash
|
||||
rig mcp serve [--port <port>]
|
||||
```
|
||||
|
||||
Current shipped MCP tools:
|
||||
- `rig_up`
|
||||
- `rig_down`
|
||||
- `rig_ps`
|
||||
- `rig_status`
|
||||
- `rig_snapshot_create`
|
||||
- `rig_snapshot_list`
|
||||
- `rig_restore`
|
||||
- `rig_discover`
|
||||
- `rig_bind`
|
||||
- `rig_bundle_inspect`
|
||||
- `rig_agent_validate`
|
||||
- `rig_rig_validate`
|
||||
- `rig_rig_nodes`
|
||||
- `rig_send`
|
||||
- `rig_capture`
|
||||
- `rig_chatroom_send`
|
||||
- `rig_chatroom_watch`
|
||||
|
||||
## Troubleshooting and Weird States
|
||||
|
||||
When the CLI behaves strangely, use the smallest truthful check first:
|
||||
|
||||
```bash
|
||||
rig whoami --json
|
||||
rig daemon status
|
||||
rig ps --nodes --json
|
||||
```
|
||||
|
||||
Specific operator rules:
|
||||
- `Sent to ...` + `Verified: no` is ambiguous delivery, not automatic failure. Check reply, `rig capture`, transcript evidence, or queue/outbox state before retrying.
|
||||
- partial `rig whoami --json` can happen when identity is still inferable but the daemon-backed path is degraded.
|
||||
- the unified-exec-process warning is a host/tooling-layer signal, not automatic proof that the OpenRig topology is unhealthy.
|
||||
|
||||
If you hit the unified-exec warning, inspect for stale one-shot helpers before touching live seats:
|
||||
|
||||
```bash
|
||||
ps -axo pid,ppid,command | rg 'tmux send-keys|rig queue create|tmux attach|codex|claude'
|
||||
```
|
||||
|
||||
Safe cleanup target:
|
||||
- orphaned one-shot wrappers like `tmux send-keys ...`
|
||||
|
||||
Do not mass-kill:
|
||||
- `tmux attach ...`
|
||||
- `codex ...`
|
||||
- `claude ...`
|
||||
|
||||
For deeper host/runtime triage, use the companion `openrig-operator` skill.
|
||||
|
||||
## JSON and Error Posture
|
||||
|
||||
Design assumptions that hold in the shipped CLI:
|
||||
- many operator commands support `--json`
|
||||
- error messages are intended to say what happened, why it matters, and what to do next
|
||||
- daemon-backed commands fail loudly when the daemon is stopped or unhealthy
|
||||
- restore failure is not something you should silently reinterpret as success
|
||||
|
||||
## After-Compaction Recovery Checklist
|
||||
|
||||
1. `rig whoami --json`
|
||||
2. `rig transcript <your-session> --tail 100`
|
||||
3. `rig ps --nodes --json`
|
||||
4. `rig chatroom history <rig> --limit 50`
|
||||
|
||||
## Commands That Do Not Exist
|
||||
|
||||
Do not assume these exist unless the shipped help starts listing them:
|
||||
- `rig claim`
|
||||
- `rig blame`
|
||||
- `rig replay`
|
||||
|
||||
`rig env` IS shipped (was previously listed as not-present). It exposes `status`, `logs`, and `down` for service-backed rigs and managed apps; verify with `rig env --help`.
|
||||
@@ -0,0 +1,259 @@
|
||||
---
|
||||
name: permission-posture
|
||||
description: |
|
||||
Use when classifying or dispatching OpenRig work that needs a specific permission scope (fleet / full QA-testing window / operator auto window / Mode 3 isolated VM), when blocked by sandbox / approval / filesystem / auth / provider permission denial, or when proof posture must be declared before a slice. Covers the four practical postures, the before-dispatch checklist, the stop-and-flag rule, the VM state mutation rule, mode mapping (D/0/1/2/3), and sizing-class posture mapping (Class A/B/C).
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Authored 2026-05-03 as the canonical skill home for content
|
||||
previously living at openrig-work/conventions/permission-posture/README.md.
|
||||
Per founder reframe (2026-05-03 conversation): conventions are
|
||||
taxonomy + frontmatter pointers; content lives as skills (single
|
||||
source of truth). This is the pilot for the convention-pointer
|
||||
pattern. The original convention README was last updated 2026-05-02
|
||||
with status: active. Content moved verbatim, restructured for skill
|
||||
shape (when-to-use bullets at top; rule-of-the-road body; examples).
|
||||
sourced_from:
|
||||
- openrig-work/conventions/permission-posture/README.md (now a frontmatter pointer back here)
|
||||
related_doctrine:
|
||||
- openrig-work/conventions/security-and-consequence-boundary-policy/README.md (underlying security model — also a candidate for skill conversion)
|
||||
sibling_skills:
|
||||
- permission-and-capability-preflight
|
||||
- security-and-consequence-boundary-policy
|
||||
- native-session-file-lab-boundary
|
||||
- transcript-retention-cleanup-policy
|
||||
- human-agent-operator-posture
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Permission Posture
|
||||
|
||||
This skill is the rule of the road for permission posture in OpenRig
|
||||
work. Permission posture is part of work routing — it says whether the
|
||||
current seat can honestly do the work and proof the packet requires.
|
||||
|
||||
For the underlying security model, see
|
||||
`security-and-consequence-boundary-policy` skill.
|
||||
In short: OpenRig gates consequence boundaries, not ordinary work
|
||||
inside the intended boundary.
|
||||
|
||||
## When to use this skill
|
||||
|
||||
Reach for this skill when:
|
||||
|
||||
- **Dispatching a packet** that involves QA, tests, lifecycle, topology
|
||||
mutation, VM/host operation, or provider runtime proof — you need to
|
||||
declare the required permission posture in the packet.
|
||||
- **Classifying a candidate** for sizing (Class A/B/C) — sizing class
|
||||
determines posture; "fleet-ok" on a Class B/C vertical is a sizing-gate
|
||||
violation.
|
||||
- **Blocked by a permission denial** (sandbox, approval, filesystem,
|
||||
auth, provider quota) — apply the stop-and-flag rule; do not silently
|
||||
degrade proof or build workaround architecture.
|
||||
- **Proposing changes to permission defaults** (Codex / Claude Code /
|
||||
workspace-write roots / approval policies) — these are Mode 3 isolated
|
||||
VM territory; do not test on the live host first.
|
||||
- **Operating inside a VM** for proof work — the VM state mutation rule
|
||||
governs what's allowed inside the disposable guest vs the host.
|
||||
- **Writing a slice plan or synthesis selection memo** — posture must be
|
||||
declared explicitly per the sizing-class mapping.
|
||||
|
||||
NOT for: deciding *whether* a particular command is destructive — that's
|
||||
the security-and-consequence-boundary-policy skill (or its convention).
|
||||
NOT for: actually configuring Codex/Claude permissions — that's
|
||||
configuration work in `~/.codex/config.toml` and `.claude/settings.local.json`,
|
||||
informed by this skill but not authored by it.
|
||||
|
||||
## Hard Stop: No Standing Codex Auto-Review
|
||||
|
||||
Never use standing Codex `approvals_reviewer="auto_review"`, Codex
|
||||
`--full-auto`, or Codex approval-bypass mode as a rig default. It can
|
||||
burn provider quota at catastrophic speed. Normal Codex fleet seats use
|
||||
full access scope with `approvals_reviewer="user"` and command rules
|
||||
that prompt/forbid consequence-boundary actions. **Full access is not
|
||||
auto-review.**
|
||||
|
||||
Claude Code auto permissions are different. They are functional on this
|
||||
host and are the preferred default for Claude seats when the
|
||||
consequence-boundary rules still apply.
|
||||
|
||||
## Core Rule
|
||||
|
||||
Do not silently degrade proof because the current seat lacks
|
||||
permissions. If the needed command or surface requires stronger
|
||||
posture, **name the exact command, blocker, and required posture, then
|
||||
route to orch or the human**.
|
||||
|
||||
## Current Practical Postures (four)
|
||||
|
||||
- **`fleet`**: default Codex posture for ordinary OpenRig work. Use full
|
||||
access scope with `approvals_reviewer="user"` and command rules that
|
||||
prompt/forbid dangerous actions. Good for docs, source scans, queue
|
||||
reading/writing, normal repo edits, SSH/rsync/VM inspection, and
|
||||
broad work under `~/code`. **Not proof of permission to
|
||||
run destructive lifecycle operations.**
|
||||
- **`full QA/testing window`**: manually upgraded Codex posture for
|
||||
serious local QA, test, browser, build, or proof work when `fleet`
|
||||
cannot run the commands honestly.
|
||||
- **`operator auto window`**: legacy packet label for a temporary
|
||||
operator-authorized posture covering `rig up/down`, topology
|
||||
mutation, tmux/OpenRig lifecycle, VM/Tart/Docker, daemon, or
|
||||
host-sensitive operations. **This label does NOT mean Codex
|
||||
auto-review by default.** Use `approvals_reviewer="user"` for normal
|
||||
Codex seats; any Codex auto-review override must be rare, bounded,
|
||||
owned, and shut off when the run ends.
|
||||
- **`Mode 3 isolated VM`**: the required posture for changing
|
||||
permission defaults or proving risky permission-system changes. **Do
|
||||
not test host permission policy changes on the live host first.**
|
||||
|
||||
## Before Dispatch
|
||||
|
||||
Any packet involving QA, tests, lifecycle, topology mutation, VM/host
|
||||
operation, or provider runtime proof should state:
|
||||
|
||||
- required permission posture;
|
||||
- exact commands or surfaces that need it;
|
||||
- whether the current rig/seat appears to have it;
|
||||
- acceptable fallback, if any;
|
||||
- escalation route when the posture is missing.
|
||||
|
||||
If no stronger posture is required, say `permission_posture: fleet-ok`
|
||||
or equivalent in the packet.
|
||||
|
||||
## Stop And Flag Rule
|
||||
|
||||
When sandbox, approval, filesystem, auth, or provider permission blocks
|
||||
required work, report:
|
||||
|
||||
1. exact command or action attempted;
|
||||
2. observed blocker or denial;
|
||||
3. required posture;
|
||||
4. whether work can continue safely without that command;
|
||||
5. route requested: manual upgrade, operator window, Mode 3 VM, or
|
||||
human decision.
|
||||
|
||||
Do not create workaround architecture, substitute weaker proof, or keep
|
||||
retrying high-token approval flows unless the packet explicitly
|
||||
authorizes that fallback.
|
||||
|
||||
## VM State Mutation Rule
|
||||
|
||||
VMs are disposable proof surfaces. When the work is explicitly
|
||||
VM-scoped, VM-local mutation is allowed by default unless it crosses a
|
||||
named deny boundary. The point of using a VM is to move ordinary proof
|
||||
work from "ask before every mutation" to "do the VM-local work,
|
||||
preserve evidence, and avoid the deny list." Installing source builds,
|
||||
refreshing provider auth inside the VM, writing guest config, updating
|
||||
guest dependencies, and snapshotting a known-good guest state are all
|
||||
valid ways to make proof repeatable.
|
||||
|
||||
The hard boundary is **the host and live fleet, not the VM**:
|
||||
|
||||
- Do not mutate host `~/.claude`, host `~/.codex`, host `~/.openrig`,
|
||||
live daemon state, or live rigs unless the packet explicitly grants
|
||||
that posture.
|
||||
- Do not treat a list of example VM actions as a whitelist. If the
|
||||
action affects only the disposable guest and is needed for the named
|
||||
proof, it is in-bounds unless it touches protected host state, live
|
||||
topology, external provider account policy, or a baseline image not
|
||||
authorized for mutation.
|
||||
- Do not treat provider-auth failure inside a VM as a product pass;
|
||||
either refresh auth in the VM, use a known-good auth-ready VM
|
||||
snapshot, or mark the proof deferred on substrate grounds.
|
||||
- Prefer a named auth-ready snapshot or clone for repeated VM proofs.
|
||||
If a proof depends on a particular provider account or auth profile,
|
||||
record that profile in the VM proof packet or harness config instead
|
||||
of leaving it implicit.
|
||||
- Mutating a baseline VM is acceptable when the human/operator intends
|
||||
that VM to become the new known-good baseline. Otherwise, mutate a
|
||||
clone and promote it only after it passes readiness.
|
||||
- If a disposable clone stops on a human-actionable blocker, such as VM
|
||||
UI login, leave the clone stopped but intact by default and report
|
||||
its name/IP/evidence path. Delete immediately only when the proof is
|
||||
complete, the clone is known-bad, or the stop condition explicitly
|
||||
says cleanup now.
|
||||
|
||||
Provider-auth setup/refresh still needs an explicit posture decision
|
||||
when it consumes real provider quota, changes accounts, or affects
|
||||
proof cost. The default distinction is:
|
||||
|
||||
- **VM-local setup/refresh for an intended auth-ready proof VM**:
|
||||
allowed after operator approval for that VM lane.
|
||||
- **Host credential mutation or live-provider account policy changes**:
|
||||
Mode 3 / human-gated.
|
||||
|
||||
## Mode Mapping
|
||||
|
||||
- **Mode D**: classify posture needs in candidate and slice packets;
|
||||
do not promote work whose proof posture is hidden.
|
||||
- **Mode 0**: `fleet` is usually enough; flag if a "small" task
|
||||
unexpectedly needs host authority.
|
||||
- **Mode 1**: driver states expected verification posture before
|
||||
editing; guard rejects overclaimed verification.
|
||||
- **Mode 2**: QA/testing seats may need a `full QA/testing window`;
|
||||
declare it before dispatch.
|
||||
- **Mode 3**: lifecycle, topology, VM, daemon, auth, persistence, or
|
||||
permission-system work needs an explicit posture plan and may need a
|
||||
`Mode 3 isolated VM`.
|
||||
|
||||
## Sizing-Class Mapping
|
||||
|
||||
Per the Product Lab ambition / sizing gate (canon at
|
||||
`substrate/shared-docs/openrig-work/specs/openrig-product-lab/README.md`
|
||||
§ Ambition / Sizing Gate), candidates are classified A/B/C. Each
|
||||
sizing class has a default posture and a never-downgrade rule:
|
||||
|
||||
- **Class A** (doctrine/convention/vocabulary): `fleet-ok` almost
|
||||
always. Paper packet authoring, source/doc trace, fixture mapping,
|
||||
and applied paper review of recent flow are all `fleet-ok`.
|
||||
- **Class B** (primitive/feature delivery): `fleet-ok` is **wrong**.
|
||||
Class B v0 declares `full QA/testing window` for Tier 1
|
||||
source-backed host tests, AND `operator auto window` for live
|
||||
topology/lifecycle/daemon/session/tmux/provider state mutation
|
||||
beyond read-only, AND `Mode 3 isolated VM` for permission default
|
||||
changes, provider-auth behavior, risky lifecycle/restore proof,
|
||||
host-sensitive policy, or disposable-VM provider/runtime proof. Mix
|
||||
postures across milestones as proof shape requires; do NOT collapse
|
||||
to `fleet-ok`. Disposable-Tart-VM proof is NOT human-gated unless
|
||||
protected-host or external-spend criteria apply.
|
||||
- **Class C** (multi-primitive/rail/system-level capability): same
|
||||
posture matrix as Class B, typically with one or more milestones at
|
||||
`Mode 3 isolated VM` because rail-level proof exercises lifecycle/
|
||||
auth/host trust contracts. Composition-of-primitives across rail
|
||||
boundaries may require `operator auto window` for the orchestration
|
||||
proof and `Mode 3 isolated VM` for end-to-end real-runtime
|
||||
validation.
|
||||
|
||||
Synthesis selection-memo classification line MUST state both the class
|
||||
AND the posture for v0. Two valid forms:
|
||||
`Class A doctrine, fleet-ok, paper packet`
|
||||
OR
|
||||
`Class B/C vertical, [posture(s)], components [list]`.
|
||||
|
||||
Reviewer rejects Class B/C v0 declared at `fleet-ok` posture as a
|
||||
sizing gate violation.
|
||||
|
||||
## Examples
|
||||
|
||||
- A Codex tester cannot run the browser or full test suite under
|
||||
`fleet`: route a `full QA/testing window` request with the exact
|
||||
command.
|
||||
- An orchestrator needs to run `rig up` or `rig down`: ask the human
|
||||
for an `operator auto window` instead of trying to route around the
|
||||
blocker.
|
||||
- A slice proposes changing Codex permission defaults: make it Mode 3
|
||||
and prove first in an isolated VM.
|
||||
|
||||
## See also
|
||||
|
||||
- `velocity-permission-preflight` — velocity-specific operating rule
|
||||
that builds on this skill (permission preflight before code-producing
|
||||
or host-touching velocity slices).
|
||||
- `openrig-operator` — host-side runtime triage including Codex
|
||||
permission failures.
|
||||
- The convention pointer at
|
||||
`permission-posture` skill exists for
|
||||
taxonomic discoverability; this skill is the source of truth.
|
||||
@@ -0,0 +1,139 @@
|
||||
---
|
||||
name: queue-handoff
|
||||
description: Use when ending a turn, finishing a slice, blocked on another agent's work, or escalating to a human — durable work handoff via queue items so the system keeps moving across compactions, missed messages, and interruptions. Covers the hot-potato terminal-turn-rule (active work ends by passing the ball, not by going idle), default-nudge semantics, and when --no-nudge or --notify is required.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Translated from openrig-work/primitives/coordination/queue-handoff-and-hot-potato.md (config-layer-dogfood, 81 lines, 2026-04-29).
|
||||
Hot-potato terminal-turn-rule originally codified in field-notes/2026-04-28-hot-potato-terminal-turn-rule.md.
|
||||
Daemon-backed `rig queue` shipped in v0.2.0 (PL-004 Phase A) with the same handed-off-to / handed-off-from / state field shape as the rigx config-layer surface. Both surfaces coexist; daemon enforces hot-potato strict-rejection at the API.
|
||||
sibling_skills:
|
||||
- workflow-runtime
|
||||
- watchdog
|
||||
- alignment-trace
|
||||
- looping-workflows
|
||||
- intake-routing
|
||||
- human-in-the-loop
|
||||
- attention-queue
|
||||
- dispatching-parallel-agents
|
||||
- subagent-driven-development
|
||||
- structured-ack-dispatch
|
||||
- control-plane-capabilities
|
||||
- status-not-chat-orchestrator
|
||||
- control-plane-queue
|
||||
- control-plane-watchdog
|
||||
- control-plane-workflows
|
||||
- control-plane-delivery-loop
|
||||
- control-plane-rollout-manager
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Queue Handoff
|
||||
|
||||
Durable work handoff via queue items. Lets the system keep moving
|
||||
through compactions, missed messages, and interruptions by passing the
|
||||
ball forward instead of leaving work suspended in chat or in-flight
|
||||
without an owner.
|
||||
|
||||
## Use this when
|
||||
|
||||
- **Ending a turn on substantive work.** Active work should end by
|
||||
passing the ball to an owner or to the human — never by going idle
|
||||
with the rig appearing dormant.
|
||||
- **Finishing a slice that has a clear next step.** Default-nudge:
|
||||
receiver gets a wake-ping plus the durable queue item.
|
||||
- **Blocked on another agent's work.** Park the qitem with
|
||||
`closure_reason: blocked_on` and the blocker qitem id.
|
||||
- **Escalating to the human.** Make the escalation a durable attention
|
||||
item, not just a chat message.
|
||||
|
||||
## Don't use this when
|
||||
|
||||
- The work is genuinely complete and there's no follow-on owner. Use
|
||||
`closure_reason: no-follow-on` (terminal completion) or
|
||||
`canceled`/`denied` as appropriate.
|
||||
- The handoff would be too small and turn work into bureaucracy. Bundle
|
||||
the work into a coherent slice instead of decomposing every step.
|
||||
- The handoff would be too broad and lose ownership/proof/closure
|
||||
criteria. Shape the qitem so the receiver knows the expected next
|
||||
action and closure evidence.
|
||||
|
||||
## The hot-potato terminal-turn-rule
|
||||
|
||||
Active work ends by passing the ball to a named next owner or to the
|
||||
human. The qitem state machine enforces this:
|
||||
|
||||
`pending → in-progress → done` requires `closure_reason` from one of:
|
||||
|
||||
- `handed_off_to` — work continues at a different seat (target = new owner)
|
||||
- `blocked_on` — parked pending another qitem (target = blocker qitem id)
|
||||
- `denied` — receiver rejected the work
|
||||
- `canceled` — sender or receiver withdrew
|
||||
- `no-follow-on` — terminal completion, nothing else needed
|
||||
- `escalation` — kicked up to a higher tier (target = escalation target)
|
||||
|
||||
Three of those (`handed_off_to`, `blocked_on`, `escalation`) additionally
|
||||
require `closure_target`. The daemon enforces this at the domain layer;
|
||||
every surface (CLI, MCP, future UI) inherits the same guarantee.
|
||||
|
||||
## Default-nudge semantics (the syntax footgun)
|
||||
|
||||
| Command | Nudges by default? | When to use |
|
||||
|---|---|---|
|
||||
| `rig queue create` | yes | New qitem created from scratch |
|
||||
| `rig queue handoff` | yes | Transactional close-as-handed-off + create-new |
|
||||
| `rig queue handoff-and-complete` | **no — requires `--notify`** | Inside a self-driving loop where motion matters |
|
||||
| `rigx queue handoff` (config-layer) | yes | Same shape as daemon, config-layer dogfood |
|
||||
|
||||
**Footgun**: `handoff-and-complete` is cold unless `--notify` is passed.
|
||||
In a self-driving loop, an agent that closes with
|
||||
`handoff-and-complete` (no `--notify`) writes a durable qitem but does
|
||||
NOT wake the next owner — the rig stalls silently while the qitem sits
|
||||
in inbox.
|
||||
|
||||
**Rule**: in a live loop, use `handoff-and-complete --notify` OR send
|
||||
a separate verified manual nudge. `--no-nudge` exists but should be
|
||||
used intentionally (e.g., explicit park or human-gate signal), not as
|
||||
an accidental stall.
|
||||
|
||||
## Failure modes (5; verbatim)
|
||||
|
||||
1. Agent ends a turn without a handoff, so the rig appears idle.
|
||||
2. Agent creates a queue item but suppresses or forgets the nudge when immediate motion was intended. This includes `handoff-and-complete` without `--notify` inside a live loop.
|
||||
3. Queue item is too small and turns work into bureaucracy.
|
||||
4. Queue item is too broad and loses ownership, proof, or closure criteria.
|
||||
5. Human escalation happens in chat but not as a durable attention item.
|
||||
|
||||
## Durable handoff field shape
|
||||
|
||||
Every qitem carries:
|
||||
|
||||
- `handed_off_to` — destination session (qualified `pod-member@rig` form)
|
||||
- `handed_off_from` — source session
|
||||
- `state` — one of: `pending | in-progress | done | blocked | failed | denied | canceled | handed-off`
|
||||
- `closure_reason` + `closure_target` — set on terminal closure per hot-potato rule
|
||||
|
||||
The fields are auditable across both `rigx queue` (config-layer) and
|
||||
`rig queue` (daemon-shipped) surfaces. Watchdog policies and workflow
|
||||
runtime project new owners off these fields.
|
||||
|
||||
## Two surfaces (same shape)
|
||||
|
||||
| Surface | Status | When to use |
|
||||
|---|---|---|
|
||||
| `rig queue ...` (daemon-shipped, v0.2.0) | Active host coordination surface | Daemon-backed PL-004 work; SQLite-canonical |
|
||||
| `rigx queue ...` (config-layer dogfood) | Coexists with daemon | Workflows still operating on the temporary substrate coordination layer; legacy artifacts |
|
||||
|
||||
Default posture: prefer daemon `rig queue` for new work. If a
|
||||
daemon-backed coordination command fails, debug the command/runtime/schema
|
||||
edge directly — don't fall back to stale pre-upgrade assumptions.
|
||||
|
||||
## See also
|
||||
|
||||
- `looping-workflows` skill — operating discipline for self-driving rig-shaped loops; queue-handoff is its current handoff substrate
|
||||
- `intake-routing` skill — how raw signals enter the system and become routed work that flows through the queue
|
||||
- `substrate/shared-docs/openrig-work/primitives/coordination/queue-handoff-and-hot-potato.md` — primitive dossier with v0 codified-doctrine record + slice closure history
|
||||
- `substrate/shared-docs/openrig-work/field-notes/2026-04-28-hot-potato-terminal-turn-rule.md` — field note where the rule was first surfaced
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
---
|
||||
name: seat-continuity-and-handover
|
||||
description: Use when replacing a seat's occupant (rebuild/handover/swap), reasoning about stable-seat-identity vs fluid-occupant-identity, choosing an old-occupant disposition (retire/advise/shadow), or recording provenance for an occupant change. Two independent outcomes (continuityOutcome + seatBindingOutcome) and the 5 failure modes that prevent silent dishonesty.
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Translated from openrig-work/primitives/continuity/seat-continuity-and-handover.md (311 lines, primitive-mvp).
|
||||
Option A rebuild MVP shipped 2026-04-30 at openrig 578bd5c (Tier 1 proof complete; Tier 2 deferred). Option B (full code-direct seat handover + SeatHandoverService daemon module) named Mode 3 follow-on.
|
||||
sibling_skills:
|
||||
- claude-compaction-restore
|
||||
- mental-model-ha
|
||||
- scope-recovery
|
||||
- session-compaction-and-restore
|
||||
- agent-startup-and-context-ingestion
|
||||
- agent-starters
|
||||
- composable-priming-packs
|
||||
- session-source-fork
|
||||
- claude-compact-in-place
|
||||
- pre-maintenance-agent-preservation
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Seat Continuity and Handover
|
||||
|
||||
A pair of primitive families that separate *who is sitting in a seat* from
|
||||
*what the seat itself is*:
|
||||
|
||||
1. **Occupant-creation primitives** — `resume`, `fork`, `rebuild`, `fresh` — produce a candidate new occupant. Answer: "where did the new occupant come from?"
|
||||
2. **Seat-binding primitives** — `seat handover`, later `seat transfer` / `seat retire` / `seat swap` — bind a candidate occupant into the topology. Answer: "what happened to the stable seat identity?"
|
||||
|
||||
Core architectural decision: **stable seat identity, fluid occupant
|
||||
identity, explicit provenance.** Today's `lead2`/`lead3`/`lead4`/`lead5`
|
||||
pattern encodes successor lineage into seat names — that's the wrong
|
||||
shape. Stable seat name + separately-recorded provenance trail is the
|
||||
right shape.
|
||||
|
||||
## Use this when
|
||||
|
||||
- Replacing a seat's occupant via rebuild, fork, fresh, or future seat-handover
|
||||
- Choosing old-occupant disposition: retire / advise / shadow
|
||||
- Reasoning about whether a seat's lineage is stable or has drifted
|
||||
- Reading or writing the provenance record for a seat
|
||||
- Designing or auditing topology stability across an occupant change
|
||||
|
||||
## Don't use this when
|
||||
|
||||
- The seat is freshly created (no occupant to replace) — use `rig launch` / `rig expand` directly
|
||||
- The intent is to change topology shape (add/remove seats), not replace an occupant — use topology-mutation primitives
|
||||
|
||||
## The two-outcome honesty model
|
||||
|
||||
Every seat-binding operation produces two **independent** outcomes:
|
||||
|
||||
```yaml
|
||||
continuityOutcome: rebuilt | resumed | forked | fresh | failed
|
||||
seatBindingOutcome: handed_over | partial | failed | unchanged
|
||||
```
|
||||
|
||||
These can disagree honestly. Examples:
|
||||
|
||||
- `continuityOutcome: failed` + `seatBindingOutcome: unchanged` — new occupant didn't materialize; seat correctly retains old occupant.
|
||||
- `continuityOutcome: rebuilt` + `seatBindingOutcome: failed` — candidate created OK; bind failed mid-flight; provenance records the gap.
|
||||
|
||||
**Don't collapse these into one outcome.** The system can describe what
|
||||
actually happened only if the two are recorded independently.
|
||||
|
||||
## Provenance record (durable, queryable)
|
||||
|
||||
Every handover writes:
|
||||
|
||||
- seat id
|
||||
- old occupant id
|
||||
- new occupant id
|
||||
- creation mode (`resume`/`fork`/`rebuild`/`fresh`)
|
||||
- source artifacts used
|
||||
- whether old occupant remains alive as advisor/shadow
|
||||
- operator or loop that initiated the motion
|
||||
- timestamp
|
||||
- result (`handed_over` / `partial` / `failed`)
|
||||
|
||||
This is the system's truth-source for "how did the current occupant get
|
||||
there." Without it, the control plane shows the current occupant but
|
||||
not the legitimacy of the transition.
|
||||
|
||||
## State models — independent
|
||||
|
||||
### Occupant-creation state (per candidate)
|
||||
|
||||
1. **Requested** — input to rebuild/fork/fresh/resume
|
||||
2. **Realized** — runtime/artifact path produced an occupant with managed-seat shape
|
||||
3. **Failed** — candidate didn't materialize; `continuityOutcome: failed`
|
||||
|
||||
### Seat-binding state (per seat)
|
||||
|
||||
1. **Stable** — current occupant attached, no in-flight binding
|
||||
2. **Binding** — handover in progress
|
||||
3. **Bound** — handover succeeded; provenance record written
|
||||
4. **Unchanged** — bind failed before completion; seat retains old occupant
|
||||
|
||||
A seat stays `Stable` even if multiple candidate-occupants were produced and discarded.
|
||||
|
||||
## Failure modes (5)
|
||||
|
||||
1. **Candidate creation failed** — `rebuild` couldn't synthesize from artifacts; `fork` couldn't resolve `session_source`; `fresh` couldn't launch. **Action**: bind operation does not begin; seat unchanged; provenance records the failed candidate-creation step.
|
||||
2. **Old occupant cannot be detached cleanly** — runtime hung, tmux locked, etc. **Action**: bind halts mid-flight; seat enters `Binding` state with explicit "halted" sub-status; operator alerted. **Do NOT auto-rollback by reattaching old-occupant if detach didn't complete cleanly.**
|
||||
3. **Bind succeeded but provenance write failed** — disk/db error. **Action**: not durable until provenance writes; treat as `Binding` halted, not `Bound`.
|
||||
4. **Old occupant disposition unfulfillable** — operator requested `advise` (keep alive as advisor) but runtime can't keep old alive. **Action**: degrade to `retire` with explicit notification, OR fail if operator passed strict-disposition flag.
|
||||
5. **Concurrent handover attempts** — two operations target the same seat. **Action**: serialize by seat-id lock; second attempt refuses with clear error.
|
||||
|
||||
## Hard boundaries (do-not list; verbatim)
|
||||
|
||||
- **Do NOT collapse `rebuild` and `seat handover` into one primitive.** The design specifically separates them so the system can describe what actually happened.
|
||||
- **Do NOT introduce successor-suffix seat names** (`lead2`/`lead3`). Stable seat identity is the architectural goal.
|
||||
- **Do NOT report `seatBindingOutcome: handed_over`** if the provenance record didn't write durably.
|
||||
- **Do NOT auto-rollback a half-completed handover** by re-attaching the old occupant unless detach completed cleanly first.
|
||||
|
||||
## Composition: seat handover over fork
|
||||
|
||||
Once `session_source` fork v1 lands (already shipped at openrig
|
||||
`c7b6df1`), the next composition is **seat handover over fork**:
|
||||
candidate occupant created via fork, bound into the existing seat via
|
||||
handover. Continuity outcome is `forked`; binding outcome is independent.
|
||||
|
||||
## Currently shipped vs deferred
|
||||
|
||||
- **Option A (rebuild) shipped** at openrig `578bd5c` (2026-04-30): `session_source.mode: rebuild` with `ref.kind: artifact_set`; identity-honesty bedrock at 4 layers (schema dispatch, orchestrator threading, SQLite read-back, negative-grep on resolver output). 26/26 Tier 1 cases green; full daemon regression 2146/2146 PASS.
|
||||
- **Option B partially shipped, partially deferred to Mode 3**: the `rig seat handover` CLI exists as a planning/observability surface — `status <seat>` reads the seat-handover observability tables (migration `021`); `handover <seat>` plans a safe two-phase handover sequence with actual execution flowing through existing seat-launch surfaces under operator gating. Full code-direct `SeatHandoverService` daemon module + `seat-binding-outcome` provenance record + nodes-table provenance migration remain deferred to Mode 3. MVP composition `seat handover over fork` is highest-leverage v1.
|
||||
|
||||
## Why load-bearing for RSI
|
||||
|
||||
Any recursive seat-refresh loop must be able to replace an occupant
|
||||
while keeping topology stable. Without these primitives, RSI loops will
|
||||
either accumulate suffixed seat names (lineage leaking into identity) or
|
||||
destabilize topology references on each cycle. Provenance must be
|
||||
durable AND queryable so RSI loops can decide whether a seat is fresh
|
||||
enough to receive new work or needs re-handover.
|
||||
|
||||
## See also
|
||||
|
||||
- `session-source-fork` skill — `fork` occupant-creation primitive (sibling)
|
||||
- `agent-starters` skill — composes occupant-creation + binding into named reusable starting points
|
||||
- `cross-host-rig-commands` skill — multi-host handover (deferred)
|
||||
- `seat-continuity-and-handover` skill — primitive dossier with proof-matrix + Option A shipping evidence + Option B deferred sub-mission scope
|
||||
- `substrate/shared-docs/openrig-work/field-notes/2026-04-19-seat-handover-and-rebuild-design.md` — primary design packet
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
---
|
||||
name: session-compaction-and-restore
|
||||
description: Use when reasoning about what survives compaction/context-loss/restart/seat-refresh, designing a high-fidelity restore packet, or distinguishing native runtime resume vs fork vs artifact-backed mental-model rebuild. Covers the 4 failure modes that prevent honest restore (compacted seat drops hot potato; restore packet preserves details but loses product intent; runtime resume mistaken for handover or fork; rebuilt seat starts with stale instructions).
|
||||
metadata:
|
||||
openrig:
|
||||
stage: factory-approved
|
||||
last_verified: "2026-05-04"
|
||||
distribution_scope: product-bound
|
||||
source_evidence: |
|
||||
Translated from openrig-work/primitives/continuity/session-compaction-and-restore.md (75 lines, design-consolidation). Cross-references the canonical packet contract in missions/recursive-self-improvement-v2/work-discovery-and-slice-shaping/slices/cross-runtime-restore-reentry-packet-standard-v0/ and the externalized-memory-surfaces convention.
|
||||
sibling_skills:
|
||||
- claude-compaction-restore
|
||||
- mental-model-ha
|
||||
- scope-recovery
|
||||
- agent-startup-and-context-ingestion
|
||||
- agent-starters
|
||||
- composable-priming-packs
|
||||
- session-source-fork
|
||||
- seat-continuity-and-handover
|
||||
- claude-compact-in-place
|
||||
- pre-maintenance-agent-preservation
|
||||
transfer_test: pending
|
||||
---
|
||||
|
||||
# Session Compaction and Restore
|
||||
|
||||
Preserving useful working state across **compaction, context loss,
|
||||
restart, or seat refresh.** Includes Claude compaction restore, Codex
|
||||
resume/fork mechanics, transcript-based mental-model rebuilds, and
|
||||
durable handoff packets.
|
||||
|
||||
**Long-lived seats are valuable only if they can survive context
|
||||
pressure.** If compaction turns a senior seat into a cold-started
|
||||
agent, users will avoid persistent topologies and fall back to
|
||||
throwaway agents.
|
||||
|
||||
## Use this when
|
||||
|
||||
- A seat is approaching compaction or just compacted
|
||||
- Designing a high-fidelity restore packet for an active workflow
|
||||
- Distinguishing native runtime resume vs fork vs artifact-backed mental-model rebuild
|
||||
- Reasoning about what should be preserved vs reconstructable
|
||||
- Auditing a restore for product-intent preservation (not just detail preservation)
|
||||
|
||||
## Don't use this when
|
||||
|
||||
- The session is fresh and has no working state to preserve
|
||||
- The intent is to *create* a new seat from a primed source — that's `session-source-fork` or `agent-starters`
|
||||
- The packet is a one-off snapshot for human review — restore packets are for re-entering active work
|
||||
|
||||
## The 5 distinctions (do not collapse)
|
||||
|
||||
Per the cross-runtime restore/reentry packet standard:
|
||||
|
||||
| Mode | What it means | Outcome literal |
|
||||
|---|---|---|
|
||||
| **Native resume** | Continue the same managed seat with native runtime token | `resumed` |
|
||||
| **Fork** | New managed seat from prior native runtime conversation; new post-fork token | `forked` |
|
||||
| **Rebuild** | Fresh-launch seeded with operator-declared artifacts in trust-precedence order | `rebuilt` |
|
||||
| **Artifact-backed mental-model rebuild** | Restored seat derives understanding from a packet rather than native runtime continuity | (case of `rebuilt`) |
|
||||
| **Fresh launch** | New agent without prior continuity | `fresh` |
|
||||
|
||||
These are load-bearing distinctions. **Do NOT collapse `fork` into
|
||||
artifact-backed reentry; do NOT collapse `rebuild` into fork.**
|
||||
|
||||
## Failure modes (4)
|
||||
|
||||
1. **A compacted seat forgets active workflow state and drops the hot potato.** Compaction without continuity preservation is silent failure.
|
||||
2. **A restore packet preserves details but loses the user's product intent.** Restore must preserve *why this work matters*, not just *what was happening*.
|
||||
3. **A runtime resume is mistaken for a seat handover or fork.** These have different continuity outcomes and provenance — don't conflate.
|
||||
4. **A rebuilt seat starts with stale instructions that conflict with current workflow mode.** Restore must include current state, not just historical state.
|
||||
|
||||
## Proof standard
|
||||
|
||||
Proof should include a deliberate compaction/restart of a seat with
|
||||
active work, followed by **measured recovery**: identity, current
|
||||
workflow, next owner, relevant files, and constraints all restored
|
||||
without human re-briefing.
|
||||
|
||||
## Canonical packet contract (16-field, v0)
|
||||
|
||||
The cross-runtime restore/reentry packet standard v0 defines:
|
||||
|
||||
- Source/target identity
|
||||
- Runtimes
|
||||
- Workspace root, default repo, role pointer
|
||||
- Bounded latest transcript
|
||||
- Touched-path inventory
|
||||
- Durable work pointers
|
||||
- Current work + next owner
|
||||
- Caveats + authority boundaries
|
||||
- Omitted classes + redaction policy
|
||||
- Source-trust ranking
|
||||
- Generated-at + generator version
|
||||
|
||||
Plus a 6-item restored-seat acceptance checklist.
|
||||
|
||||
Source-trust ranking applies when restored seat ingests packet evidence:
|
||||
**`rig whoami` > target rigspec > bounded latest transcript > full
|
||||
transcript > touched-files > `restore-summary.json`.**
|
||||
|
||||
## Memory surfaces consumed at restore time
|
||||
|
||||
This primitive consumes multiple externalized memory surfaces. Per the
|
||||
externalized-memory-surfaces convention's 13-row first inventory pass,
|
||||
the primary rows for restore are:
|
||||
|
||||
| Row | Surface |
|
||||
|---|---|
|
||||
| 1 | Transcripts |
|
||||
| 2 | Durable chat |
|
||||
| 3 | Startup replay context |
|
||||
| 5 | Checkpoints |
|
||||
| 12 | Restore/reentry packets and Agent Starters |
|
||||
|
||||
The umbrella's authority-rank + permission-posture columns govern which
|
||||
surfaces a restore path can write vs only read.
|
||||
|
||||
## See also
|
||||
|
||||
- `claude-compaction-restore` skill — the Claude Code restore SOP (PreCompact hook + JSONL restore script for post-compaction recovery)
|
||||
- `mental-model-ha` skill — HA-pair compaction recovery (different scenario; sister primitive)
|
||||
- `session-source-fork` skill — `fork` mode for native-runtime-continuity-based restoration
|
||||
- `seat-continuity-and-handover` skill — occupant-creation primitives (resume/fork/rebuild/fresh) that this primitive instantiates
|
||||
- `externalized-memory-surfaces` skill — umbrella convention listing all surfaces this primitive consumes
|
||||
- `substrate/shared-docs/openrig-work/missions/recursive-self-improvement-v2/work-discovery-and-slice-shaping/slices/cross-runtime-restore-reentry-packet-standard-v0/README.md` — canonical packet contract
|
||||
- `session-compaction-and-restore` skill — primitive dossier with adjacent doctrine pointers
|
||||
Reference in new issue
Block a user