mirror of
https://github.com/mvschwarz/openrig.git
synced 2026-11-07 05:56:21 -10:00
whoami: a bounded, edge-first peer list in compact --json (output.compact) (#1225)
With `output.compact` on, `rig whoami --json` lists at most `output.whoami_peers` peers (a new setting, default 8, env `OPENRIG_OUTPUT_WHOAMI_PEERS`), so the identity call every seat makes at boot stays small as a rig grows. Selection is edge-first: peers with an edge to or from the seat, then peers in the seat's pod, then the rest, each in the daemon's roster order; `edges` stays complete. When the bound cuts the list, `peersNote` says how many are shown out of how many, the order, the bound and the exact command that lists them all (with the same `--node-id` or `--session`), and an additive `peersOmitted` gives the count. A rig within the bound passes through unchanged plus `peersOmitted: 0`. Error answers print exactly as before. The new `--all-peers` prints today's compact output byte for byte and is forwarded on the SSH `--host` path; the remote's note names its own command. With the switch off (the default), every form is byte-identical to before, and `--full` and plain text are unchanged. The setting is registered like `output.compact` (CLI store, daemon twin, help, TUI label), and the `output.compact` help line now names queue writes, `queue show`, `queue transitions` and whoami peers. The startup guidance, the identity hint, two openrig-core skills, the capability map and three shipped team cultures add one conditional sentence naming `--all-peers`, true with the switch on or off. The daemon's `/api/whoami` is unchanged. Refs slice 15 P5.
This commit is contained in:
1 parent
8add46c40c
commit
904fa99bc5
18 files changed
+413
-18
No files matched your search
@@ -101,7 +101,8 @@ Keys:
|
||||
watchdog_keep_per_job, batch_size
|
||||
launch.non_interruptive accept harness first-launch warnings at full bypass for new rigs (default off)
|
||||
terminal.status_bar show the inner tmux status bar on launch (default off)
|
||||
output.compact short default output for queue writes; --full restores the whole row (default off)
|
||||
output.compact short default output for queue writes, queue show, queue transitions and whoami peers; --full restores today's output (default off)
|
||||
output.whoami_peers with output.compact on, how many peers rig whoami --json lists, edge peers first; --all-peers lists all (default 8)
|
||||
provider.usage_windows Codex and Claude usage windows in rig provider status (Codex read on request; Claude from its seats' status lines) (default off)
|
||||
|
||||
Precedence: CLI flag > environment variable > config file > default`)
|
||||
|
||||
@@ -9,6 +9,9 @@ import type { StatusDeps } from "./status.js";
|
||||
import { loadHostRegistry, resolveHost, hostDisplayTarget, resolveRemoteBearer, bearerAuthHeaders, classifyHttpFailedStep, classifyHttpError, type HttpHostEntry } from "../host-registry.js";
|
||||
import { runCrossHostCommand, type RunCrossHostCommandOpts } from "../cross-host-executor.js";
|
||||
import { emitCrossHostError, emitCrossHostFailure } from "../cross-host-cli-helpers.js";
|
||||
import { ConfigStore } from "../config-store.js";
|
||||
import { compactOutputEnabled } from "../queue-receipt.js";
|
||||
import { shellQuote } from "../cross-host-executor.js";
|
||||
|
||||
interface WhoamiCliOptions {
|
||||
nodeId?: string;
|
||||
@@ -19,6 +22,7 @@ interface WhoamiCliOptions {
|
||||
json?: boolean;
|
||||
full?: boolean;
|
||||
verbose?: boolean;
|
||||
allPeers?: boolean;
|
||||
}
|
||||
|
||||
export interface WhoamiDeps extends StatusDeps {
|
||||
@@ -75,9 +79,9 @@ interface WhoamiResult {
|
||||
* cannot silently re-bloat the every-boot path). Carries exactly the fields the
|
||||
* boot + compaction-restore recovery contract treats as ground truth.
|
||||
*/
|
||||
function projectCompactWhoami(data: Record<string, unknown>): Record<string, unknown> {
|
||||
function projectCompactWhoami(data: Record<string, unknown>, bounded?: BoundedPeers): Record<string, unknown> {
|
||||
const id = (data["identity"] ?? {}) as Record<string, unknown>;
|
||||
const peers = Array.isArray(data["peers"]) ? (data["peers"] as unknown[]) : [];
|
||||
const peers = bounded ? bounded.peers : Array.isArray(data["peers"]) ? (data["peers"] as unknown[]) : [];
|
||||
const transcript = (data["transcript"] ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
resolvedBy: data["resolvedBy"],
|
||||
@@ -96,13 +100,63 @@ function projectCompactWhoami(data: Record<string, unknown>): Record<string, unk
|
||||
return { logicalId: peer["logicalId"], sessionName: peer["sessionName"], runtime: peer["runtime"] };
|
||||
}),
|
||||
// KEEP: openrig-user SKILL.md documents peersNote as a required recovery field.
|
||||
peersNote: data["peersNote"],
|
||||
peersNote: bounded ? bounded.note : data["peersNote"],
|
||||
...(bounded ? { peersOmitted: bounded.omitted } : {}),
|
||||
// edges already carry only kind + to/from {logicalId, sessionName}.
|
||||
edges: data["edges"],
|
||||
transcript: { path: transcript["path"], tailCommand: transcript["tailCommand"] },
|
||||
};
|
||||
}
|
||||
|
||||
export interface BoundedPeers {
|
||||
peers: unknown[];
|
||||
omitted: number;
|
||||
/** The daemon's own note when every peer fits; otherwise what was left out and how to list it. */
|
||||
note: unknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Slice 15 (OPR.0.7.0.15) P5 — with `output.compact` on, compact `--json` lists at most `bound` peers so the
|
||||
* every-boot call stays small as a rig grows. Edge peers (either direction) come first, then peers in the
|
||||
* seat's own pod, then the rest, each in the daemon's roster order. Nothing is silently missing: the note
|
||||
* says how many were left out and the exact command that lists them all, and `edges` stays complete. When
|
||||
* every peer fits, the roster and the daemon's note pass through unchanged, so a small rig pays only for
|
||||
* `peersOmitted: 0`.
|
||||
*/
|
||||
export function boundPeers(data: Record<string, unknown>, bound: number, allPeersCommand: string): BoundedPeers {
|
||||
const peers = Array.isArray(data["peers"]) ? (data["peers"] as Array<Record<string, unknown> | null>) : [];
|
||||
if (peers.length <= bound) return { peers, omitted: 0, note: data["peersNote"] };
|
||||
const id = (data["identity"] ?? {}) as Record<string, unknown>;
|
||||
const edges = (data["edges"] ?? {}) as { outgoing?: WhoamiEdge[]; incoming?: WhoamiEdge[] };
|
||||
const linked = new Set<unknown>([
|
||||
...(Array.isArray(edges.outgoing) ? edges.outgoing : []).map((edge) => edge?.to?.logicalId),
|
||||
...(Array.isArray(edges.incoming) ? edges.incoming : []).map((edge) => edge?.from?.logicalId),
|
||||
]);
|
||||
const ownPod = id["podId"] ?? null;
|
||||
const tier = (peer: Record<string, unknown> | null): number => {
|
||||
if (linked.has(peer?.["logicalId"])) return 0;
|
||||
if (ownPod !== null && peer?.["podId"] === ownPod) return 1;
|
||||
return 2;
|
||||
};
|
||||
const ordered = [0, 1, 2].flatMap((t) => peers.filter((peer) => tier(peer) === t));
|
||||
const shown = ordered.slice(0, bound);
|
||||
const omitted = peers.length - shown.length;
|
||||
const note = `peers = ${shown.length} of this rig's ${peers.length} (roster excluding self; edge peers first, then your pod, then roster order; `
|
||||
+ `bound output.whoami_peers=${bound}); ${omitted} not shown; all: ${allPeersCommand}; `
|
||||
+ "edges = directional relationships (always complete); use `rig ps --nodes` for node inventory including self + live state";
|
||||
return { peers: shown, omitted, note };
|
||||
}
|
||||
|
||||
/** The bound from `output.whoami_peers`, by the config store's own resolution; the default if unreadable. */
|
||||
function whoamiPeersBound(): number {
|
||||
try {
|
||||
const value = new ConfigStore().get("output.whoami_peers");
|
||||
return typeof value === "number" && Number.isInteger(value) && value >= 1 ? value : 8;
|
||||
} catch {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
type TmuxExecFn = (cmd: string) => string;
|
||||
|
||||
const defaultTmuxExec: TmuxExecFn = (cmd: string) => execSync(cmd, { encoding: "utf-8" }).trim();
|
||||
@@ -228,13 +282,18 @@ export function whoamiCommand(depsOverride?: WhoamiDeps): Command {
|
||||
.option("--json", "JSON output for agents (compact identity-recovery projection by default)")
|
||||
.option("--full", "Show the complete whoami payload (contextUsage, commands, runtimeContext, workspace, all sub-fields)")
|
||||
.option("--verbose", "Alias for --full")
|
||||
.option("--all-peers", "With output.compact on, list every peer in --json (today's compact output)")
|
||||
.addHelpText("after", `
|
||||
By default rig whoami is COMPACT: identity (rig/pod/member/session/runtime),
|
||||
peers (with sessionName for 'rig send'), edges, and the transcript path — the
|
||||
boot + compaction-restore recovery essentials. Use --full / --verbose for the
|
||||
complete payload (contextUsage, command examples, runtime token detail,
|
||||
workspace block). The compact form omits the Context line; use 'rig context' or
|
||||
'rig whoami --full' for usage.`)
|
||||
'rig whoami --full' for usage.
|
||||
|
||||
With output.compact on, --json lists at most output.whoami_peers peers (default
|
||||
8), edge peers first, then your pod; peersNote and peersOmitted say how many were
|
||||
left out. --all-peers lists every one (today's compact output).`)
|
||||
.action(async (opts: WhoamiCliOptions) => {
|
||||
// OPR.0.4.6.MH1 FR-2: selected-host routing — explicit --host wins;
|
||||
// else the persisted selection feeds the SHIPPED --host path; no
|
||||
@@ -292,8 +351,12 @@ workspace block). The compact form omits the Context line; use 'rig context' or
|
||||
// Preserve the complete payload and original error; the routing hint is additive.
|
||||
console.log(JSON.stringify(hint ? { ...res.data, hint } : res.data, null, 2));
|
||||
} else {
|
||||
// Compact default: the identity-recovery ALLOWLIST projection.
|
||||
console.log(JSON.stringify(projectCompactWhoami(res.data)));
|
||||
// Compact default: the identity-recovery ALLOWLIST projection; with output.compact on,
|
||||
// a bounded peer list unless --all-peers asks for every one.
|
||||
const bounded = !opts.allPeers && compactOutputEnabled()
|
||||
? boundPeers(res.data, whoamiPeersBound(), allPeersCommand(opts))
|
||||
: undefined;
|
||||
console.log(JSON.stringify(projectCompactWhoami(res.data, bounded)));
|
||||
}
|
||||
if (res.status >= 400) process.exitCode = 1;
|
||||
return;
|
||||
@@ -421,6 +484,7 @@ async function runCrossHostWhoami(
|
||||
if (opts.nodeId !== undefined) argv.push("--node-id", opts.nodeId);
|
||||
if (opts.session !== undefined) argv.push("--session", opts.session);
|
||||
if (opts.json) argv.push("--json");
|
||||
if (opts.allPeers) argv.push("--all-peers");
|
||||
|
||||
const result = await runner(host, argv);
|
||||
|
||||
@@ -593,3 +657,11 @@ async function runFanOutWhoami(opts: WhoamiCliOptions, deps: WhoamiDeps): Promis
|
||||
|
||||
if (hasFailure) process.exitCode = 3;
|
||||
}
|
||||
|
||||
/** The exact command that lists every peer, for the same identity this call resolved. */
|
||||
function allPeersCommand(opts: WhoamiCliOptions): string {
|
||||
const parts = ["rig", "whoami", "--json", "--all-peers"];
|
||||
if (opts.nodeId) parts.push("--node-id", shellQuote(opts.nodeId));
|
||||
else if (opts.session) parts.push("--session", shellQuote(opts.session));
|
||||
return parts.join(" ");
|
||||
}
|
||||
@@ -168,6 +168,8 @@ export interface RiggedConfig {
|
||||
// settings-store twin keeps the two registries in lockstep.
|
||||
output: {
|
||||
compact: boolean;
|
||||
/** With compact on, how many peers `rig whoami --json` lists (edge peers first). */
|
||||
whoamiPeers: number;
|
||||
};
|
||||
// Slice 01 (OPR.0.7.0.1) — Codex and Claude usage windows in the provider view (default
|
||||
// off). Read by the daemon; the CLI key is the lockstep twin.
|
||||
@@ -326,6 +328,7 @@ const DEFAULTS = {
|
||||
// Slice 15 — compact command-output defaults OFF until the switch is on.
|
||||
output: {
|
||||
compact: false,
|
||||
whoamiPeers: 8,
|
||||
},
|
||||
// Slice 01 — Codex usage windows OFF until the switch is on.
|
||||
provider: {
|
||||
@@ -446,6 +449,7 @@ export const VALID_KEYS = [
|
||||
"terminal.status_bar",
|
||||
// Slice 15 — compact command-output defaults; lockstep with the daemon twin.
|
||||
"output.compact",
|
||||
"output.whoami_peers",
|
||||
// Slice 01 — Codex and Claude usage windows in the provider view; lockstep with the daemon twin.
|
||||
"provider.usage_windows",
|
||||
// OPR.0.4.6.FS-1 W2 — queue-retention maintenance knobs; CLI-settable twin,
|
||||
@@ -545,6 +549,7 @@ export const ENV_MAP: Record<ValidKey, { primary: string; legacy?: string }> = {
|
||||
// OPR.0.4.6.02 S1 — net-new key; OPENRIG_* primary only (no RIGGED_* legacy).
|
||||
"terminal.status_bar": { primary: "OPENRIG_TERMINAL_STATUS_BAR" },
|
||||
"output.compact": { primary: "OPENRIG_OUTPUT_COMPACT" },
|
||||
"output.whoami_peers": { primary: "OPENRIG_OUTPUT_WHOAMI_PEERS" },
|
||||
"provider.usage_windows": { primary: "OPENRIG_PROVIDER_USAGE_WINDOWS" },
|
||||
// OPR.0.4.6.FS-1 W2 — retention knobs; net-new keys, OPENRIG_* primary only.
|
||||
"retention.enabled": { primary: "OPENRIG_RETENTION_ENABLED" },
|
||||
@@ -626,6 +631,7 @@ const KEY_TO_PATH: Record<ValidKey, string[]> = {
|
||||
"snapshots.periodic.retention_keep": ["snapshots", "periodic", "retentionKeep"],
|
||||
"terminal.status_bar": ["terminal", "statusBar"],
|
||||
"output.compact": ["output", "compact"],
|
||||
"output.whoami_peers": ["output", "whoamiPeers"],
|
||||
"provider.usage_windows": ["provider", "usageWindows"],
|
||||
"retention.enabled": ["retention", "enabled"],
|
||||
"retention.transitions_days": ["retention", "transitionsDays"],
|
||||
@@ -922,6 +928,12 @@ const KEY_CONSTRAINTS: Partial<Record<ValidKey, (raw: string, coerced: string |
|
||||
// OPR.0.4.6.FS-1 W2 — retention numeric bounds. Lockstep with the daemon
|
||||
// settings-store KEY_CONSTRAINTS twin (same messages/bounds). retention.enabled
|
||||
// is a boolean (coerceValue enforces true/false) — no constraint entry.
|
||||
// Slice 15 P5 — the whoami peer bound; lockstep with the daemon twin.
|
||||
"output.whoami_peers": (raw, coerced) => {
|
||||
if (!/^-?\d+$/.test((raw ?? "").trim()) || typeof coerced !== "number" || !Number.isInteger(coerced) || coerced < 1) {
|
||||
throw new Error(`Invalid value for output.whoami_peers: must be an integer >= 1, got "${raw}"`);
|
||||
}
|
||||
},
|
||||
"retention.transitions_days": (raw, coerced) => {
|
||||
if (!/^-?\d+$/.test((raw ?? "").trim()) || typeof coerced !== "number" || !Number.isInteger(coerced) || coerced < 1) {
|
||||
throw new Error(`Invalid value for retention.transitions_days: must be an integer >= 1, got "${raw}"`);
|
||||
@@ -1158,6 +1170,7 @@ export class ConfigStore {
|
||||
},
|
||||
output: {
|
||||
compact: v("output.compact") as boolean,
|
||||
whoamiPeers: v("output.whoami_peers") as number,
|
||||
},
|
||||
provider: {
|
||||
usageWindows: v("provider.usage_windows") as boolean,
|
||||
|
||||
@@ -173,6 +173,8 @@ describe("ConfigStore — extended namespaces (User Settings v0)", () => {
|
||||
"terminal.status_bar",
|
||||
// Slice 15 (OPR.0.7.0.15) — compact command-output defaults switch.
|
||||
"output.compact",
|
||||
// Slice 15 P5 — the whoami peer bound under output.compact.
|
||||
"output.whoami_peers",
|
||||
// Slice 01 (OPR.0.7.0.1) — Codex usage windows switch.
|
||||
"provider.usage_windows",
|
||||
// OPR.0.4.6.FS-1 W2 — queue-retention maintenance knobs.
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
|
||||
import http from "node:http";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import { ConfigStore } from "../src/config-store.js";
|
||||
import { Command } from "commander";
|
||||
import { whoamiCommand, boundPeers } from "../src/commands/whoami.js";
|
||||
import { DaemonClient } from "../src/client.js";
|
||||
import { STATE_FILE, type LifecycleDeps, type DaemonState } from "../src/daemon-lifecycle.js";
|
||||
import type { StatusDeps } from "../src/commands/status.js";
|
||||
|
||||
/**
|
||||
* Slice 15 (OPR.0.7.0.15) P5 — `rig whoami --json` lists a bounded, edge-first set of peers when
|
||||
* `output.compact` is on. Switch off (the default) and `--all-peers` print today's compact output.
|
||||
*/
|
||||
|
||||
function lifecycle(port: number): LifecycleDeps {
|
||||
return {
|
||||
spawn: vi.fn(() => ({ pid: 1, unref: vi.fn() }) as never), fetch: vi.fn(async () => ({ ok: true })), kill: vi.fn(() => true),
|
||||
readFile: vi.fn((p: string) => (p === STATE_FILE
|
||||
? JSON.stringify({ pid: 123, port, db: "test.sqlite", startedAt: "2026-04-03T00:00:00Z" } as DaemonState) : null)),
|
||||
writeFile: vi.fn(), removeFile: vi.fn(), exists: vi.fn((p: string) => p === STATE_FILE), mkdirp: vi.fn(),
|
||||
openForAppend: vi.fn(() => 3), isProcessAlive: vi.fn(() => true),
|
||||
};
|
||||
}
|
||||
|
||||
const DAEMON_NOTE = "peers = this rig's roster excluding self (no edge filter); edges = directional relationships; use `rig ps --nodes` for node inventory including self + live state";
|
||||
|
||||
const peer = (logicalId: string, podId: string) => ({
|
||||
logicalId, sessionName: `${logicalId.replace(".", "-")}@big`, runtime: "claude-code", podId, podNamespace: podId, memberId: logicalId.split(".")[1],
|
||||
});
|
||||
|
||||
/** A 23-peer roster in daemon (created_at) order. The edge peers sit late, so edge-first ordering is visible. */
|
||||
const BIG_PEERS = [
|
||||
...Array.from({ length: 10 }, (_, i) => peer(`ops.o${i}`, "ops")),
|
||||
peer("dev.a", "dev"), peer("dev.b", "dev"),
|
||||
...Array.from({ length: 9 }, (_, i) => peer(`misc.m${i}`, "misc")),
|
||||
peer("review.r1", "review"), peer("orch.lead", "orch"),
|
||||
];
|
||||
|
||||
function response(peers: ReturnType<typeof peer>[], edges = {
|
||||
outgoing: [{ kind: "delegates_to", to: { logicalId: "orch.lead", sessionName: "orch-lead@big" } }],
|
||||
incoming: [{ kind: "can_observe", from: { logicalId: "review.r1", sessionName: "review-r1@big" } }],
|
||||
}) {
|
||||
return {
|
||||
resolvedBy: "node_id",
|
||||
identity: {
|
||||
rigId: "rig-1", rigName: "big", nodeId: "node-1", logicalId: "dev.impl", attachmentType: "tmux", podId: "dev",
|
||||
podNamespace: "dev", podLabel: null, memberId: "impl", memberLabel: null, sessionName: "dev-impl@big", runtime: "claude-code",
|
||||
cwd: "/tmp", agentRef: null, profile: null, resolvedSpecName: null, resolvedSpecVersion: null,
|
||||
},
|
||||
peers,
|
||||
peersNote: DAEMON_NOTE,
|
||||
edges,
|
||||
transcript: { enabled: true, path: "/tmp/t.log", tailCommand: "rig transcript dev-impl@big --tail 100", grepCommand: null },
|
||||
commands: { sendExamples: [], captureExamples: [] },
|
||||
};
|
||||
}
|
||||
|
||||
const SMALL_PEERS = [peer("dev.a", "dev"), peer("orch.lead", "orch"), peer("review.r1", "review")];
|
||||
|
||||
/** Main's compact projection (`projectCompactWhoami` at 7510731d), restated so "unchanged" is checked against a fixed shape. */
|
||||
function todaysCompact(data: ReturnType<typeof response>): string {
|
||||
return JSON.stringify({
|
||||
resolvedBy: data.resolvedBy,
|
||||
identity: {
|
||||
rigName: data.identity.rigName, nodeId: data.identity.nodeId, logicalId: data.identity.logicalId, podId: data.identity.podId,
|
||||
podNamespace: data.identity.podNamespace, memberId: data.identity.memberId, sessionName: data.identity.sessionName,
|
||||
runtime: data.identity.runtime,
|
||||
},
|
||||
peers: data.peers.map((p) => ({ logicalId: p.logicalId, sessionName: p.sessionName, runtime: p.runtime })),
|
||||
peersNote: data.peersNote,
|
||||
edges: data.edges,
|
||||
transcript: { path: data.transcript.path, tailCommand: data.transcript.tailCommand },
|
||||
});
|
||||
}
|
||||
|
||||
describe("rig whoami --json: bounded peers (output.compact, output.whoami_peers)", () => {
|
||||
let server: http.Server;
|
||||
let port: number;
|
||||
let served: ReturnType<typeof response> | Record<string, unknown>;
|
||||
let status = 200;
|
||||
|
||||
beforeAll(async () => {
|
||||
server = http.createServer((req, res) => {
|
||||
if ((req.url ?? "").includes("/api/whoami")) {
|
||||
res.writeHead(status, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify(served));
|
||||
} else {
|
||||
res.writeHead(404, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: "not found" }));
|
||||
}
|
||||
});
|
||||
await new Promise<void>((resolve) => { server.listen(0, resolve); });
|
||||
port = (server.address() as { port: number }).port;
|
||||
});
|
||||
afterAll(() => { server.close(); });
|
||||
|
||||
beforeEach(() => {
|
||||
served = response(BIG_PEERS);
|
||||
status = 200;
|
||||
vi.stubEnv("OPENRIG_NODE_ID", "");
|
||||
vi.stubEnv("OPENRIG_SESSION_NAME", "");
|
||||
vi.stubEnv("TMUX_PANE", "");
|
||||
});
|
||||
afterEach(() => { vi.unstubAllEnvs(); });
|
||||
|
||||
async function run(...args: string[]): Promise<string> {
|
||||
const deps: StatusDeps = { lifecycleDeps: lifecycle(port), clientFactory: (url) => new DaemonClient(url) };
|
||||
const prog = new Command();
|
||||
prog.exitOverride();
|
||||
prog.addCommand(whoamiCommand(deps));
|
||||
const logs: string[] = [];
|
||||
const spy = vi.spyOn(console, "log").mockImplementation((...a: unknown[]) => { logs.push(a.join(" ")); });
|
||||
try {
|
||||
await prog.parseAsync(["node", "rig", "whoami", "--node-id", "node-1", ...args]);
|
||||
} finally {
|
||||
spy.mockRestore();
|
||||
}
|
||||
return logs.join("\n");
|
||||
}
|
||||
|
||||
it("switch off (default): today's compact output byte for byte, on a large and a small rig", async () => {
|
||||
expect(await run("--json")).toBe(todaysCompact(served as ReturnType<typeof response>));
|
||||
served = response(SMALL_PEERS);
|
||||
expect(await run("--json")).toBe(todaysCompact(served as ReturnType<typeof response>));
|
||||
});
|
||||
|
||||
it("switch off: --all-peers changes nothing", async () => {
|
||||
expect(await run("--json", "--all-peers")).toBe(todaysCompact(served as ReturnType<typeof response>));
|
||||
});
|
||||
|
||||
it("switch on, large rig: 8 peers, edge peers (both directions) first, then the seat's pod, then roster order", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
const out = JSON.parse(await run("--json"));
|
||||
expect(out.peers.map((p: { logicalId: string }) => p.logicalId)).toEqual([
|
||||
"review.r1", "orch.lead", "dev.a", "dev.b", "ops.o0", "ops.o1", "ops.o2", "ops.o3",
|
||||
]);
|
||||
expect(out.peers[0]).toEqual({ logicalId: "review.r1", sessionName: "review-r1@big", runtime: "claude-code" });
|
||||
expect(out.peersOmitted).toBe(15);
|
||||
expect(out.peersNote).toBe(
|
||||
"peers = 8 of this rig's 23 (roster excluding self; edge peers first, then your pod, then roster order; "
|
||||
+ "bound output.whoami_peers=8); 15 not shown; all: rig whoami --json --all-peers --node-id 'node-1'; "
|
||||
+ "edges = directional relationships (always complete); use `rig ps --nodes` for node inventory including self + live state",
|
||||
);
|
||||
// Identity, edges and transcript stay at today's paths, complete.
|
||||
const today = JSON.parse(todaysCompact(served as ReturnType<typeof response>));
|
||||
expect(out.identity).toEqual(today.identity);
|
||||
expect(out.edges).toEqual(today.edges);
|
||||
expect(out.transcript).toEqual(today.transcript);
|
||||
expect(Object.keys(out)).toEqual(["resolvedBy", "identity", "peers", "peersNote", "peersOmitted", "edges", "transcript"]);
|
||||
});
|
||||
|
||||
it("switch on, small rig: today's peers and note verbatim, plus only peersOmitted: 0", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
served = response(SMALL_PEERS);
|
||||
const out = await run("--json");
|
||||
const today = JSON.parse(todaysCompact(served as ReturnType<typeof response>));
|
||||
expect(JSON.parse(out)).toEqual({ ...today, peersOmitted: 0 });
|
||||
expect(Object.keys(JSON.parse(out))).toEqual(["resolvedBy", "identity", "peers", "peersNote", "peersOmitted", "edges", "transcript"]);
|
||||
expect(Buffer.byteLength(out) - Buffer.byteLength(todaysCompact(served as ReturnType<typeof response>))).toBe('"peersOmitted":0,'.length);
|
||||
});
|
||||
|
||||
it("a rig of exactly the bound is shown whole, unchanged", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
served = response(BIG_PEERS.slice(0, 8));
|
||||
const out = JSON.parse(await run("--json"));
|
||||
expect(out.peers).toEqual(JSON.parse(todaysCompact(served as ReturnType<typeof response>)).peers);
|
||||
expect(out.peersNote).toBe(DAEMON_NOTE);
|
||||
expect(out.peersOmitted).toBe(0);
|
||||
});
|
||||
|
||||
it("switch on: an error answer prints exactly as main does (no bounding, no rewritten note, no peersOmitted)", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
for (const code of [404, 409, 500]) {
|
||||
status = code;
|
||||
served = { error: "Session not found", peers: BIG_PEERS, peersNote: DAEMON_NOTE };
|
||||
const out = await run("--json");
|
||||
// Main's error path: the whole answer, pretty-printed; a 404 also gets its additive routing hint.
|
||||
if (code === 404) expect(JSON.parse(out)).toEqual({ ...served, hint: expect.any(String) });
|
||||
else expect(out).toBe(JSON.stringify(served, null, 2));
|
||||
expect(out).not.toContain("peersOmitted");
|
||||
}
|
||||
});
|
||||
|
||||
it("the setting changes the bound", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
vi.stubEnv("OPENRIG_OUTPUT_WHOAMI_PEERS", "3");
|
||||
let out = JSON.parse(await run("--json"));
|
||||
expect(out.peers.map((p: { logicalId: string }) => p.logicalId)).toEqual(["review.r1", "orch.lead", "dev.a"]);
|
||||
expect(out.peersOmitted).toBe(20);
|
||||
expect(out.peersNote).toContain("bound output.whoami_peers=3); 20 not shown");
|
||||
vi.stubEnv("OPENRIG_OUTPUT_WHOAMI_PEERS", "30");
|
||||
out = JSON.parse(await run("--json"));
|
||||
expect(out.peers).toEqual(JSON.parse(todaysCompact(served as ReturnType<typeof response>)).peers);
|
||||
expect(out.peersNote).toBe(DAEMON_NOTE);
|
||||
expect(out.peersOmitted).toBe(0);
|
||||
});
|
||||
|
||||
it("switch on: --all-peers prints today's compact output byte for byte", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
expect(await run("--json", "--all-peers")).toBe(todaysCompact(served as ReturnType<typeof response>));
|
||||
});
|
||||
|
||||
it("switch on: --full keeps today's whole payload", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
expect(await run("--json", "--full")).toBe(JSON.stringify(served, null, 2));
|
||||
});
|
||||
|
||||
it("switch on: plain text still lists every peer (out of P5)", async () => {
|
||||
vi.stubEnv("OPENRIG_OUTPUT_COMPACT", "1");
|
||||
const out = await run();
|
||||
for (const p of BIG_PEERS) expect(out).toContain(p.logicalId);
|
||||
});
|
||||
|
||||
it("the full-list command names the identity the call used: --session, quoted", () => {
|
||||
const bounded = boundPeers(response(BIG_PEERS), 2, "rig whoami --json --all-peers --session 'dev-impl@big'");
|
||||
expect(bounded.note).toContain("all: rig whoami --json --all-peers --session 'dev-impl@big';");
|
||||
});
|
||||
|
||||
it("edge peers past the bound are cut like any other, and edges still name them", () => {
|
||||
const bounded = boundPeers(response(BIG_PEERS), 1, "rig whoami --json --all-peers");
|
||||
expect(bounded.peers.map((p) => (p as { logicalId: string }).logicalId)).toEqual(["review.r1"]);
|
||||
expect(bounded.omitted).toBe(22);
|
||||
});
|
||||
|
||||
it("is deterministic and keeps roster order inside each tier", () => {
|
||||
const a = boundPeers(response(BIG_PEERS), 8, "x");
|
||||
const b = boundPeers(response(BIG_PEERS), 8, "x");
|
||||
expect(a).toEqual(b);
|
||||
const noEdges = boundPeers(response(BIG_PEERS, { outgoing: [], incoming: [] }), 4, "x");
|
||||
expect(noEdges.peers.map((p) => (p as { logicalId: string }).logicalId)).toEqual(["dev.a", "dev.b", "ops.o0", "ops.o1"]);
|
||||
});
|
||||
|
||||
it("the bound set in the config file applies, as `rig config get` reports it", async () => {
|
||||
// Its own scratch home, so the test never depends on the suite's (or the caller's) OPENRIG_HOME.
|
||||
const home = fs.mkdtempSync(path.join(os.tmpdir(), "whoami-peers-home-"));
|
||||
vi.stubEnv("OPENRIG_HOME", home);
|
||||
try {
|
||||
expect(new ConfigStore().configPath).toBe(path.join(home, "config.json"));
|
||||
new ConfigStore().set("output.compact", "true");
|
||||
new ConfigStore().set("output.whoami_peers", "5");
|
||||
expect(new ConfigStore().resolveWithSource("output.whoami_peers")).toMatchObject({ value: 5, source: "file" });
|
||||
const out = JSON.parse(await run("--json"));
|
||||
expect(out.peers).toHaveLength(5);
|
||||
expect(out.peersOmitted).toBe(18);
|
||||
} finally {
|
||||
// OPENRIG_HOME is restored by afterEach's vi.unstubAllEnvs(); remove only this test's own home.
|
||||
fs.rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("output.whoami_peers: registered like output.compact", () => {
|
||||
let home: string;
|
||||
beforeEach(() => { vi.unstubAllEnvs(); home = fs.mkdtempSync(path.join(os.tmpdir(), "whoami-peers-config-")); });
|
||||
afterEach(() => { vi.restoreAllMocks(); vi.unstubAllEnvs(); fs.rmSync(home, { recursive: true, force: true }); });
|
||||
|
||||
it("defaults to 8 and accepts only an integer >= 1", () => {
|
||||
const store = new ConfigStore(path.join(home, "config.json"));
|
||||
expect(store.resolveWithSource("output.whoami_peers")).toMatchObject({ value: 8, source: "default" });
|
||||
store.set("output.whoami_peers", "12");
|
||||
expect(store.get("output.whoami_peers")).toBe(12);
|
||||
for (const raw of ["0", "-3", "2.5", "8junk", "many"]) {
|
||||
expect(() => store.set("output.whoami_peers", raw)).toThrow(/output\.whoami_peers/);
|
||||
}
|
||||
});
|
||||
|
||||
it("an invalid OPENRIG_OUTPUT_WHOAMI_PEERS is rejected with the store's warning and falls back", () => {
|
||||
const stderr: string[] = [];
|
||||
vi.spyOn(process.stderr, "write").mockImplementation(((s: string) => { stderr.push(String(s)); return true; }) as typeof process.stderr.write);
|
||||
vi.stubEnv("OPENRIG_OUTPUT_WHOAMI_PEERS", "0");
|
||||
expect(new ConfigStore(path.join(home, "config.json")).get("output.whoami_peers")).toBe(8);
|
||||
expect(stderr.join("")).toContain("output.whoami_peers");
|
||||
});
|
||||
});
|
||||
@@ -12,7 +12,8 @@ orientation, and it cannot tell you what your rig is for or what you are suppose
|
||||
rig whoami --json
|
||||
```
|
||||
|
||||
Returns your rig, pod, member, peers, edges, and transcript path. **Treat it as ground truth.**
|
||||
Returns your rig, pod, member, peers, edges, and transcript path. If `peersNote` says some peers
|
||||
are not shown, `rig whoami --json --all-peers` lists them all. **Treat it as ground truth.**
|
||||
A startup overlay can be stale; this one is small precisely so it has less room to be wrong.
|
||||
|
||||
Run it again after any compaction, restart or restore — **before** concluding anything about
|
||||
|
||||
+4
-2
@@ -117,7 +117,9 @@ read only the pieces a task needs.
|
||||
|
||||
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.
|
||||
pod, member, peers, edges, transcript path. On a large rig the peer list can be
|
||||
a bounded, edge-first selection: `peersNote` then says how many are not shown,
|
||||
and `rig whoami --json --all-peers` lists them all.
|
||||
|
||||
---
|
||||
|
||||
@@ -232,7 +234,7 @@ OpenRig has a few load-bearing cultural principles. Internalize these:
|
||||
If you're booting into a new seat in an OpenRig rig:
|
||||
|
||||
1. **`rig whoami --json`** — recover identity. Know your rig, pod, member,
|
||||
peers.
|
||||
peers (`--all-peers` when `peersNote` says some are not shown).
|
||||
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
|
||||
|
||||
@@ -500,11 +500,12 @@ rig ps --active # opt-in active-state filter (does NOT change the al
|
||||
```bash
|
||||
rig whoami # compact: identity + peers names + edges + transcript path
|
||||
rig whoami --json # compact JSON
|
||||
rig whoami --json --all-peers # compact JSON with every peer, when output.compact bounds the list
|
||||
rig whoami --full # complete payload (v0.3.4 default shape)
|
||||
rig whoami --verbose # alias of --full
|
||||
```
|
||||
|
||||
The first command every agent runs on boot AND every compaction-restore. The compact default keeps identity-recovery essentials (`identity`, `peers` names + sessionNames, `edges` directional `kind` + `to.sessionName`, `transcriptPath`). `--full` adds `contextUsage`, `commands`, `peersNote`, `runtimeContext`. The compact-default is an ALLOWLIST projection — future payload fields default to `--full` and cannot silently re-bloat the every-boot path.
|
||||
The first command every agent runs on boot AND every compaction-restore. The compact default keeps identity-recovery essentials (`identity`, `peers` names + sessionNames, `peersNote`, `edges` directional `kind` + `to.sessionName`, the transcript path). `--full` adds `contextUsage`, `commands`, `runtimeContext` and `workspace`. With `output.compact` on, compact JSON lists at most `output.whoami_peers` peers (default 8): edge peers first, then your pod, then roster order. `peersNote` and `peersOmitted` then say how many were left out, and `--all-peers` lists every one; `edges` stays complete. The compact-default is an ALLOWLIST projection — future payload fields default to `--full` and cannot silently re-bloat the every-boot path.
|
||||
|
||||
### `rig queue list` — active-frontier + docker/kubectl grammar
|
||||
|
||||
|
||||
@@ -24,7 +24,8 @@ model IDs).
|
||||
one command away. Your memory of the fleet is a claim about the past.
|
||||
|
||||
- **`rig whoami`** — who you are, who your peers are, and the exact string you use to reach each of
|
||||
them. Ground truth over anything your startup overlay says.
|
||||
them. Ground truth over anything your startup overlay says. On a large rig the JSON peer list can be
|
||||
bounded; `peersNote` says how many it left out, and `--all-peers` lists them all.
|
||||
- **`rig ps`** — what rigs exist. **`rig ps --nodes -A`** — the state of every seat everywhere,
|
||||
*including the broken ones*. **The bare form is the question you usually want:** `--active`
|
||||
filters to running and **hides detached, exited and attention states** — which are exactly the
|
||||
|
||||
@@ -40,7 +40,7 @@ Do not stall silently. Do not pretend you have permissions you don't.
|
||||
|
||||
## After startup
|
||||
|
||||
Every agent should run `rig whoami --json` immediately after launch or compaction to recover identity, peers, and edges.
|
||||
Every agent should run `rig whoami --json` immediately after launch or compaction to recover identity, peers, and edges. If `peersNote` says some peers are not shown, `rig whoami --json --all-peers` lists them all.
|
||||
|
||||
## Culture
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ If blocked on another agent:
|
||||
|
||||
## After startup or compaction
|
||||
|
||||
Run `rig whoami --json` immediately. This tells you who you are, who your peers are, and how to reach them.
|
||||
Run `rig whoami --json` immediately. This tells you who you are, who your peers are, and how to reach them. If `peersNote` says some peers are not shown, `rig whoami --json --all-peers` lists them all.
|
||||
|
||||
## Culture
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ If blocked on another agent:
|
||||
|
||||
## After startup or compaction
|
||||
|
||||
Run `rig whoami --json` immediately. This tells you who you are, who your peers are, and how to reach them.
|
||||
Run `rig whoami --json` immediately. This tells you who you are, who your peers are, and how to reach them. If `peersNote` says some peers are not shown, `rig whoami --json --all-peers` lists them all.
|
||||
|
||||
## Culture
|
||||
|
||||
|
||||
@@ -2644,6 +2644,7 @@ export class PodRigInstantiator {
|
||||
"This is your startup identity hint. For durable identity recovery after compaction, run:",
|
||||
" rig whoami --json",
|
||||
"That command returns your full topology context: rig, pod, peers, edges, and transcript path.",
|
||||
"If its peersNote says some peers are not shown, rig whoami --json --all-peers lists them all.",
|
||||
].filter((line): line is string => Boolean(line));
|
||||
|
||||
return {
|
||||
|
||||
@@ -184,6 +184,7 @@ export const SETTINGS_VALID_KEYS = [
|
||||
"terminal.status_bar",
|
||||
// Slice 15 (OPR.0.7.0.15) — compact command-output defaults; CLI-read, twin of the CLI key.
|
||||
"output.compact",
|
||||
"output.whoami_peers",
|
||||
// Slice 01 (OPR.0.7.0.1) — Codex and Claude usage windows in the provider view; daemon-read, twin of the CLI key.
|
||||
"provider.usage_windows",
|
||||
// OPR.0.4.6.FS-1 W2 — queue-retention maintenance knobs (arch D3; closed-set,
|
||||
@@ -290,6 +291,7 @@ const ENV_MAP: Record<SettingsValidKey, { primary: string; legacy?: string }> =
|
||||
// OPR.0.4.6.02 S1 — net-new key; OPENRIG_* primary only (no RIGGED_* legacy).
|
||||
"terminal.status_bar": { primary: "OPENRIG_TERMINAL_STATUS_BAR" },
|
||||
"output.compact": { primary: "OPENRIG_OUTPUT_COMPACT" },
|
||||
"output.whoami_peers": { primary: "OPENRIG_OUTPUT_WHOAMI_PEERS" },
|
||||
"provider.usage_windows": { primary: "OPENRIG_PROVIDER_USAGE_WINDOWS" },
|
||||
// OPR.0.4.6.FS-1 W2 — retention knobs; net-new keys, OPENRIG_* primary only.
|
||||
"retention.enabled": { primary: "OPENRIG_RETENTION_ENABLED" },
|
||||
@@ -370,6 +372,7 @@ const KEY_TO_PATH: Record<SettingsValidKey, string[]> = {
|
||||
"snapshots.periodic.retention_keep": ["snapshots", "periodic", "retentionKeep"],
|
||||
"terminal.status_bar": ["terminal", "statusBar"],
|
||||
"output.compact": ["output", "compact"],
|
||||
"output.whoami_peers": ["output", "whoamiPeers"],
|
||||
"provider.usage_windows": ["provider", "usageWindows"],
|
||||
"retention.enabled": ["retention", "enabled"],
|
||||
"retention.transitions_days": ["retention", "transitionsDays"],
|
||||
@@ -645,6 +648,7 @@ function getDefaultValue(key: SettingsValidKey, workspaceRoot: string): string |
|
||||
// is redundant chrome). Operator flip is future-launches-only.
|
||||
case "terminal.status_bar": return false;
|
||||
case "output.compact": return false;
|
||||
case "output.whoami_peers": return 8;
|
||||
case "provider.usage_windows": return false;
|
||||
// OPR.0.4.6.FS-1 W2 — retention defaults (arch D3 safe values: archive
|
||||
// terminal+aged transitions >30d; prune watchdog_history >14d keep-50/job;
|
||||
@@ -816,6 +820,12 @@ const KEY_CONSTRAINTS: Partial<Record<SettingsValidKey, (raw: string, coerced: s
|
||||
// cli/src/config-store.ts KEY_CONSTRAINTS twin so the daemon HTTP write
|
||||
// surface rejects exactly what the CLI rejects. `retention.enabled` is a
|
||||
// boolean (coerceValue enforces true/false) — no constraint entry needed.
|
||||
// Slice 15 P5 — the whoami peer bound; lockstep with the CLI twin.
|
||||
"output.whoami_peers": (raw, coerced) => {
|
||||
if (!/^-?\d+$/.test((raw ?? "").trim()) || typeof coerced !== "number" || !Number.isInteger(coerced) || coerced < 1) {
|
||||
throw new Error(`Invalid value for output.whoami_peers: must be an integer >= 1, got "${raw}"`);
|
||||
}
|
||||
},
|
||||
"retention.transitions_days": (raw, coerced) => {
|
||||
if (!/^-?\d+$/.test((raw ?? "").trim()) || typeof coerced !== "number" || !Number.isInteger(coerced) || coerced < 1) {
|
||||
throw new Error(`Invalid value for retention.transitions_days: must be an integer >= 1, got "${raw}"`);
|
||||
|
||||
@@ -111,7 +111,8 @@ describe("config routes (User Settings v0)", () => {
|
||||
// + 1 queue.wake_human_rung (#811; default "always").
|
||||
// + 1 output.compact (slice 15 compact command-output switch; default off).
|
||||
// + 1 provider.usage_windows (slice 01 Codex usage windows; default off).
|
||||
expect(Object.keys(body.settings).length).toBe(75);
|
||||
// + 1 output.whoami_peers (slice 15 P5 whoami peer bound; default 8).
|
||||
expect(Object.keys(body.settings).length).toBe(76);
|
||||
expect(body.settings["launch.non_interruptive"]).toMatchObject({ value: false, source: "default" });
|
||||
expect(body.settings["runtime.readiness_timeout_seconds"]).toMatchObject({ value: 30, source: "default" });
|
||||
expect(body.settings["ui.enabled"]).toMatchObject({ value: false, source: "default" });
|
||||
|
||||
@@ -155,6 +155,8 @@ describe("SettingsStore (User Settings v0)", () => {
|
||||
"terminal.status_bar",
|
||||
// Slice 15 (OPR.0.7.0.15) — compact command-output defaults switch.
|
||||
"output.compact",
|
||||
// Slice 15 P5 — the whoami peer bound under output.compact.
|
||||
"output.whoami_peers",
|
||||
// Slice 01 (OPR.0.7.0.1) — Codex usage windows switch.
|
||||
"provider.usage_windows",
|
||||
// OPR.0.4.6.FS-1 W2 — queue-retention maintenance knobs.
|
||||
@@ -201,6 +203,16 @@ describe("SettingsStore (User Settings v0)", () => {
|
||||
expect(store.resolveOne("agents.advisor_session")).toMatchObject({ value: "advisor-lead@kernel", source: "default" });
|
||||
});
|
||||
|
||||
it("defaults output.whoami_peers to 8 and accepts only an integer >= 1 (slice 15 P5)", () => {
|
||||
const store = new SettingsStore(configPath);
|
||||
expect(store.resolveOne("output.whoami_peers")).toMatchObject({ value: 8, source: "default" });
|
||||
store.set("output.whoami_peers", "12");
|
||||
expect(store.resolveOne("output.whoami_peers")).toMatchObject({ value: 12, source: "file" });
|
||||
for (const raw of ["0", "-3", "2.5", "8junk", "many"]) {
|
||||
expect(() => store.set("output.whoami_peers", raw)).toThrow(/output\.whoami_peers/);
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves mutable 95/99 context-pressure thresholds and rejects invalid order", () => {
|
||||
const store = new SettingsStore(configPath);
|
||||
expect(store.resolveContextPressurePolicy()).toEqual({ warningPercent: 95, criticalPercent: 99 });
|
||||
|
||||
@@ -66,6 +66,7 @@ const LABELS: Record<string, string> = {
|
||||
"ui.preview.default_lines": "Preview lines", "context.system_world": "System world",
|
||||
"onboarding.default_pack.enabled": "Default onboarding pack",
|
||||
"output.compact": "Compact command output",
|
||||
"output.whoami_peers": "Peers listed by whoami (compact)",
|
||||
"provider.usage_windows": "Codex and Claude usage windows",
|
||||
"ui.enabled": "Web UI",
|
||||
"health.context_pressure.warning_percent": "Context warning threshold",
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"claude-compaction-restore/templates/compact-instruction.md": "682c778464517b831902ddc2678c055545899b4751a1d32391ce965d5eb7061d",
|
||||
"claude-compaction-restore/templates/post-compact-restore-instruction.md": "10a5a7e5e052f3aa000a4209f386dc4eb90a3c2bba449ca685f124714f59e47a",
|
||||
"delegating-work/SKILL.md": "54c061d1b28435266952d431765b40f4170581279decfffd3b10839848cd65e4",
|
||||
"forming-an-openrig-mental-model/SKILL.md": "58476b06e677c740e3986838488653b5be2010bff562f3038e6a7e339e4457f9",
|
||||
"forming-an-openrig-mental-model/SKILL.md": "5dce5a8c2cbe4928a0c2bff7f7701f7b54fd0ecc4d9650dd1287e8ec1960df60",
|
||||
"loading-addressable-markdown/SKILL.md": "104dc6a02ae6e2d0eff519387c6839cb09803945b9ee35ec3498af8feaaa8d1e",
|
||||
"loading-addressable-markdown/scripts/resolve-markdown.mjs": "61a6d52c6c94fb1878361d19bdfaeb4524062fc4c483f557ab2f140e516d15f9",
|
||||
"messaging-the-human/SKILL.md": "4804d32fe66db050722caf2b43f8b868ca65b3dfcf2f2830f8f4859726f6f1b0",
|
||||
@@ -82,7 +82,7 @@
|
||||
"openrig-operating-model/templates/SOP.md": "1eaf0daf4f9bbf0715e6a5aaf0f29f030af5fe8b8a10232488ee97c507afa6dc",
|
||||
"openrig-operating-model/templates/SPEC.md": "f126536daa59cea3f1cfb0898d83c7cdf99f6392f6f155ea64fe55187dd0f5f1",
|
||||
"openrig-skills/SKILL.md": "ed2de7fe238dcfeda0e108e3cd8c447a6c9f2d16869bbdbac404e0a1621cd6f6",
|
||||
"openrig-user/SKILL.md": "5f6ac4f04470ce33b0ab588dac1b118be4dc0e74a644632ff6f522348484b340",
|
||||
"openrig-user/SKILL.md": "f5acb06f98c225c6be1212572ea266af9a73a949f86249d684a3767d41edc0be",
|
||||
"orienting-to-an-inherited-seat/CHANGELOG.md": "4d0970e75e5c15b00a48d6a7150376d646257777264fd1b9a191165a8d5a6e84",
|
||||
"orienting-to-an-inherited-seat/SKILL.md": "65300ce0e299cce0148a8bacd51b0475ac1b59cd77a51a9bb2db9d43bda6e208",
|
||||
"queue-handoff/SKILL.md": "d4d7e3e8772886b3771daffd60523b5f79040309b892c8be1afd47d1f7287f7b",
|
||||
|
||||
Reference in new issue
Block a user