@openrig/cli 0.6.7 → 0.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/daemon/assets/plugins/openrig-core/hooks/scripts/refocus.cjs +41 -2
- package/daemon/assets/plugins/openrig-core/skills/messaging-the-human/SKILL.md +18 -4
- package/daemon/assets/plugins/openrig-core/skills/refocusing/scripts/trace-to-root.py +218 -7
- package/daemon/context-packs/help/manifest.yaml +1 -1
- package/daemon/context-packs/onboarding-width/manifest.yaml +1 -1
- package/daemon/context-packs/onboarding-width/public-what-you-can-do.md +40 -13
- package/daemon/context-packs/reference/getting-started.md +2 -2
- package/daemon/context-packs/reference/instance-layout.md +5 -0
- package/daemon/context-packs/reference/manifest.yaml +1 -1
- package/daemon/context-packs/reference/rig-spec.md +12 -9
- package/daemon/context-packs/reference/whats-new.md +71 -114
- package/daemon/context-packs/skills/agent-operated-software/manifest.yaml +1 -1
- package/daemon/context-packs/skills/agent-operated-workflows/manifest.yaml +1 -1
- package/daemon/context-packs/skills/applying-a-permission-policy/manifest.yaml +1 -1
- package/daemon/context-packs/skills/claude-compaction-restore/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/agent-starters/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/agent-startup-and-context-ingestion/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/cross-host-rig-commands/SKILL.md +1 -0
- package/daemon/context-packs/skills/core/cross-host-rig-commands/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/human-in-the-loop/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/messaging-the-human/SKILL.md +18 -4
- package/daemon/context-packs/skills/core/messaging-the-human/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-architect/SKILL.md +3 -0
- package/daemon/context-packs/skills/core/openrig-architect/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-cmux/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-herdr/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-skills/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-software-factory/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/openrig-software-factory/references/worked-example.md +15 -14
- package/daemon/context-packs/skills/core/openrig-upgrade/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/rig-bundles-and-shareable-artifacts/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/rig-lifecycle/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/session-source-fork/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/specification-system/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/topology-mutation-and-seat-management/manifest.yaml +1 -1
- package/daemon/context-packs/skills/core/watchdog/manifest.yaml +1 -1
- package/daemon/context-packs/skills/delegating-work/manifest.yaml +1 -1
- package/daemon/context-packs/skills/forming-an-openrig-mental-model/manifest.yaml +1 -1
- package/daemon/context-packs/skills/loading-addressable-markdown/manifest.yaml +1 -1
- package/daemon/context-packs/skills/mission-slice-sop/manifest.yaml +1 -1
- package/daemon/context-packs/skills/openrig-operating-model/manifest.yaml +1 -1
- package/daemon/context-packs/skills/openrig-user/manifest.yaml +1 -1
- package/daemon/context-packs/skills/orienting-to-an-inherited-seat/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/backlog-capture/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/context-builder/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/exec-summary/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/office-hours/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/plan-review/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/requirements-writer/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pm/ui-mockup/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/development-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/orchestration-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/oversight-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/pods/review-team/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/agent-browser/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/context-engineering/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/dogfood/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/frontend-design/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/systematic-debugging/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/test-driven-development/manifest.yaml +1 -1
- package/daemon/context-packs/skills/process/verification-before-completion/manifest.yaml +1 -1
- package/daemon/context-packs/skills/queue-handoff/manifest.yaml +1 -1
- package/daemon/context-packs/skills/refocusing/manifest.yaml +1 -1
- package/daemon/context-packs/skills/refocusing/scripts/trace-to-root.py +218 -7
- package/daemon/context-packs/skills/retiring-and-inheriting-a-seat/manifest.yaml +1 -1
- package/daemon/context-packs/skills/seat-continuity-and-handover/manifest.yaml +1 -1
- package/daemon/context-packs/skills/session-compaction-and-restore/manifest.yaml +1 -1
- package/daemon/context-packs/skills/software-for-agents/manifest.yaml +1 -1
- package/daemon/context-packs/world-example/manifest.yaml +1 -1
- package/daemon/context-packs/world-public/manifest.yaml +1 -1
- package/daemon/dist/build-info.js +3 -3
- package/daemon/dist/db/all-migrations.d.ts.map +1 -1
- package/daemon/dist/db/all-migrations.js +2 -0
- package/daemon/dist/db/all-migrations.js.map +1 -1
- package/daemon/dist/db/migrations/098_queue_transitions_qitem_id_order.d.ts +3 -0
- package/daemon/dist/db/migrations/098_queue_transitions_qitem_id_order.d.ts.map +1 -0
- package/daemon/dist/db/migrations/098_queue_transitions_qitem_id_order.js +12 -0
- package/daemon/dist/db/migrations/098_queue_transitions_qitem_id_order.js.map +1 -0
- package/daemon/dist/domain/claude-compaction-enforcer.d.ts +23 -2
- package/daemon/dist/domain/claude-compaction-enforcer.d.ts.map +1 -1
- package/daemon/dist/domain/claude-compaction-enforcer.js +153 -94
- package/daemon/dist/domain/claude-compaction-enforcer.js.map +1 -1
- package/daemon/dist/domain/context-monitor.d.ts +5 -0
- package/daemon/dist/domain/context-monitor.d.ts.map +1 -1
- package/daemon/dist/domain/context-monitor.js +21 -6
- package/daemon/dist/domain/context-monitor.js.map +1 -1
- package/daemon/dist/domain/current-work.d.ts +74 -0
- package/daemon/dist/domain/current-work.d.ts.map +1 -1
- package/daemon/dist/domain/current-work.js +165 -0
- package/daemon/dist/domain/current-work.js.map +1 -1
- package/daemon/dist/domain/gateway/connections-projection.d.ts +2 -0
- package/daemon/dist/domain/gateway/connections-projection.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/connections-projection.js +3 -1
- package/daemon/dist/domain/gateway/connections-projection.js.map +1 -1
- package/daemon/dist/domain/gateway/gateway-subsystem.d.ts +3 -1
- package/daemon/dist/domain/gateway/gateway-subsystem.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/gateway-subsystem.js +3 -1
- package/daemon/dist/domain/gateway/gateway-subsystem.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/inbound.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/inbound.js +1 -0
- package/daemon/dist/domain/gateway/slack/inbound.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/queue-access.d.ts +5 -0
- package/daemon/dist/domain/gateway/slack/queue-access.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/queue-access.js +6 -1
- package/daemon/dist/domain/gateway/slack/queue-access.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-api.d.ts +7 -2
- package/daemon/dist/domain/gateway/slack/slack-api.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-api.js +35 -3
- package/daemon/dist/domain/gateway/slack/slack-api.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts +4 -0
- package/daemon/dist/domain/gateway/slack/slack-delivery.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.js +16 -1
- package/daemon/dist/domain/gateway/slack/slack-delivery.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts +17 -0
- package/daemon/dist/domain/gateway/slack/slack-subsystem.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js +92 -34
- package/daemon/dist/domain/gateway/slack/slack-subsystem.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/socket-inbound.d.ts +37 -0
- package/daemon/dist/domain/gateway/slack/socket-inbound.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/socket-inbound.js +281 -38
- package/daemon/dist/domain/gateway/slack/socket-inbound.js.map +1 -1
- package/daemon/dist/domain/gateway/slack/state-store.d.ts +14 -1
- package/daemon/dist/domain/gateway/slack/state-store.d.ts.map +1 -1
- package/daemon/dist/domain/gateway/slack/state-store.js +16 -0
- package/daemon/dist/domain/gateway/slack/state-store.js.map +1 -1
- package/daemon/dist/domain/human-questions.d.ts +12 -3
- package/daemon/dist/domain/human-questions.d.ts.map +1 -1
- package/daemon/dist/domain/human-questions.js +10 -5
- package/daemon/dist/domain/human-questions.js.map +1 -1
- package/daemon/dist/domain/kernel-boot-tracker.d.ts +20 -1
- package/daemon/dist/domain/kernel-boot-tracker.d.ts.map +1 -1
- package/daemon/dist/domain/kernel-boot-tracker.js +66 -14
- package/daemon/dist/domain/kernel-boot-tracker.js.map +1 -1
- package/daemon/dist/domain/kernel-boot.d.ts +4 -0
- package/daemon/dist/domain/kernel-boot.d.ts.map +1 -1
- package/daemon/dist/domain/kernel-boot.js +26 -1
- package/daemon/dist/domain/kernel-boot.js.map +1 -1
- package/daemon/dist/domain/native-process-lineage.d.ts.map +1 -1
- package/daemon/dist/domain/native-process-lineage.js +44 -9
- package/daemon/dist/domain/native-process-lineage.js.map +1 -1
- package/daemon/dist/domain/profile-resolver.d.ts.map +1 -1
- package/daemon/dist/domain/profile-resolver.js +54 -15
- package/daemon/dist/domain/profile-resolver.js.map +1 -1
- package/daemon/dist/domain/queue-nudge-text.d.ts +4 -0
- package/daemon/dist/domain/queue-nudge-text.d.ts.map +1 -0
- package/daemon/dist/domain/queue-nudge-text.js +35 -0
- package/daemon/dist/domain/queue-nudge-text.js.map +1 -0
- package/daemon/dist/domain/queue-repository.d.ts +17 -0
- package/daemon/dist/domain/queue-repository.d.ts.map +1 -1
- package/daemon/dist/domain/queue-repository.js +58 -7
- package/daemon/dist/domain/queue-repository.js.map +1 -1
- package/daemon/dist/domain/queue-transition-log.d.ts +12 -4
- package/daemon/dist/domain/queue-transition-log.d.ts.map +1 -1
- package/daemon/dist/domain/queue-transition-log.js +24 -0
- package/daemon/dist/domain/queue-transition-log.js.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.d.ts +1 -0
- package/daemon/dist/domain/restore-orchestrator.d.ts.map +1 -1
- package/daemon/dist/domain/restore-orchestrator.js +17 -11
- package/daemon/dist/domain/restore-orchestrator.js.map +1 -1
- package/daemon/dist/domain/rig-teardown.d.ts.map +1 -1
- package/daemon/dist/domain/rig-teardown.js +6 -0
- package/daemon/dist/domain/rig-teardown.js.map +1 -1
- package/daemon/dist/domain/scope/scope-approve.d.ts +21 -0
- package/daemon/dist/domain/scope/scope-approve.d.ts.map +1 -1
- package/daemon/dist/domain/scope/scope-approve.js +48 -4
- package/daemon/dist/domain/scope/scope-approve.js.map +1 -1
- package/daemon/dist/domain/session-transport.d.ts +12 -1
- package/daemon/dist/domain/session-transport.d.ts.map +1 -1
- package/daemon/dist/domain/session-transport.js +33 -7
- package/daemon/dist/domain/session-transport.js.map +1 -1
- package/daemon/dist/domain/skill-catalog.d.ts +11 -0
- package/daemon/dist/domain/skill-catalog.d.ts.map +1 -1
- package/daemon/dist/domain/skill-catalog.js +136 -12
- package/daemon/dist/domain/skill-catalog.js.map +1 -1
- package/daemon/dist/domain/workflow-validator.d.ts.map +1 -1
- package/daemon/dist/domain/workflow-validator.js +22 -1
- package/daemon/dist/domain/workflow-validator.js.map +1 -1
- package/daemon/dist/routes/gateway.d.ts.map +1 -1
- package/daemon/dist/routes/gateway.js +2 -0
- package/daemon/dist/routes/gateway.js.map +1 -1
- package/daemon/dist/routes/kernel-status.js +3 -1
- package/daemon/dist/routes/kernel-status.js.map +1 -1
- package/daemon/dist/routes/queue.d.ts.map +1 -1
- package/daemon/dist/routes/queue.js +17 -2
- package/daemon/dist/routes/queue.js.map +1 -1
- package/daemon/dist/routes/scope-approve.d.ts.map +1 -1
- package/daemon/dist/routes/scope-approve.js +3 -0
- package/daemon/dist/routes/scope-approve.js.map +1 -1
- package/daemon/dist/routes/sessions.js +1 -1
- package/daemon/dist/routes/sessions.js.map +1 -1
- package/daemon/docs/reference/agent-startup-guide.md +1 -1
- package/daemon/docs/reference/bundle-formats.md +1 -1
- package/daemon/docs/reference/getting-started.md +2 -2
- package/daemon/docs/reference/instance-layout.md +5 -0
- package/daemon/docs/reference/publishing-a-rig-bundle.md +4 -2
- package/daemon/docs/reference/refocus-channel.md +33 -0
- package/daemon/docs/reference/rig-spec.md +12 -9
- package/daemon/docs/reference/slack-app-setup.md +18 -1
- package/daemon/docs/reference/topology-naming.md +74 -0
- package/daemon/docs/reference/whats-new.md +71 -114
- package/daemon/specs/agents/shared/skills/core/cross-host-rig-commands/SKILL.md +1 -0
- package/daemon/specs/agents/shared/skills/core/messaging-the-human/SKILL.md +18 -4
- package/daemon/specs/agents/shared/skills/core/openrig-architect/SKILL.md +3 -0
- package/daemon/specs/agents/shared/skills/core/openrig-software-factory/references/worked-example.md +15 -14
- package/dist/build-info.js +3 -3
- package/dist/commands/chatroom.d.ts.map +1 -1
- package/dist/commands/chatroom.js +25 -5
- package/dist/commands/chatroom.js.map +1 -1
- package/dist/commands/context.d.ts.map +1 -1
- package/dist/commands/context.js +8 -0
- package/dist/commands/context.js.map +1 -1
- package/dist/commands/launch.d.ts.map +1 -1
- package/dist/commands/launch.js +114 -8
- package/dist/commands/launch.js.map +1 -1
- package/dist/commands/proof.js +1 -1
- package/dist/commands/proof.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +3 -0
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +11 -2
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/send.d.ts.map +1 -1
- package/dist/commands/send.js +23 -5
- package/dist/commands/send.js.map +1 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +6 -0
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/slack.d.ts +17 -0
- package/dist/commands/slack.d.ts.map +1 -1
- package/dist/commands/slack.js +41 -0
- package/dist/commands/slack.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +5 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/cross-host-types.d.ts +1 -1
- package/dist/cross-host-types.d.ts.map +1 -1
- package/dist/launch-plan-guard.d.ts +27 -0
- package/dist/launch-plan-guard.d.ts.map +1 -0
- package/dist/launch-plan-guard.js +73 -0
- package/dist/launch-plan-guard.js.map +1 -0
- package/dist/lib/scope/scope-fs.d.ts +13 -0
- package/dist/lib/scope/scope-fs.d.ts.map +1 -1
- package/dist/lib/scope/scope-fs.js +28 -4
- package/dist/lib/scope/scope-fs.js.map +1 -1
- package/dist/remote-host-ops.d.ts +4 -0
- package/dist/remote-host-ops.d.ts.map +1 -1
- package/dist/remote-host-ops.js +20 -2
- package/dist/remote-host-ops.js.map +1 -1
- package/package.json +2 -2
- package/tui/dist/commands/registry.js +1 -1
- package/tui/dist/connections/connections-model.d.ts +5 -0
- package/tui/dist/connections/connections-model.d.ts.map +1 -1
- package/tui/dist/connections/connections-model.js +16 -1
- package/tui/dist/connections/connections-model.js.map +1 -1
- package/tui/dist/main.js +16 -4
- package/tui/dist/main.js.map +1 -1
- package/tui/dist/render.js +1 -1
- package/tui/dist/scopes/scopes-model.js +4 -4
- package/tui/dist/types.d.ts +1 -1
package/README.md
CHANGED
|
@@ -22,11 +22,11 @@ Not setting this up today? Get the next walkthrough and occasional OpenRig updat
|
|
|
22
22
|
|
|
23
23
|
Requires Node.js 22 or 24 and tmux, on macOS or Linux. On Linux, the distribution's own Node.js can be older (Ubuntu 24.04's is 18); install a supported version with [nvm](https://github.com/nvm-sh/nvm) (`nvm install 22`) or NodeSource. On a Mac with Apple silicon, use Node.js 22 ([compatibility history](docs/releases/v0.5.15.md#known-compatibility-limitation)). On Windows, use WSL2, the Windows route OpenRig supports; native Windows isn't supported. OpenRig's automated tests don't run on WSL2 yet; see [one user's working setup](docs/reference/getting-started.md#wsl2-a-reported-working-setup). Launching a rig writes provider hooks and workspace trust settings. Before running the commands below, read [what OpenRig changes on your machine](#what-openrig-changes-on-your-machine) and back up the relevant files.
|
|
24
24
|
|
|
25
|
-
**One command.** This runs OpenRig's install script from the `v0.6.
|
|
25
|
+
**One command.** This runs OpenRig's install script from the `v0.6.8` release. It checks Node.js and npm, installs the latest published `@openrig/cli` with `npm install -g`, runs the Node.js and SQLite check, then runs `rig setup --dry-run` and `rig setup`. The first line prints that plan and changes nothing:
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.
|
|
29
|
-
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.
|
|
28
|
+
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.8/scripts/install.sh | sh -s -- --dry-run
|
|
29
|
+
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.8/scripts/install.sh | sh
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
If a provider isn't signed in yet, the last step reports `FAILED [4/4]`, and `rig setup` lists the sign-in under "Some steps need attention". If the only remaining failures are provider sign-ins, the install steps finished; sign in to each selected provider as below and continue.
|
|
@@ -107,7 +107,9 @@ recommends one of three, and fits it to the providers you have.
|
|
|
107
107
|
|
|
108
108
|
As shipped, `starter` uses both Claude Code and Codex. With only one of them, ask
|
|
109
109
|
the kernel operator to adapt it: it writes a copy of the team for your providers
|
|
110
|
-
under the same name.
|
|
110
|
+
under the same name. [The naming guide](docs/reference/topology-naming.md) explains
|
|
111
|
+
how their pods and roles grow and how to name your own team. `first-project` is
|
|
112
|
+
starter's old name and still starts it,
|
|
111
113
|
unless you already have a rig named `first-project` or `starter`: then it refuses
|
|
112
114
|
and names the `rig up <name> --existing` command that brings that rig back.
|
|
113
115
|
Show the selected runtime, configured model and command before launch; confirm the
|
|
@@ -102,10 +102,29 @@ function readConfiguredContent(home) {
|
|
|
102
102
|
// current work, its basis travels to the trace script, which alone decides how to render it;
|
|
103
103
|
// a failed or unreadable answer is passed as UNKNOWN. Never guess a work node here: a guess
|
|
104
104
|
// would silently re-point the whole trace.
|
|
105
|
+
// OPR.0.7.0.12 — the work packet, switched off by default: the trace receives the daemon's
|
|
106
|
+
// labelled work candidates and carries duties and notes as text. With it off, every path below
|
|
107
|
+
// is unchanged.
|
|
108
|
+
function workPacketEnabled() {
|
|
109
|
+
return enabled(process.env.OPENRIG_REFOCUS_WORK_PACKET, false);
|
|
110
|
+
}
|
|
111
|
+
|
|
105
112
|
function readQueueWhoami(timeout = 2_000) {
|
|
106
|
-
const
|
|
113
|
+
const args = ["queue", "whoami", "--json", ...(workPacketEnabled() ? ["--work-candidates"] : [])];
|
|
114
|
+
let result = spawnSync("rig", args, {
|
|
107
115
|
encoding: "utf8", env: process.env, timeout, maxBuffer: 16 * 1024 * 1024,
|
|
108
116
|
});
|
|
117
|
+
// A rig CLI older than this hook rejects the flag. Ask again without it, inside what is left of
|
|
118
|
+
// the budget, so the role and the strict answer still arrive (just without candidates).
|
|
119
|
+
if (args.includes("--work-candidates") && !result.error && result.status !== 0
|
|
120
|
+
&& /unknown option/i.test(`${result.stderr || ""}${result.stdout || ""}`)) {
|
|
121
|
+
const remaining = remainingLookupBudget();
|
|
122
|
+
if (remaining > 250) {
|
|
123
|
+
result = spawnSync("rig", ["queue", "whoami", "--json"], {
|
|
124
|
+
encoding: "utf8", env: process.env, timeout: remaining, maxBuffer: 16 * 1024 * 1024,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
109
128
|
if (result.error) return { unknown: `queue whoami failed: ${result.error.message}` };
|
|
110
129
|
if (result.status !== 0 || !result.stdout || !result.stdout.trim()) {
|
|
111
130
|
return { unknown: `queue whoami exited ${result.status ?? "without a status"} with no answer` };
|
|
@@ -208,11 +227,31 @@ function renderTrace() {
|
|
|
208
227
|
}
|
|
209
228
|
role = renderRole(whoami);
|
|
210
229
|
}
|
|
211
|
-
|
|
230
|
+
const packet = workPacketEnabled();
|
|
231
|
+
let evidence = null;
|
|
232
|
+
if (packet) {
|
|
233
|
+
args.push("--packet");
|
|
234
|
+
if (trees !== "topology") {
|
|
235
|
+
if (!whoami) {
|
|
236
|
+
const timeout = remainingLookupBudget();
|
|
237
|
+
whoami = timeout > 250 ? readQueueWhoami(timeout) : { unknown: "no budget left" };
|
|
238
|
+
}
|
|
239
|
+
const candidates = whoami.answer?.workCandidates;
|
|
240
|
+
if (candidates && typeof candidates === "object") evidence = JSON.stringify(candidates);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// An explicit start always wins. Otherwise the candidates replace the strict answer: the
|
|
244
|
+
// strict node ignores mission-only rows, so it could hide a second mission in flight.
|
|
245
|
+
const explicitStart = Boolean(process.env.OPENRIG_REFOCUS_WORK_NODE);
|
|
246
|
+
if (work.start && (explicitStart || !evidence)) args.push("--work-start", work.start);
|
|
247
|
+
else if (evidence) args.push("--work-candidates", "-");
|
|
212
248
|
else if (work.basis) args.push("--work-basis", work.basis);
|
|
213
249
|
else if (work.unknown) args.push("--work-unknown", work.unknown);
|
|
250
|
+
// With an explicit start, the evidence supplies only held and next work.
|
|
251
|
+
if (explicitStart && evidence) args.push("--work-candidates", "-");
|
|
214
252
|
const result = spawnSync(process.env.PYTHON || "python3", args, {
|
|
215
253
|
encoding: "utf8",
|
|
254
|
+
...(evidence ? { input: evidence } : {}),
|
|
216
255
|
env: traceEnv(trees),
|
|
217
256
|
timeout: 2_000,
|
|
218
257
|
maxBuffer: 16 * 1024 * 1024,
|
|
@@ -92,16 +92,30 @@ the human (a pending human decision, or a row parked on the human), since a
|
|
|
92
92
|
reply in that thread would answer the decision. In every such case the
|
|
93
93
|
`--verify` result says `threaded: false` with the reason.
|
|
94
94
|
|
|
95
|
+
To answer a reply the human typed inside one of your threads, `--reply-to` the
|
|
96
|
+
row their reply landed as. The update posts in that thread unless one of the
|
|
97
|
+
cases above applies, and `--verify` reports it the same way. A message the
|
|
98
|
+
human started at the top level of the channel has no thread yet, so the answer
|
|
99
|
+
posts as a new message.
|
|
100
|
+
|
|
95
101
|
A **decision** with a few clear choices can carry `--human-questions-file <path>`:
|
|
96
102
|
a JSON array of 1–4 questions, each
|
|
97
103
|
`{"id", "question", "options": [{"id", "label", "recommended"?}]}` with 2–4
|
|
98
104
|
options (labels up to 75 characters, at most one recommended). Slack shows each
|
|
99
105
|
question as a row of buttons. Each click records that answer on the item, and
|
|
100
106
|
the decision resolves once every question has one. You then receive one reply
|
|
101
|
-
row listing the answers
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
107
|
+
row listing the answers. In `humanAnswers`, clicked answers are option-id strings.
|
|
108
|
+
A whole typed reply is an object:
|
|
109
|
+
`{kind: "typed-reply", text: "…", placement: "first-unanswered", unansweredCount: N}`.
|
|
110
|
+
It is placed automatically under the first unanswered question; the person did
|
|
111
|
+
not select that question. `N` counts the other question slots still empty in
|
|
112
|
+
`humanAnswers` after that placement.
|
|
113
|
+
Earlier button answers stay intact. Read `text` as the person's words, never as
|
|
114
|
+
an option id even if the strings match. The typed reply still closes the decision
|
|
115
|
+
with the other questions unanswered; `done` is not approval. If every button
|
|
116
|
+
answer was already recorded, that final answer set stays intact and the typed
|
|
117
|
+
reply remains in its correlated reply row. Keep the brief complete: the questions
|
|
118
|
+
add buttons, they do not replace the explanation.
|
|
105
119
|
|
|
106
120
|
If an existing agent-owned row must wait for a **decision**, block it on the **new live qitem ID**
|
|
107
121
|
(`rig queue block <work-id> --on <human-qitem-id> ...`), not on the human address.
|
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
"""Render topology and work context by ascending directory paths only."""
|
|
3
3
|
|
|
4
4
|
import argparse
|
|
5
|
+
import datetime
|
|
5
6
|
import json
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
8
9
|
import subprocess
|
|
10
|
+
import sys
|
|
9
11
|
from pathlib import Path
|
|
10
12
|
|
|
11
13
|
SHELVES = {"rigs", "pods", "seats", "missions", "slices"}
|
|
@@ -148,7 +150,82 @@ def light_learned(text):
|
|
|
148
150
|
return body[:boundary if boundary > 0 else 800].rstrip() + "\n[… use --depth full for the rest]"
|
|
149
151
|
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
# OPR.0.7.0.12 — the refocus packet (--packet). Duties and notes are carried as text by
|
|
154
|
+
# conventional section, with an honest "not found" and a bounded excerpt otherwise; nobody is
|
|
155
|
+
# asked to add headings. Old files are named with their measured age as a cue, not a verdict.
|
|
156
|
+
DUTY_HEADINGS = ("my job here", "standing duties")
|
|
157
|
+
NOTES_STATE_HEADINGS = ("current state",)
|
|
158
|
+
HEADING = re.compile(r"^(#{1,6})\s+(.*?)\s*#*\s*$")
|
|
159
|
+
DEFAULT_NOTES_MAX_AGE_DAYS = 14
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def heading_sections(text, wanted):
|
|
163
|
+
"""[(title, body)] for each Markdown heading whose title starts with a wanted phrase."""
|
|
164
|
+
lines = re.sub(r"^---\s*\n.*?\n---\s*\n", "", text or "", count=1, flags=re.S).splitlines()
|
|
165
|
+
found = []
|
|
166
|
+
for index, line in enumerate(lines):
|
|
167
|
+
match = HEADING.match(line)
|
|
168
|
+
if not match or not match.group(2).strip().lower().startswith(wanted):
|
|
169
|
+
continue
|
|
170
|
+
level = len(match.group(1))
|
|
171
|
+
body = []
|
|
172
|
+
for later in lines[index + 1:]:
|
|
173
|
+
heading = HEADING.match(later)
|
|
174
|
+
if heading and len(heading.group(1)) <= level:
|
|
175
|
+
break
|
|
176
|
+
body.append(later)
|
|
177
|
+
found.append((match.group(2).strip(), "\n".join(body).strip()))
|
|
178
|
+
return found
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def by_section(text, wanted, path):
|
|
182
|
+
found = heading_sections(text, wanted)
|
|
183
|
+
shown = "\n\n".join(f"{title}\n{body}" if body else title for title, body in found)
|
|
184
|
+
missing = [phrase for phrase in wanted if not any(title.lower().startswith(phrase) for title, _ in found)]
|
|
185
|
+
if not missing:
|
|
186
|
+
return shown
|
|
187
|
+
if not found:
|
|
188
|
+
names = " or ".join(f'"{phrase.upper()}"' for phrase in wanted)
|
|
189
|
+
return f"SECTION NOT FOUND — no {names} heading in {path}; bounded excerpt follows\n{light_learned(text)}"
|
|
190
|
+
# Some sections matched: name each one that did not, with the same bounded excerpt, so text
|
|
191
|
+
# kept under another heading is not silently dropped.
|
|
192
|
+
names = ", ".join(f'"{phrase.upper()}"' for phrase in missing)
|
|
193
|
+
return f"{shown}\n\nSECTION NOT FOUND — no {names} heading in {path}; bounded excerpt follows\n{light_learned(text)}"
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def notes_max_age_days():
|
|
197
|
+
try:
|
|
198
|
+
value = float(os.environ.get("OPENRIG_REFOCUS_NOTES_MAX_AGE_DAYS", DEFAULT_NOTES_MAX_AGE_DAYS))
|
|
199
|
+
except ValueError:
|
|
200
|
+
return DEFAULT_NOTES_MAX_AGE_DAYS
|
|
201
|
+
return value if value > 0 else DEFAULT_NOTES_MAX_AGE_DAYS
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def age_cue(path, text):
|
|
205
|
+
"""An OLD NOTES line when the file is past the threshold, measured from `updated:` or mtime."""
|
|
206
|
+
threshold = notes_max_age_days()
|
|
207
|
+
updated, source = None, "mtime"
|
|
208
|
+
match = re.match(r"^---\s*\n(.*?)\n---", text or "", re.S)
|
|
209
|
+
if match:
|
|
210
|
+
found = re.search(r"^updated:\s*[\"']?(\d{4}-\d{2}-\d{2})", match.group(1), re.M)
|
|
211
|
+
if found:
|
|
212
|
+
try:
|
|
213
|
+
updated = datetime.datetime.strptime(found.group(1), "%Y-%m-%d").replace(tzinfo=datetime.timezone.utc)
|
|
214
|
+
source = "updated"
|
|
215
|
+
except ValueError:
|
|
216
|
+
updated = None
|
|
217
|
+
if updated is None:
|
|
218
|
+
try:
|
|
219
|
+
updated = datetime.datetime.fromtimestamp(path.stat().st_mtime, datetime.timezone.utc)
|
|
220
|
+
except OSError:
|
|
221
|
+
return None
|
|
222
|
+
age = (datetime.datetime.now(datetime.timezone.utc) - updated).total_seconds() / 86400
|
|
223
|
+
if age <= threshold:
|
|
224
|
+
return None
|
|
225
|
+
return f"OLD NOTES — {path}, {age:.0f} days old, source {source}, threshold {threshold:g} days"
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def render_topology(start, root, depth, failures, packet=False):
|
|
152
229
|
output = ["## TOPOLOGY TRACE", f"root: {root}", f"start: {start}"]
|
|
153
230
|
nodes, error = ascent(start, root)
|
|
154
231
|
if error:
|
|
@@ -161,12 +238,21 @@ def render_topology(start, root, depth, failures):
|
|
|
161
238
|
if text is None:
|
|
162
239
|
output.append(f"\n### {label}\nMISSING LINK — {chain_file}")
|
|
163
240
|
continue
|
|
164
|
-
|
|
241
|
+
if depth == "full":
|
|
242
|
+
body = text.strip()
|
|
243
|
+
elif packet and node == nodes[-1]:
|
|
244
|
+
# The seat's own file: its job and standing duties as text.
|
|
245
|
+
body = by_section(text, DUTY_HEADINGS, chain_file)
|
|
246
|
+
else:
|
|
247
|
+
body = light_learned(text)
|
|
165
248
|
output.append(f"\n### {label} · LEARNED.md\n{body}")
|
|
249
|
+
cue = age_cue(chain_file, text) if packet else None
|
|
250
|
+
if cue:
|
|
251
|
+
output.append(cue)
|
|
166
252
|
return "\n".join(output)
|
|
167
253
|
|
|
168
254
|
|
|
169
|
-
def render_work(start, root, depth, failures, fallback=None):
|
|
255
|
+
def render_work(start, root, depth, failures, fallback=None, packet=False):
|
|
170
256
|
output = ["## WORK TRACE", f"root: {root}", f"start: {start}"]
|
|
171
257
|
if fallback:
|
|
172
258
|
output.append(f"FALLBACK — {fallback}")
|
|
@@ -201,6 +287,19 @@ def render_work(start, root, depth, failures, fallback=None):
|
|
|
201
287
|
output.append(f"NOTES RESOLUTION GAP — resolved {notes_name} became unreadable at {notes_path}")
|
|
202
288
|
else:
|
|
203
289
|
output.append(f"\nNOTES · {notes_name}\n{notes_text.strip()}")
|
|
290
|
+
cue = age_cue(notes_path, notes_text) if packet else None
|
|
291
|
+
if cue:
|
|
292
|
+
output.append(cue)
|
|
293
|
+
elif packet:
|
|
294
|
+
notes_text = read(notes_path)
|
|
295
|
+
if notes_text is None:
|
|
296
|
+
failures.append(f"unreadable notes: {notes_path}")
|
|
297
|
+
output.append(f"NOTES RESOLUTION GAP — resolved {notes_name} became unreadable at {notes_path}")
|
|
298
|
+
else:
|
|
299
|
+
output.append(f"\nNOTES · {notes_name}\n{by_section(notes_text, NOTES_STATE_HEADINGS, notes_path)}")
|
|
300
|
+
cue = age_cue(notes_path, notes_text)
|
|
301
|
+
if cue:
|
|
302
|
+
output.append(cue)
|
|
204
303
|
else:
|
|
205
304
|
try:
|
|
206
305
|
size = notes_path.stat().st_size
|
|
@@ -211,9 +310,107 @@ def render_work(start, root, depth, failures, fallback=None):
|
|
|
211
310
|
output.append(f"NOTES · {notes_name} · {size} bytes · {notes_path}")
|
|
212
311
|
else:
|
|
213
312
|
output.append(f"NOTES GAP — no readable mission notes at {node}")
|
|
313
|
+
|
|
314
|
+
# The SDLC convention keeps a node's current delivery state in PROGRESS.md; carry it
|
|
315
|
+
# as text when present. Its absence is not a gap: many nodes have no PROGRESS.md.
|
|
316
|
+
progress_path = node / "PROGRESS.md"
|
|
317
|
+
progress_text = read(progress_path) if packet else None
|
|
318
|
+
if progress_text is not None:
|
|
319
|
+
progress_body = progress_text.strip() if depth == "full" else by_section(progress_text, NOTES_STATE_HEADINGS, progress_path)
|
|
320
|
+
output.append(f"\nPROGRESS · PROGRESS.md\n{progress_body}")
|
|
321
|
+
cue = age_cue(progress_path, progress_text)
|
|
322
|
+
if cue:
|
|
323
|
+
output.append(cue)
|
|
324
|
+
return "\n".join(output)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def candidate_line(candidate):
|
|
328
|
+
name = candidate.get("mission") or "?"
|
|
329
|
+
if candidate.get("slice"):
|
|
330
|
+
name += f" / {candidate['slice']}"
|
|
331
|
+
intent_text = candidate.get("intent") or "no readable intent"
|
|
332
|
+
line = f"- {name} — {intent_text} — source: {candidate.get('source') or 'unknown'}"
|
|
333
|
+
if candidate.get("unresolved"):
|
|
334
|
+
line += f" — UNRESOLVED ON THIS HOST: {candidate['unresolved']}"
|
|
335
|
+
return line
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def render_held_and_next(evidence):
|
|
339
|
+
output = []
|
|
340
|
+
held = evidence.get("held") or []
|
|
341
|
+
if held:
|
|
342
|
+
output.append("HELD WORK (blocked; this is not next):")
|
|
343
|
+
for row in held:
|
|
344
|
+
output.append(f"- {row.get('qitemId') or 'a row'} — {row.get('summary') or 'no summary'} — "
|
|
345
|
+
f"blocked on {row.get('blockedOn') or 'unknown'} — "
|
|
346
|
+
f"continuation: {row.get('continuation') or 'none recorded'}")
|
|
347
|
+
following = evidence.get("next") or []
|
|
348
|
+
if following:
|
|
349
|
+
output.append("POSSIBLE NEXT WORK (pending; no order implied):")
|
|
350
|
+
for row in following:
|
|
351
|
+
missions = ", ".join(row.get("missions") or [])
|
|
352
|
+
output.append(f"- {row.get('qitemId') or 'a row'} — {row.get('summary') or 'no summary'}"
|
|
353
|
+
+ (f" [mission: {missions}]" if missions else ""))
|
|
354
|
+
if evidence.get("heldAndNextTruncated"):
|
|
355
|
+
output.append("(held and next lists were cut; `rig queue list` has the rest)")
|
|
214
356
|
return "\n".join(output)
|
|
215
357
|
|
|
216
358
|
|
|
359
|
+
def render_candidates(evidence, root, depth, failures):
|
|
360
|
+
"""The work section from labelled queue evidence. One direct, resolved tag is shown as the
|
|
361
|
+
work with its source; anything less certain lists every candidate and asks the agent."""
|
|
362
|
+
candidates = evidence.get("candidates") or []
|
|
363
|
+
unknown = evidence.get("unknown") or []
|
|
364
|
+
if (len(candidates) == 1 and not unknown and candidates[0].get("kind") == "tag"
|
|
365
|
+
and candidates[0].get("workNodePath") and not candidates[0].get("unresolved")):
|
|
366
|
+
only = candidates[0]
|
|
367
|
+
notes = [f"WORK FROM QUEUE EVIDENCE — {only.get('source')}. If this is stale, say what you are actually doing."]
|
|
368
|
+
if not only.get("slice"):
|
|
369
|
+
notes.append("no slice named")
|
|
370
|
+
traced = render_work(Path(only["workNodePath"]), root, depth, failures, packet=True)
|
|
371
|
+
head, _, rest = traced.partition("\n")
|
|
372
|
+
return "\n".join([head, *notes, rest])
|
|
373
|
+
|
|
374
|
+
missions_root = evidence.get("missionsRoot") or str(root / "missions")
|
|
375
|
+
output = ["## WORK — name it"]
|
|
376
|
+
if not candidates and not unknown:
|
|
377
|
+
output.append("No in-progress queue row names your work. If you are working from messages, "
|
|
378
|
+
"name the mission they belong to; if you have no current work, say so.")
|
|
379
|
+
elif len(candidates) > 1:
|
|
380
|
+
output.append(f"{len(candidates)} candidates are in flight. Say which mission you are actually working on.")
|
|
381
|
+
else:
|
|
382
|
+
output.append("The queue evidence does not settle your work. Confirm a candidate or name your mission.")
|
|
383
|
+
if candidates:
|
|
384
|
+
output.append("Candidates (evidence, not a verdict; none is adopted for you):")
|
|
385
|
+
output.extend(candidate_line(candidate) for candidate in candidates)
|
|
386
|
+
if unknown:
|
|
387
|
+
output.append("In-progress rows the queue cannot place:")
|
|
388
|
+
output.extend(f"- {row.get('qitemId') or 'a row'} — {row.get('summary') or 'no summary'}: {row.get('reason')}"
|
|
389
|
+
for row in unknown)
|
|
390
|
+
if not candidates:
|
|
391
|
+
names = evidence.get("missionsOnHost") or []
|
|
392
|
+
if names:
|
|
393
|
+
listing = ", ".join(names) + (", …" if evidence.get("missionsOnHostTruncated") else "")
|
|
394
|
+
output.append(f"Missions on this host: {listing}")
|
|
395
|
+
output.append("Then read that mission's current intent as text:")
|
|
396
|
+
output.append(f" python3 {Path(__file__).resolve()} --trees work --packet --work-start {missions_root}/<mission>")
|
|
397
|
+
orientation = render_work(root, root, "light", failures, packet=True)
|
|
398
|
+
_, _, rest = orientation.partition("\n")
|
|
399
|
+
output.append("\n## ORIENTATION — the project-level chain, not your work\n" + rest.lstrip("\n"))
|
|
400
|
+
return "\n".join(output)
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def read_candidates(source):
|
|
404
|
+
try:
|
|
405
|
+
raw = sys.stdin.read() if source == "-" else Path(source).read_text(encoding="utf8")
|
|
406
|
+
evidence = json.loads(raw)
|
|
407
|
+
except (OSError, json.JSONDecodeError) as error:
|
|
408
|
+
return None, f"work candidates unreadable: {error}"
|
|
409
|
+
if not isinstance(evidence, dict) or not isinstance(evidence.get("candidates", []), list):
|
|
410
|
+
return None, "work candidates have an invalid shape"
|
|
411
|
+
return evidence, None
|
|
412
|
+
|
|
413
|
+
|
|
217
414
|
def canonical_seat(session):
|
|
218
415
|
"""(member, rig) for a canonical session name, else None so the caller asks `rig whoami`.
|
|
219
416
|
|
|
@@ -279,6 +476,8 @@ def main():
|
|
|
279
476
|
parser.add_argument("--work-basis", help="the daemon's reason no current work node was named")
|
|
280
477
|
parser.add_argument("--work-unknown", help="why the current work node could not be read")
|
|
281
478
|
parser.add_argument("--check", action="store_true", help="exit nonzero on trace or notes resolution failure; rendered text is unchanged")
|
|
479
|
+
parser.add_argument("--packet", action="store_true", help="carry duties and notes as text by section, with old-notes cues")
|
|
480
|
+
parser.add_argument("--work-candidates", help="labelled work evidence as JSON (a path, or - for stdin), from `rig queue whoami --work-candidates`")
|
|
282
481
|
args = parser.parse_args()
|
|
283
482
|
|
|
284
483
|
sections = []
|
|
@@ -293,7 +492,7 @@ def main():
|
|
|
293
492
|
sections.append(gap("## TOPOLOGY TRACE\nTRACE GAP — topology.root is unresolved"))
|
|
294
493
|
else:
|
|
295
494
|
start = Path(args.topology_start) if args.topology_start else derive_topology_start(root)
|
|
296
|
-
sections.append(render_topology(start, root, args.depth, failures) if start else
|
|
495
|
+
sections.append(render_topology(start, root, args.depth, failures, packet=args.packet) if start else
|
|
297
496
|
gap("## TOPOLOGY TRACE\nTRACE GAP — current topology node is unresolved; pass --topology-start with a literal absolute path (or configure OPENRIG_REFOCUS_TOPOLOGY_NODE separately)"))
|
|
298
497
|
|
|
299
498
|
if args.trees in {"work", "both"}:
|
|
@@ -304,10 +503,19 @@ def main():
|
|
|
304
503
|
# Precedence: an explicit start wins; then the hook's daemon answer (basis or unknown);
|
|
305
504
|
# only a standalone run with neither falls back to inferring from the working directory.
|
|
306
505
|
explicit = args.work_start or os.environ.get("OPENRIG_REFOCUS_WORK_NODE")
|
|
506
|
+
evidence = None
|
|
507
|
+
if args.work_candidates:
|
|
508
|
+
evidence, error = read_candidates(args.work_candidates)
|
|
509
|
+
if error:
|
|
510
|
+
sections.append(gap(f"## WORK TRACE\nTRACE GAP — {error}"))
|
|
307
511
|
if explicit:
|
|
308
|
-
sections.append(render_work(Path(explicit), root, args.depth, failures))
|
|
512
|
+
sections.append(render_work(Path(explicit), root, args.depth, failures, packet=args.packet))
|
|
513
|
+
elif evidence is not None:
|
|
514
|
+
sections.append(render_candidates(evidence, root, args.depth, failures))
|
|
515
|
+
elif args.work_candidates:
|
|
516
|
+
pass # the unreadable-evidence gap above is the answer; never fall back to the root
|
|
309
517
|
elif args.work_basis == NO_CURRENT_BATON_BASIS:
|
|
310
|
-
sections.append(render_work(root, root, args.depth, failures, fallback=(
|
|
518
|
+
sections.append(render_work(root, root, args.depth, failures, packet=args.packet, fallback=(
|
|
311
519
|
f"no current typed baton ({args.work_basis}). Showing the project-level chain from the work root: "
|
|
312
520
|
"a broad orientation, not evidence of a current mission")))
|
|
313
521
|
elif args.work_basis:
|
|
@@ -316,8 +524,11 @@ def main():
|
|
|
316
524
|
sections.append(gap(f"## WORK TRACE\nTRACE GAP — current work node UNKNOWN: {args.work_unknown}"))
|
|
317
525
|
else:
|
|
318
526
|
start = derive_work_start(root)
|
|
319
|
-
sections.append(render_work(start, root, args.depth, failures) if start else
|
|
527
|
+
sections.append(render_work(start, root, args.depth, failures, packet=args.packet) if start else
|
|
320
528
|
gap("## WORK TRACE\nTRACE GAP — current work node is unresolved; pass --work-start with a literal absolute path (or configure OPENRIG_REFOCUS_WORK_NODE separately)"))
|
|
529
|
+
held_and_next = render_held_and_next(evidence) if evidence else ""
|
|
530
|
+
if held_and_next:
|
|
531
|
+
sections.append(held_and_next)
|
|
321
532
|
|
|
322
533
|
print("\n\n".join(sections))
|
|
323
534
|
return 1 if args.check and failures else 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent help: the single troubleshooting and escalation guide (docs/reference/help.md).
|
|
2
2
|
name: help
|
|
3
|
-
version: "0.6.
|
|
3
|
+
version: "0.6.9"
|
|
4
4
|
taxonomy: world
|
|
5
5
|
purpose: "What an agent does when its user says OpenRig isn't working: diagnose, fix within permissions, verify, and escalate to OpenRig support."
|
|
6
6
|
files:
|
|
@@ -11,7 +11,7 @@ not know exists.** So the point of what follows is not skill. It is recognition:
|
|
|
11
11
|
lands, something rhymes, and you go check instead of building.
|
|
12
12
|
|
|
13
13
|
Eighty-seven top-level verbs ship (capability canon refreshed through
|
|
14
|
-
`capability-delta-v0.6.
|
|
14
|
+
`capability-delta-v0.6.9`). This marker describes the pack's teaching, not publication or live
|
|
15
15
|
adoption. Read this once for shape, and let it make you suspicious that a thing already exists.
|
|
16
16
|
Model-divergence proclamations are live product (trust them; pins use canonical
|
|
17
17
|
model IDs).
|
|
@@ -89,6 +89,9 @@ unseen — which makes messages the one delivery channel that never gets skipped
|
|
|
89
89
|
confirm by effect, never relaunch on it. A send or wake to a seat whose latest hook is a
|
|
90
90
|
question waiting for a person is refused with `target_needs_input`, even when its screen
|
|
91
91
|
can't be read: read it with `rig capture` and get the question answered instead of retrying.
|
|
92
|
+
Over `--host` to an http-registered host, a send that may have reached the remote reads
|
|
93
|
+
`remote-outcome-unknown` ("Delivery UNCONFIRMED"): check the target with `rig capture <session>
|
|
94
|
+
--host <id>` before any resend. Only a host that was never reached reads as unreachable.
|
|
92
95
|
- **`rig send --raw`** — send exact text or keystrokes without the From/To envelope;
|
|
93
96
|
the interactive-prompt guard still applies. To deliberately drive an interactive prompt,
|
|
94
97
|
**`--dangerously-interact --reason "<why>"`** is the explicit override and implies raw text.
|
|
@@ -110,7 +113,9 @@ unseen — which makes messages the one delivery channel that never gets skipped
|
|
|
110
113
|
runtime tokens.
|
|
111
114
|
- **`rig chatroom wait`** — block until a peer actually says something, instead of polling
|
|
112
115
|
`capture` in a loop. Also `history`, `watch`, `topic` for a named thread that stays retrievable.
|
|
113
|
-
Its `--timeout` is in seconds and ignores a suffix, so `2m` means 2 seconds.
|
|
116
|
+
Its `--timeout` is in seconds and ignores a suffix, so `2m` means 2 seconds. Poll timeouts,
|
|
117
|
+
connection refusals, and reset or closed connections are retried until that deadline; other errors,
|
|
118
|
+
including an error answer from the daemon such as a removed rig, still end the wait.
|
|
114
119
|
- **`rig stream emit` / `list` / `watch` / `archive`** — drop an observation somewhere the *next*
|
|
115
120
|
agent will find it. Emitting costs nothing and does not interrupt anyone; the value appears when
|
|
116
121
|
someone lists the stream before starting.
|
|
@@ -148,7 +153,10 @@ unseen — which makes messages the one delivery channel that never gets skipped
|
|
|
148
153
|
readiness for the delivery decision and `queue create --verify` for its receipt; connector
|
|
149
154
|
setup or verification is not an outbound human message. After a connection gap the daemon
|
|
150
155
|
catches up on missed channel messages and marks them late; `rig slack status` shows the
|
|
151
|
-
connection
|
|
156
|
+
connection, the catch-up state, and the count of retained retry records for inbound messages,
|
|
157
|
+
reactions and click answers (kept across restarts, not a distinct-message count). A retained alert whose row closed before its retry isn't
|
|
158
|
+
posted; the row says why. Its `Delivery:` line says whether events are confirmed arriving;
|
|
159
|
+
"connected" alone is not delivery, and a connection that stops delivering is replaced. **`rig slack channel-map list` / `set <match> <channel>` /
|
|
152
160
|
`remove <match>`** sends a rig's or seat's human-bound posts to its own channel (a seat match
|
|
153
161
|
beats a rig match; the rest use the default channel). After a change, invite the app to each
|
|
154
162
|
channel, run `rig slack verify`, and rewire the connector (`rig slack disable` then `enable`, or
|
|
@@ -157,7 +165,10 @@ unseen — which makes messages the one delivery channel that never gets skipped
|
|
|
157
165
|
It needs the `reactions:read` scope and the `reaction_added` event (reinstall the app);
|
|
158
166
|
`rig slack verify` warns when the scope is missing. A long ask is posted in numbered thread parts; one that
|
|
159
167
|
still can't be posted comes back to the asking seat as a `slack-undeliverable` task. Replies
|
|
160
|
-
sent with “Also send to #channel” reach the seat like any other thread reply.
|
|
168
|
+
sent with “Also send to #channel” reach the seat like any other thread reply. A person's reply
|
|
169
|
+
inside a thread OpenRig opened is answered in that thread: answer with `rig queue create
|
|
170
|
+
--human-intent update --reply-to <the inbound reply's row>`; a top-level message is answered
|
|
171
|
+
top-level.
|
|
161
172
|
|
|
162
173
|
## Making work outlive you
|
|
163
174
|
|
|
@@ -223,7 +234,10 @@ relay is a reason to inspect the available routing, not to surrender judgment to
|
|
|
223
234
|
|
|
224
235
|
- **`rig workflow specs`** — what can be started here, and which are shipped versus authored by
|
|
225
236
|
this rig.
|
|
226
|
-
- **`rig workflow validate`** — will this spec instantiate at all, before a run finds out for you.
|
|
237
|
+
- **`rig workflow validate`** — will this spec instantiate at all, before a run finds out for you. A
|
|
238
|
+
step with no allowed exit that can finish it or route onward is reported as `step_cannot_finish`.
|
|
239
|
+
An allowed mapped exit is valid; in a dependency graph, `handoff` can finish a sink without a next
|
|
240
|
+
step.
|
|
227
241
|
- **`rig workflow compile <mission>`** / **`instantiate-lifecycle`** — derive one executable graph
|
|
228
242
|
from `project.yaml` → `mission.yaml` → `slice.yaml`, inspect it without writing, then start an
|
|
229
243
|
eligible graph with an opaque replay key. Typed acceptance candidate, verdict, and evidence
|
|
@@ -314,7 +328,9 @@ fresh, since a web tool's cached copy can be older. The built-in shelf also hold
|
|
|
314
328
|
`research`, `pm`, `secrets-manager` and `kernel`. Older names are gone: `adversarial-review`,
|
|
315
329
|
`research-team` and `pm-team` became `code-review`, `research` and `pm` with no alias, and
|
|
316
330
|
`first-project-claude`, `first-project-mixed`, `conveyor`, `demo`, `implementation-pair` and
|
|
317
|
-
`product-team` were removed.
|
|
331
|
+
`product-team` were removed. When authoring a new team, read
|
|
332
|
+
`docs/reference/topology-naming.md`: name the rig for its purpose, pods for domains,
|
|
333
|
+
and members for their roles. Verify prerequisites (Node 22 or 24) and actual runtime readiness
|
|
318
334
|
before work. The starter's Codex model `gpt-6-astra` needs a Codex newer than 0.145; when a Codex
|
|
319
335
|
seat reports that, the operator offers to update Codex and restart that seat. Existing Herdr/cmux
|
|
320
336
|
terminals can present the managed team through `rig terminal open`.
|
|
@@ -332,8 +348,9 @@ not have to tear one down to change it, and you rarely have to start from nothin
|
|
|
332
348
|
human mode it first prints one line with each agent's last known activity, or “unknown”: a
|
|
333
349
|
best-effort snapshot, not a record of what was interrupted. With `--host`, a remote teardown
|
|
334
350
|
that reports errors exits 2.
|
|
335
|
-
- **`rig launch <
|
|
336
|
-
rest.
|
|
351
|
+
- **`rig launch <rigId> [seat]`** — one seat is down; start just that one, without disturbing the
|
|
352
|
+
rest. It takes the rig ID from `rig ps --json`; a rig name gives `rig_not_found`. Add `--plan` to preview it first, locally or with `--host`; a plan is never sent to a
|
|
353
|
+
daemon older than 0.5.9, which would ignore it and launch.
|
|
337
354
|
- **`rig seat launch <seat> --fresh --reason <why>`** — deliberately create a blank occupant for
|
|
338
355
|
exactly one existing seat. It uses no resume, fork, rebuild, snapshot, or restore packet;
|
|
339
356
|
siblings and durable work stay put, and unmanaged ambiguity refuses.
|
|
@@ -463,7 +480,8 @@ version, from checking the environment to a useful report to the OpenRig team.
|
|
|
463
480
|
skill covers writing and reading the map; `claude-compact-in-place` no longer ships. After a
|
|
464
481
|
managed compaction, refocus waits for the restore request, which names the `refocusing` skill
|
|
465
482
|
(the daemon installs it globally); three failed refocus attempts in a row for one occupant post
|
|
466
|
-
one issue to the stream.
|
|
483
|
+
one issue to the stream. The turn boundary, restore request and read-depth audit reach a seat
|
|
484
|
+
that stays quiet after `/compact`; they don't wait for its next turn.
|
|
467
485
|
- **Know what compaction costs before you reach for it.** On some runtimes what comes back has
|
|
468
486
|
enough context left to believe it knows everything and not enough to actually know anything — and
|
|
469
487
|
**the compacted agent is the only one who knows it happened**, while every other seat keeps
|
|
@@ -509,7 +527,8 @@ scheme, and nothing downstream can see it.
|
|
|
509
527
|
- **`rig scope slice progress` / `mission progress`** — record that a step moved, in a form the
|
|
510
528
|
progress view can parse.
|
|
511
529
|
- **`rig scope slice approve`** — freeze a decision — *this is the plan* or *this is delivered* —
|
|
512
|
-
so the freeze is recorded rather than asserted in chat.
|
|
530
|
+
so the freeze is recorded rather than asserted in chat. With `--workspace`, approve stamps the
|
|
531
|
+
tree you named, and refuses one with no `missions/` folder.
|
|
513
532
|
- **`rig scope slice close` / `ship` / `move`** — retire it with the reason attached, move it into
|
|
514
533
|
the release it belongs to preserving git history, or re-file it under a different mission.
|
|
515
534
|
- **`rig scope slice stage` / `verified`** — how mature is this, and *when was it last checked and
|
|
@@ -550,8 +569,12 @@ scheme, and nothing downstream can see it.
|
|
|
550
569
|
require `--project`. World packs named in `project.yaml`'s `install.worlds`
|
|
551
570
|
are listed after the System World; read each with `rig context get <ref>`.
|
|
552
571
|
`context profile` and `context work-install` both accept `--runtime claude-code` (alias
|
|
553
|
-
`claude`) or `codex`; explicit invalid values refuse before projection. This does not rename
|
|
554
|
-
|
|
572
|
+
`claude`) or `codex`; explicit invalid values refuse before projection. This does not rename every
|
|
573
|
+
other command's runtime vocabulary. A catalog skill with uncommitted content blocks only itself:
|
|
574
|
+
it is skipped and named (`catalog_skill_skipped`, or `selected_skill_skipped` with its selector).
|
|
575
|
+
With `--runtime`, work-install reports selected skips with exit 1. It inspects by default;
|
|
576
|
+
`--apply-skills` projects the remaining clean skills while keeping any previously projected copy
|
|
577
|
+
of the skipped skill. Commit or restore that skill's folder, then apply the projection again.
|
|
555
578
|
- **`rig context show` / `sync` / `rm`** — what is inside a context pack before you prime a seat
|
|
556
579
|
with it, and how to make the library catch up when you edit one. `sync` also finds a workspace
|
|
557
580
|
pack created after the daemon started; a daemon restart is no longer needed.
|
|
@@ -665,7 +688,8 @@ reaching them is ordinary work rather than an escalation.
|
|
|
665
688
|
request, then boundedly inspect its transport receipt. Posted means posted, not read;
|
|
666
689
|
pending, failed, or indeterminate delivery leaves the row intact. Inspect the same row's
|
|
667
690
|
transitions before retrying. `rig send` remains agent-terminal delivery only.
|
|
668
|
-
`--human-questions-file` adds clickable decision questions.
|
|
691
|
+
`--human-questions-file` adds clickable decision questions. A typed Slack reply to one is kept
|
|
692
|
+
in `humanAnswers`, placed under the first unanswered question; closing isn't approval.
|
|
669
693
|
- **When project policy calls for human judgment or an update, load `messaging-the-human`.**
|
|
670
694
|
Use `rig context get skills/core/messaging-the-human/SKILL.md` for a complete decision brief,
|
|
671
695
|
related supplemental detail and explicit quiet-update intent. An FYI creates no approval
|
|
@@ -771,6 +795,9 @@ your circumstances is configuration, and the ones that are not, another agent ca
|
|
|
771
795
|
it attaches to the existing kernel terminal; Ctrl-b then d detaches, and no missing
|
|
772
796
|
seat or terminal is implicitly launched. Explain the failure and help with the
|
|
773
797
|
chosen fallback. Plain `rig tui` remains a separate dashboard.
|
|
798
|
+
On an empty command line every typed command reaches the line whole and `j`/`k` move the
|
|
799
|
+
selection; the footer toggle is `F`, and a selected Scopes view's mini-requirements and
|
|
800
|
+
narrative keys are `M` and `N`.
|
|
774
801
|
Open the instance row for one continuous cross-rig agent table with pod separators and material
|
|
775
802
|
`RECENT` transitions; drill into a rig, mission, slice, or agent without losing the owning
|
|
776
803
|
identity. Use the mission's workflow/packet view and Specs purpose/source to understand work.
|
|
@@ -105,11 +105,11 @@ OpenRig view and the handoff to the operator as one procedure. From 0.6.7, OpenR
|
|
|
105
105
|
script will do and changes nothing:
|
|
106
106
|
|
|
107
107
|
```sh
|
|
108
|
-
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.
|
|
108
|
+
curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.8/scripts/install.sh | sh -s -- --dry-run
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
The same command without `--dry-run`,
|
|
112
|
-
`curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.
|
|
112
|
+
`curl -fsSL https://raw.githubusercontent.com/mvschwarz/openrig/v0.6.8/scripts/install.sh | sh`, installs the
|
|
113
113
|
latest published `@openrig/cli` with `npm install -g`,
|
|
114
114
|
runs the Node.js and SQLite check, then `rig setup --dry-run` and `rig setup`. `rig setup` checks both Claude Code and
|
|
115
115
|
Codex and may install a missing one, as described below. A failed step prints
|
|
@@ -79,6 +79,11 @@ Missing or malformed selections fail; absence is never inferred as disablement.
|
|
|
79
79
|
`rig context work-install --json` reports the effective state, source, manifest,
|
|
80
80
|
context selectors, and skills. With `--runtime`, its managed skill loadout then
|
|
81
81
|
combines System World, topology, and Project World selectors with provenance.
|
|
82
|
+
The catalog is a Git checkout. A skill folder with uncommitted or untracked content is
|
|
83
|
+
skipped by itself and named. With `--runtime`, work-install reports selected skips with
|
|
84
|
+
exit 1. It inspects by default; `--apply-skills` projects the remaining clean skills while
|
|
85
|
+
keeping any previously projected copy of the skipped skill. An uncommitted change directly
|
|
86
|
+
in the catalog root, such as `catalog.yaml`, still makes the whole catalog unavailable.
|
|
82
87
|
|
|
83
88
|
For a pre-0.5.9 home, use the `openrig-upgrade` skill's
|
|
84
89
|
`migrate-telemetry-state-0.5.9.mjs` helper as an Agent-Operated Migration. Its
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Reference guides the help pack links to, loaded one file or section at a time by address
|
|
2
2
|
# (rig context get reference/<file>#<section>). Each file is a symlink to docs/reference/.
|
|
3
3
|
name: reference
|
|
4
|
-
version: "0.6.
|
|
4
|
+
version: "0.6.9"
|
|
5
5
|
taxonomy: world
|
|
6
6
|
purpose: "Version-matched reference guides the help guide links to. Load a single file or section by address; don't load the whole pack."
|
|
7
7
|
files:
|