borgmcp 1.0.6 → 1.0.7
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/dist/assimilate-cmd.js +39 -511
- package/dist/assimilate-deps.js +3 -177
- package/dist/assimilate-welcome.js +2 -24
- package/dist/auth-env.js +1 -107
- package/dist/auth.js +23 -612
- package/dist/claude.js +11 -281
- package/dist/cli-help.js +29 -50
- package/dist/cli-platform.js +4 -94
- package/dist/codex-app-server.js +4 -228
- package/dist/codex-app-wake.js +2 -122
- package/dist/codex-launch.js +1 -81
- package/dist/codex-remote.js +1 -250
- package/dist/config-utils.js +3 -385
- package/dist/config.js +1 -190
- package/dist/console-prefix.js +1 -86
- package/dist/cube-name.js +1 -65
- package/dist/cubes.js +4 -269
- package/dist/debug.js +1 -71
- package/dist/device-auth.js +1 -167
- package/dist/direct-log.js +1 -11
- package/dist/health-beat.js +1 -168
- package/dist/inbox-monitor.js +1 -129
- package/dist/index.js +26 -1378
- package/dist/lifecycle-log-guard.js +2 -93
- package/dist/list-roles-render.js +6 -39
- package/dist/log-audit.js +3 -186
- package/dist/log-stream.js +9 -848
- package/dist/name-validator.js +1 -22
- package/dist/parse-assimilate-args.js +1 -82
- package/dist/postinstall.js +8 -22
- package/dist/regen-format.js +11 -337
- package/dist/regen.js +5 -83
- package/dist/remote-client.js +1 -695
- package/dist/role-resolver.js +1 -36
- package/dist/role-section.js +8 -208
- package/dist/roster-render.js +3 -96
- package/dist/setup.js +36 -251
- package/dist/shell-escape.js +1 -22
- package/dist/spawn.js +10 -29
- package/dist/stale-version-check.js +1 -102
- package/dist/stream-owner.js +2 -202
- package/dist/stream-status.js +3 -211
- package/dist/subscription-retry.js +1 -23
- package/dist/sync-roles-render.js +3 -118
- package/dist/sync.js +22 -286
- package/dist/templates.js +120 -626
- package/dist/terminal-title.js +1 -68
- package/dist/token-crypto.js +1 -91
- package/dist/token-store.js +1 -222
- package/dist/types.js +0 -5
- package/dist/version.js +2 -78
- package/dist/worktree-lifecycle.js +2 -173
- package/package.json +11 -2
- package/dist/assimilate-cmd.d.ts.map +0 -1
- package/dist/assimilate-cmd.js.map +0 -1
- package/dist/assimilate-deps.d.ts.map +0 -1
- package/dist/assimilate-deps.js.map +0 -1
- package/dist/assimilate-welcome.d.ts.map +0 -1
- package/dist/assimilate-welcome.js.map +0 -1
- package/dist/auth-env.d.ts.map +0 -1
- package/dist/auth-env.js.map +0 -1
- package/dist/auth.d.ts.map +0 -1
- package/dist/auth.js.map +0 -1
- package/dist/claude.d.ts.map +0 -1
- package/dist/claude.js.map +0 -1
- package/dist/cli-help.d.ts.map +0 -1
- package/dist/cli-help.js.map +0 -1
- package/dist/cli-platform.d.ts.map +0 -1
- package/dist/cli-platform.js.map +0 -1
- package/dist/codex-app-server.d.ts.map +0 -1
- package/dist/codex-app-server.js.map +0 -1
- package/dist/codex-app-wake.d.ts.map +0 -1
- package/dist/codex-app-wake.js.map +0 -1
- package/dist/codex-launch.d.ts.map +0 -1
- package/dist/codex-launch.js.map +0 -1
- package/dist/codex-remote.d.ts.map +0 -1
- package/dist/codex-remote.js.map +0 -1
- package/dist/config-utils.d.ts.map +0 -1
- package/dist/config-utils.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/console-prefix.d.ts.map +0 -1
- package/dist/console-prefix.js.map +0 -1
- package/dist/cube-name.d.ts.map +0 -1
- package/dist/cube-name.js.map +0 -1
- package/dist/cubes.d.ts.map +0 -1
- package/dist/cubes.js.map +0 -1
- package/dist/debug.d.ts.map +0 -1
- package/dist/debug.js.map +0 -1
- package/dist/device-auth.d.ts.map +0 -1
- package/dist/device-auth.js.map +0 -1
- package/dist/direct-log.d.ts.map +0 -1
- package/dist/direct-log.js.map +0 -1
- package/dist/health-beat.d.ts.map +0 -1
- package/dist/health-beat.js.map +0 -1
- package/dist/inbox-monitor.d.ts.map +0 -1
- package/dist/inbox-monitor.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/lifecycle-log-guard.d.ts.map +0 -1
- package/dist/lifecycle-log-guard.js.map +0 -1
- package/dist/list-roles-render.d.ts.map +0 -1
- package/dist/list-roles-render.js.map +0 -1
- package/dist/log-audit.d.ts.map +0 -1
- package/dist/log-audit.js.map +0 -1
- package/dist/log-stream.d.ts.map +0 -1
- package/dist/log-stream.js.map +0 -1
- package/dist/name-validator.d.ts.map +0 -1
- package/dist/name-validator.js.map +0 -1
- package/dist/parse-assimilate-args.d.ts.map +0 -1
- package/dist/parse-assimilate-args.js.map +0 -1
- package/dist/postinstall.d.ts.map +0 -1
- package/dist/postinstall.js.map +0 -1
- package/dist/regen-format.d.ts.map +0 -1
- package/dist/regen-format.js.map +0 -1
- package/dist/regen.d.ts.map +0 -1
- package/dist/regen.js.map +0 -1
- package/dist/remote-client.d.ts.map +0 -1
- package/dist/remote-client.js.map +0 -1
- package/dist/role-resolver.d.ts.map +0 -1
- package/dist/role-resolver.js.map +0 -1
- package/dist/role-section.d.ts.map +0 -1
- package/dist/role-section.js.map +0 -1
- package/dist/roster-render.d.ts.map +0 -1
- package/dist/roster-render.js.map +0 -1
- package/dist/setup.d.ts.map +0 -1
- package/dist/setup.js.map +0 -1
- package/dist/shell-escape.d.ts.map +0 -1
- package/dist/shell-escape.js.map +0 -1
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/stale-version-check.d.ts.map +0 -1
- package/dist/stale-version-check.js.map +0 -1
- package/dist/stream-owner.d.ts.map +0 -1
- package/dist/stream-owner.js.map +0 -1
- package/dist/stream-status.d.ts.map +0 -1
- package/dist/stream-status.js.map +0 -1
- package/dist/subscription-retry.d.ts.map +0 -1
- package/dist/subscription-retry.js.map +0 -1
- package/dist/sync-roles-render.d.ts.map +0 -1
- package/dist/sync-roles-render.js.map +0 -1
- package/dist/sync.d.ts.map +0 -1
- package/dist/sync.js.map +0 -1
- package/dist/templates.d.ts.map +0 -1
- package/dist/templates.js.map +0 -1
- package/dist/terminal-title.d.ts.map +0 -1
- package/dist/terminal-title.js.map +0 -1
- package/dist/token-crypto.d.ts.map +0 -1
- package/dist/token-crypto.js.map +0 -1
- package/dist/token-store.d.ts.map +0 -1
- package/dist/token-store.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/worktree-lifecycle.d.ts.map +0 -1
- package/dist/worktree-lifecycle.js.map +0 -1
package/dist/name-validator.js
CHANGED
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Validates an identifier used in worktree paths and other safety-critical
|
|
3
|
-
* positions (must survive shell-arg parsing without escaping).
|
|
4
|
-
*
|
|
5
|
-
* Allowed: lowercase ASCII letters, digits, hyphens, underscores.
|
|
6
|
-
* Length 1–48. Anchored. No leading hyphen (would parse as a flag).
|
|
7
|
-
*/
|
|
8
|
-
const NAME_RE = /^[a-z0-9_][a-z0-9_-]{0,47}$/;
|
|
9
|
-
export function validateName(name) {
|
|
10
|
-
if (name.length === 0) {
|
|
11
|
-
return { ok: false, error: `name must not be empty` };
|
|
12
|
-
}
|
|
13
|
-
if (!NAME_RE.test(name)) {
|
|
14
|
-
return {
|
|
15
|
-
ok: false,
|
|
16
|
-
error: `invalid name "${name}". Use lowercase letters, digits, hyphens, or ` +
|
|
17
|
-
`underscores; max 48 chars; must not start with a hyphen.`,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
return { ok: true };
|
|
21
|
-
}
|
|
22
|
-
//# sourceMappingURL=name-validator.js.map
|
|
1
|
+
const t=/^[a-z0-9_][a-z0-9_-]{0,47}$/;function r(e){return e.length===0?{ok:!1,error:"name must not be empty"}:t.test(e)?{ok:!0}:{ok:!1,error:`invalid name "${e}". Use lowercase letters, digits, hyphens, or underscores; max 48 chars; must not start with a hyphen.`}}export{r as validateName};
|
|
@@ -1,82 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Parse argv for `borg assimilate [role] [--worktree <n>] [--template <n>]
|
|
3
|
-
* [--no-template] [--cube-name <n>] [--here] [--yes]`. The `assimilate`
|
|
4
|
-
* subcommand token must already be stripped by the caller.
|
|
5
|
-
*/
|
|
6
|
-
export function parseAssimilateArgs(rawArgs) {
|
|
7
|
-
let role;
|
|
8
|
-
const flags = {};
|
|
9
|
-
for (let i = 0; i < rawArgs.length; i += 1) {
|
|
10
|
-
const arg = rawArgs[i];
|
|
11
|
-
if (arg === '--worktree') {
|
|
12
|
-
const next = rawArgs[i + 1];
|
|
13
|
-
if (typeof next !== 'string' || next.length === 0) {
|
|
14
|
-
return { ok: false, error: '--worktree requires a name argument (e.g. `--worktree drone-2`)' };
|
|
15
|
-
}
|
|
16
|
-
flags.worktree = next;
|
|
17
|
-
i += 1;
|
|
18
|
-
}
|
|
19
|
-
else if (arg === '--template') {
|
|
20
|
-
const next = rawArgs[i + 1];
|
|
21
|
-
if (typeof next !== 'string' || next.length === 0) {
|
|
22
|
-
return { ok: false, error: '--template requires a name argument (e.g. `--template software-dev`)' };
|
|
23
|
-
}
|
|
24
|
-
flags.template = next;
|
|
25
|
-
i += 1;
|
|
26
|
-
}
|
|
27
|
-
else if (arg === '--no-template') {
|
|
28
|
-
flags.noTemplate = true;
|
|
29
|
-
}
|
|
30
|
-
else if (arg === '--cube-name') {
|
|
31
|
-
const next = rawArgs[i + 1];
|
|
32
|
-
if (typeof next !== 'string' || next.length === 0) {
|
|
33
|
-
return { ok: false, error: '--cube-name requires a name argument (e.g. `--cube-name my-cube`)' };
|
|
34
|
-
}
|
|
35
|
-
flags.cubeName = next;
|
|
36
|
-
i += 1;
|
|
37
|
-
}
|
|
38
|
-
else if (arg === '--here') {
|
|
39
|
-
flags.here = true;
|
|
40
|
-
}
|
|
41
|
-
else if (arg === '--yes' || arg === '-y') {
|
|
42
|
-
flags.yes = true;
|
|
43
|
-
}
|
|
44
|
-
else if (arg === '--cli') {
|
|
45
|
-
const next = rawArgs[i + 1];
|
|
46
|
-
if (next !== 'claude' && next !== 'codex') {
|
|
47
|
-
return { ok: false, error: '--cli requires claude or codex' };
|
|
48
|
-
}
|
|
49
|
-
flags.cli = next;
|
|
50
|
-
i += 1;
|
|
51
|
-
}
|
|
52
|
-
else if (arg.startsWith('--cli=')) {
|
|
53
|
-
const value = arg.slice('--cli='.length);
|
|
54
|
-
if (value !== 'claude' && value !== 'codex') {
|
|
55
|
-
return { ok: false, error: '--cli requires claude or codex' };
|
|
56
|
-
}
|
|
57
|
-
flags.cli = value;
|
|
58
|
-
}
|
|
59
|
-
else if (arg.startsWith('--')) {
|
|
60
|
-
return {
|
|
61
|
-
ok: false,
|
|
62
|
-
error: `unknown flag: ${arg}. Supported: --worktree, --template, --no-template, --cube-name, --here, --yes, --cli`,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
if (role !== undefined) {
|
|
67
|
-
return { ok: false, error: `unexpected extra argument: ${arg} (already have role "${role}")` };
|
|
68
|
-
}
|
|
69
|
-
role = arg;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
// CR-PF-F1 (drone-2 Phase F review 2026-05-18T05:04Z): spec rev-2
|
|
73
|
-
// §Error paths mandates an argparse-level error when both --template
|
|
74
|
-
// and --no-template are passed. Catch at parse time rather than in
|
|
75
|
-
// the orchestrator so the failure happens before any auth / network /
|
|
76
|
-
// worktree-spawn work.
|
|
77
|
-
if (flags.template !== undefined && flags.noTemplate) {
|
|
78
|
-
return { ok: false, error: '--template and --no-template are mutually exclusive' };
|
|
79
|
-
}
|
|
80
|
-
return { ok: true, role, flags };
|
|
81
|
-
}
|
|
82
|
-
//# sourceMappingURL=parse-assimilate-args.js.map
|
|
1
|
+
function a(n){let o;const t={};for(let l=0;l<n.length;l+=1){const r=n[l];if(r==="--worktree"){const e=n[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--worktree requires a name argument (e.g. `--worktree drone-2`)"};t.worktree=e,l+=1}else if(r==="--template"){const e=n[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--template requires a name argument (e.g. `--template software-dev`)"};t.template=e,l+=1}else if(r==="--no-template")t.noTemplate=!0;else if(r==="--cube-name"){const e=n[l+1];if(typeof e!="string"||e.length===0)return{ok:!1,error:"--cube-name requires a name argument (e.g. `--cube-name my-cube`)"};t.cubeName=e,l+=1}else if(r==="--here")t.here=!0;else if(r==="--yes"||r==="-y")t.yes=!0;else if(r==="--cli"){const e=n[l+1];if(e!=="claude"&&e!=="codex")return{ok:!1,error:"--cli requires claude or codex"};t.cli=e,l+=1}else if(r.startsWith("--cli=")){const e=r.slice(6);if(e!=="claude"&&e!=="codex")return{ok:!1,error:"--cli requires claude or codex"};t.cli=e}else{if(r.startsWith("--"))return{ok:!1,error:`unknown flag: ${r}. Supported: --worktree, --template, --no-template, --cube-name, --here, --yes, --cli`};if(o!==void 0)return{ok:!1,error:`unexpected extra argument: ${r} (already have role "${o}")`};o=r}}return t.template!==void 0&&t.noTemplate?{ok:!1,error:"--template and --no-template are mutually exclusive"}:{ok:!0,role:o,flags:t}}export{a as parseAssimilateArgs};
|
package/dist/postinstall.js
CHANGED
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
console.error('\n◼ Error: borg must be installed globally\n');
|
|
11
|
-
console.error('Please install with:');
|
|
12
|
-
console.error(' npm install -g borg@beta\n');
|
|
13
|
-
console.error('Local installation is not supported.\n');
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
// Global install - show instructions
|
|
17
|
-
console.log('\n╔════════════════════════════════════╗');
|
|
18
|
-
console.log('║ ◼ Borg MCP Installed ◼ ║');
|
|
19
|
-
console.log('╚════════════════════════════════════╝\n');
|
|
20
|
-
console.log('Next step:');
|
|
21
|
-
console.log(' borg-setup\n');
|
|
22
|
-
export {};
|
|
23
|
-
//# sourceMappingURL=postinstall.js.map
|
|
2
|
+
const o=process.env.npm_config_global==="true";o||(console.error(`
|
|
3
|
+
\u25FC Error: borg must be installed globally
|
|
4
|
+
`),console.error("Please install with:"),console.error(` npm install -g borg@beta
|
|
5
|
+
`),console.error(`Local installation is not supported.
|
|
6
|
+
`),process.exit(1)),console.log(`
|
|
7
|
+
\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557`),console.log("\u2551 \u25FC Borg MCP Installed \u25FC \u2551"),console.log(`\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D
|
|
8
|
+
`),console.log("Next step:"),console.log(` borg-setup
|
|
9
|
+
`);
|
package/dist/regen-format.js
CHANGED
|
@@ -1,337 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* Build the universal drone playbook.
|
|
13
|
-
*
|
|
14
|
-
* The playbook is appended to every regen / cube / assimilate response.
|
|
15
|
-
* Including it on every refresh is intentional: it protects against
|
|
16
|
-
* /compact and /clear losing the procedural knowledge while state still
|
|
17
|
-
* flows through.
|
|
18
|
-
*
|
|
19
|
-
* The playbook describes the autonomous-default behavior shared by every
|
|
20
|
-
* role. Role-specific overrides (e.g., "consult the human Queen" for the
|
|
21
|
-
* Coordinator role; "ship on consensus" for the Queen role when the seat
|
|
22
|
-
* is delegated to a drone) live in each role's detailed_description, not
|
|
23
|
-
* here.
|
|
24
|
-
*/
|
|
25
|
-
export function getDronePlaybook() {
|
|
26
|
-
return `## How to operate as a Drone
|
|
27
|
-
|
|
28
|
-
You're a Drone connected to a Cube. Other drones may be working in the same cube — coordinate through the activity log.
|
|
29
|
-
|
|
30
|
-
**Tools available to you:**
|
|
31
|
-
- \`borg:regen\` — refresh full state (cube directive, role, roster, recent log) in one call
|
|
32
|
-
- \`borg:cube\` — re-read the cube directive and the role overview
|
|
33
|
-
- \`borg:role\` — re-read your role's detailed playbook
|
|
34
|
-
- \`borg:roster\` — see who else is connected
|
|
35
|
-
- \`borg:read-log [since] [limit]\` — read recent log entries from all drones
|
|
36
|
-
- \`borg:log <message>\` — append to the log
|
|
37
|
-
- \`borg:assimilate <cube>\` — switch to a different cube
|
|
38
|
-
|
|
39
|
-
**How coordination works:**
|
|
40
|
-
|
|
41
|
-
The Cube provides primitives, not workflows. Your role's detailed description (above) is your specific playbook — the conventions and signals it references come from there, not from the system. Different cubes use different conventions. The activity log is the coordination channel.
|
|
42
|
-
|
|
43
|
-
**Default operating principle: act autonomously, coordinate through the log.**
|
|
44
|
-
|
|
45
|
-
You are part of a coordinating hive. When you need input, post your question to the log and continue with other actionable work — other drones will respond. Don't wait for user input; the human supervisor (if any) is reachable through the cube's human-seat role(s) — typically a Coordinator-class role in software-dev cubes — or through the platform Queen role when delegated (when the human Queen has stepped away). The Queen role is the autonomous variant of the human-seat role: same base responsibilities, plus additional autonomous-mode behaviors documented in the Queen role's own \`detailed_description\`. The seat is singular and continuous. Your role's \`detailed_description\` (above) tells you when to escalate to the human-seat / Queen seat and what kinds of decisions require human input — follow it.
|
|
46
|
-
|
|
47
|
-
**Your operating loop:**
|
|
48
|
-
|
|
49
|
-
Each time you receive fresh state (this regen, a tool result, or a user prompt), interpret the recent log (already included in the regen output above) through your role's conventions. Look for:
|
|
50
|
-
- Other drones' questions — answer them if you can
|
|
51
|
-
- Other drones stuck or blocked — help unblock them
|
|
52
|
-
- Pending work you can pick up — claim it per your role's conventions
|
|
53
|
-
- Recent decisions or context affecting how you'd respond
|
|
54
|
-
|
|
55
|
-
If you find an actionable signal, act on it — post the appropriate convention to the log and proceed. Don't wait to be asked.
|
|
56
|
-
|
|
57
|
-
If there's a user prompt waiting, respond to it informed by the cube context. Apply your role's log conventions to the work the same way you would for a task picked up from the log: substantive units (changes that ship, blockers you hit, findings worth sharing) get logged regardless of who initiated them. If nothing's actionable and no prompt is waiting, this iteration is done — wait for the next.
|
|
58
|
-
|
|
59
|
-
**When you wake from a \`<task-notification>\`:** the event payload is a preview and may be truncated by the harness (appended with \`...(truncated)\`). The full entry is always in the DB. Before acting on a truncated entry, call \`borg:read-log since=<timestamp from the notification>\` or \`borg:regen\` to fetch the complete message — don't act on the truncated preview alone.
|
|
60
|
-
|
|
61
|
-
**When you first wake in a session:** post one \`ARRIVAL: <your-drone-label> (<your-role>) online on <hostname> at <project-path>\` entry to the cube log so the Coordinator and other drones know you've joined. Run the \`hostname\` shell command for the hostname value; use your current working directory for the project path. This is a one-time-per-session post — subsequent wakes don't repeat it. Skip if you posted ARRIVAL earlier in this same session (rare but possible after a \`/mcp\` reconnect).
|
|
62
|
-
|
|
63
|
-
**When a log entry routes work to you specifically:** call \`borg:ack entry_id=<entry-id>\` within ~60s of reading it. Applies to entries that explicitly mention your drone label and ask for action — typically \`ASSIGN:\`, \`DISPATCH:\`, \`ROUTING:\`, or a direct \`<your-drone-label>:\` mention requesting a response. The ack signals to the sender that the dispatch was received (not that the work is done — \`STARTING\` / \`DONE\` per your role's conventions still apply for that, posted as cube-log entries). Use the \`borg:ack\` tool, NOT an in-band \`ACK:\` log entry — \`borg:ack\` records the acknowledgement as a queryable DB flag (\`activity_log_acks\`) AND fans out an SSE notification to the original entry's author drone (Sprint 25 substrate + Sprint 26 ack-fan-out). The sender's Monitor wakes on your ack just like it would have on an in-band ACK post; the cube log stays clean of ack noise. Don't ack every entry that mentions your label — only routing-class signals. Mere broadcast information that happens to mention you doesn't need an ack.
|
|
64
|
-
|
|
65
|
-
**When stuck:**
|
|
66
|
-
|
|
67
|
-
Post your question or blocker to the log per your role's conventions. Continue with other actionable work in the meantime. Escalation to the Queen seat (if any) is handled by your role's specific instructions, not by stalling this session.
|
|
68
|
-
|
|
69
|
-
**Anti-passive-waiting (when your lane goes idle):**
|
|
70
|
-
|
|
71
|
-
When your role's lane goes idle — no in-flight dispatch addressed to you, no actionable signal in the recent log, no \`STARTING\` / \`REVIEW-READY\` / dispatch routing to you specifically — post \`READY: <your-drone-label> (<your-role>) — capacity clean, awaiting next dispatch from the Coordinator\` to the cube log. Don't sit silently; signal availability.
|
|
72
|
-
|
|
73
|
-
Asking for next work goes through the cube's Coordinator, never directly to the human Queen — preserves the standard escalation hierarchy. Coordinator routes you to open queue items or peer-drone work as appropriate. The \`READY\` signal is a positive availability assertion (capacity-to-allocate input for routing), not a request for human attention. Don't spam \`READY\` — once per idle period is sufficient. If Coordinator doesn't dispatch within ~15 min, follow up with \`PING: Coordinator — capacity available since <time>; any queue item I can pick up?\` to surface the gap.
|
|
74
|
-
|
|
75
|
-
Event-driven roles (PM, Security Auditor, Visionary, UX Expert, QA Tester) satisfy the same rule differently: instead of \`READY\`, proactively surface lane-substantive work that doesn't wait on dispatch (RECAP / coherence sweep / threat-model write-up / proposal authoring / UX-courtesy review on relevant in-flight PRs / QA-courtesy verification on user-observable surfaces). "Stand on signal" is the *correct* steady state for these lanes — but lane-substantive-work-surfacing-when-cluster-events-warrant is the higher-value posture per your role's standing-cadence definition. Dispatch-from-queue roles (Builder, Code Reviewer) post \`READY\` when their lane goes idle; event-driven roles do not.
|
|
76
|
-
|
|
77
|
-
**Verifying factual claims (Refinement #13 — cube-collective-validated, gh#68):**
|
|
78
|
-
|
|
79
|
-
Any time you make a factual claim that could be verified — "PR #X shipped as version Y", "function Z does W", "endpoint A returns B in prod", "package P is at version Q on npm" — verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels emerged from cluster evidence:
|
|
80
|
-
|
|
81
|
-
- **v1 (verify against the actual surface):** check the claim against the surface it describes. Caught PR #62's "watchdog scans last_log_post" factual error (claim made about \`workers/heartbeat.ts\` line behavior; grep against that file disproved the claim). Apply when the claim is about code-state.
|
|
82
|
-
- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you're checking), verify against the canonical source-of-truth: \`git tag\` for version-attribution, code-by-grep / direct file read for code-state, live \`curl\` or \`wrangler tail\` for prod-state, \`npm view\` for npm-state. Caught PR #70's "v0.8.7 / PR #47" version-attribution error (the version was verified against a prior post that itself carried the misattribution; \`git tag --contains\` was the source-of-truth that disambiguated). Apply when version numbers, deploy timestamps, or other discrete facts are in scope.
|
|
83
|
-
- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim ("the watchdog wakes silent drones"), verify the END-TO-END execution path, not just each isolated component. Code-only review of gh#39 across multiple PRs verified watchdog scan + broadcast fan-out + DB INSERT + RLS scope (each isolated mechanism correct) but didn't trace the path through to SELF Monitor fire on the SELF target — which is where the gh#71 own-drone-filter gap silently blocked the wake. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.
|
|
84
|
-
|
|
85
|
-
**Concrete verification surfaces by claim type:**
|
|
86
|
-
- Version attribution → \`git tag --contains <sha>\` or \`git log --oneline <tag>\`
|
|
87
|
-
- Code state → match the grep surface to the claim surface:
|
|
88
|
-
- Local uncommitted claim → \`grep -n "<symbol>" <file>\` or direct file read in the working tree
|
|
89
|
-
- \`origin/main\`, PR head, branch, merge-SHA, or tag claim → \`git show <ref>:<path> | grep -n "<symbol>"\` (examples: \`git show origin/main:workers/heartbeat.ts | grep -n "last_log_post"\`; \`git show origin/feat/foo:client/src/log-stream.ts | grep -n "ownDrone"\`; \`git show abc1234:workers/cubes.ts | grep -n "visibility"\`)
|
|
90
|
-
- Prod state → \`curl https://<endpoint>\` or \`wrangler tail --env production\`
|
|
91
|
-
- npm registry state → \`npm view <package>@<version>\` or \`npm view <package>@latest\`
|
|
92
|
-
- DB state → query through the existing \`db\` interface; never trust a doc claim about row counts / column values
|
|
93
|
-
- Cube log state → \`borg:read-log since=<cursor>\` directly; don't cite from memory or from another drone's summary
|
|
94
|
-
|
|
95
|
-
**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). Refinement #13 lives in this playbook rather than in any one role's text because it applies to ALL reviewers.
|
|
96
|
-
|
|
97
|
-
**Four-surface propagation (Refinement #13 sharpening — Sprint 8 PR-B + PR-D + Sprint 9 PR-A empirical evidence)**:
|
|
98
|
-
|
|
99
|
-
The discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:
|
|
100
|
-
|
|
101
|
-
- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep's the referenced file BEFORE composing the proposal. If the proposal cites current \`origin/main\` or a branch/SHA, grep that ref via \`git show <ref>:<path> | grep\`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.
|
|
102
|
-
- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep's the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via \`git show <ref>:<path> | grep\`; don't let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.
|
|
103
|
-
- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.
|
|
104
|
-
- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking gh issue from a cube event payload, the FILING drone fetches the originating entry's full body via \`borg:read-log since=<timestamp>\` BEFORE composing the issue body. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface — the filed issue becomes the cube's durable cross-sprint memory; correcting it requires a follow-up issuecomment post-filing, and Sprint N+1 pickup drones inherit the incomplete framing if the correction is missed.
|
|
105
|
-
|
|
106
|
-
**Empirical case studies (2026-05-17 Sprint 8 + Sprint 9)**: PR-B 5-drone cascade-failure on error-code casing — drone-8 proposed lowercase code names at brainstorm without grepping \`workers/errors.ts:11 ErrorCode\` enum (Surface 1 origin); drone-1 reproduced in dispatch verbatim; drone-6 implemented from dispatch; drone-2 CR + drone-3 QA initially approved without source-grepping (Surface 3 inherited the wrong framing); drone-8 caught their own origin gap at review-time via Surface 3 self-application. PR-D drone-2 CR-NIT #1 — drone-6 wrote JSDoc claim citing \`gh#39 watchdog\` semantics without grepping \`workers/heartbeat.ts\` (Surface 2 origin); drone-2 caught at Surface 3 review-time via direct file read. PR-#102 gh#103 filing — drone-1 filed gh#103 (PR #102 PM-NITs) composing the issue body from the TRUNCATED cube event preview of drone-7's 16:12:51Z entry (Surface 4 origin); NIT #2 substance dropped mid-paragraph; drone-7 closed the gap via issuecomment-4471465300 50 min post-filing. All three cases would have been cheapest to catch at the originating surface (1, 2, or 4 respectively).
|
|
107
|
-
|
|
108
|
-
**Posting to the log:**
|
|
109
|
-
|
|
110
|
-
Every time you start a task, finish a task, get stuck, answer another drone, or learn something other drones should know — post to the log per your role's conventions. This applies regardless of who initiated the work: a log signal, your own scan of the cube, or a direct user prompt all produce the same logging duty. The conventions live in your role detail; the system stays vocabulary-agnostic.
|
|
111
|
-
|
|
112
|
-
**Routing your posts — widen the directed default when delivery needs are broader (gh#16 / gh#675):**
|
|
113
|
-
|
|
114
|
-
The cube's message taxonomy routes most prefixes DIRECTED to the Coordinator by default; the \`to:\` / \`visibility:\` you pass ALWAYS overrides that default. Widen it whenever a post must reach more than "the Coordinator's attention":
|
|
115
|
-
- **Coordinators:** when you post a \`MERGED\` / \`REVIEW-FEEDBACK\` / \`QA-FAIL\` (or any verdict) a specific drone is waiting on, add \`to:[that drone]\` so they're actually WOKEN on it. Directed-ness governs the WAKE/notification — a non-recipient isn't pushed it (and for posts you mark explicit \`visibility:'direct'\`, it's also kept out of their default \`borg:read-log\` view) — so without \`to:[author]\` they can be left UNAWARE of their own merge or feedback. It is NOT read-confidentiality: every cube member can read every entry — the cube is the trust boundary — so never post secrets relying on \`to:[x]\`.
|
|
116
|
-
- **Any drone posting a multi-seat DELIVERABLE** — a spec, a security classification, a review artifact that 3+ gate seats build or review against — pass \`visibility:broadcast\` (or \`to:[the seats]\`), EVEN IF your prefix (\`DONE\`, etc.) is a directed status class. Otherwise only the Coordinator is woken on it (the taxonomy routes by prefix, not by payload) and the seats that must build or gate against it may never notice — \`visibility:broadcast\` wakes them all.
|
|
117
|
-
|
|
118
|
-
The default optimizes the COMMON case (routine status → the Coordinator's attention); you own widening the WAKE when your post must reach more — the taxonomy can't tell a bare "\`DONE:\` finished" from a "\`DONE:\`" that carries a load-bearing spec.
|
|
119
|
-
|
|
120
|
-
**Pre-commit git hygiene (universal, gh#86):**
|
|
121
|
-
|
|
122
|
-
Any drone that commits code: run \`git diff --staged --stat\` before \`git commit\` to verify file count + LOC direction + paths match your intent. Costs <100ms; catches anomalous diffs (deleted files, unexpected large -LOC, wrong paths) before they reach origin. This is universal hygiene — your role's specific playbook may layer additional git operational rules on top (Builder/Coordinator roles carry the full set per gh#86), but the pre-commit staged-diff check applies to any drone touching git state. Originates from the 2026-05-17 1dc8f01 production-main-corruption incident where a -528 LOC anomalous diff shipped to origin/main; reflexive staged-diff verification would have caught it pre-push.`;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Eager export of the playbook text. Cheap to compute (string concat);
|
|
126
|
-
* exporting as a constant lets callers splice it directly without a
|
|
127
|
-
* function call site.
|
|
128
|
-
*/
|
|
129
|
-
export const DRONE_PLAYBOOK = getDronePlaybook();
|
|
130
|
-
/**
|
|
131
|
-
* Format an absolute timestamp as a coarse "Xs/Xm/Xh ago" string.
|
|
132
|
-
*/
|
|
133
|
-
export function humanAgo(date) {
|
|
134
|
-
const then = typeof date === 'string' ? new Date(date) : date;
|
|
135
|
-
const ms = Date.now() - then.getTime();
|
|
136
|
-
if (!Number.isFinite(ms) || ms < 0)
|
|
137
|
-
return 'just now';
|
|
138
|
-
const sec = Math.floor(ms / 1000);
|
|
139
|
-
if (sec < 60)
|
|
140
|
-
return `${sec}s ago`;
|
|
141
|
-
const min = Math.floor(sec / 60);
|
|
142
|
-
if (min < 60)
|
|
143
|
-
return `${min}m ago`;
|
|
144
|
-
const hr = Math.floor(min / 60);
|
|
145
|
-
if (hr < 24)
|
|
146
|
-
return `${hr}h ago`;
|
|
147
|
-
const days = Math.floor(hr / 24);
|
|
148
|
-
return `${days}d ago`;
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Format a regen() composite into the markdown text shown to drones.
|
|
152
|
-
*
|
|
153
|
-
* The playbook is always appended. The token cost is bounded (~500 tokens),
|
|
154
|
-
* but the risk of a drone losing the playbook to /compact or /clear and
|
|
155
|
-
* being left with state but no procedural knowledge is unbounded. Always
|
|
156
|
-
* include — robustness wins.
|
|
157
|
-
*/
|
|
158
|
-
/**
|
|
159
|
-
* gh#479 — discoverability tip for intent-based routing (#468). When a
|
|
160
|
-
* cube has no `message_taxonomy` declared, borg:regen + borg:cube append
|
|
161
|
-
* this tip so operators discover how to enable smart routing. Self-
|
|
162
|
-
* removing: returns '' once a taxonomy exists. Copy is UX-locked
|
|
163
|
-
* (design d45098c1) — keep verbatim.
|
|
164
|
-
*/
|
|
165
|
-
export function nullTaxonomyTip(messageTaxonomy) {
|
|
166
|
-
const isEmpty = messageTaxonomy == null ||
|
|
167
|
-
(Array.isArray(messageTaxonomy) && messageTaxonomy.length === 0);
|
|
168
|
-
if (!isEmpty)
|
|
169
|
-
return '';
|
|
170
|
-
return 'Tip: no message taxonomy declared — set one to enable intent-based smart routing (#468). Use borg:update-cube with a taxonomy array, or add classes with borg:patch-taxonomy-class.';
|
|
171
|
-
}
|
|
172
|
-
export function regenWakePathDroneLabel(result, cachedDroneLabel) {
|
|
173
|
-
return result.drone?.label ?? cachedDroneLabel ?? null;
|
|
174
|
-
}
|
|
175
|
-
let boilerplateEmittedThisSession = false;
|
|
176
|
-
let cachedRoleTextHash = null;
|
|
177
|
-
let cachedDirectiveHash = null;
|
|
178
|
-
export function __resetRegenSessionState() {
|
|
179
|
-
boilerplateEmittedThisSession = false;
|
|
180
|
-
cachedRoleTextHash = null;
|
|
181
|
-
cachedDirectiveHash = null;
|
|
182
|
-
}
|
|
183
|
-
function safetyDisciplinesForRole(detailedDescription) {
|
|
184
|
-
const text = detailedDescription ?? '';
|
|
185
|
-
const roleScoped = ROLE_SCOPED_SAFETY_DISCIPLINES.filter((discipline) => text.includes(discipline));
|
|
186
|
-
return [...UNIVERSAL_SAFETY_DISCIPLINES, ...roleScoped];
|
|
187
|
-
}
|
|
188
|
-
export function formatRationalePointer(role, section) {
|
|
189
|
-
return `rationale → borg:role-rationale ${JSON.stringify(role)} ${JSON.stringify(section)}`;
|
|
190
|
-
}
|
|
191
|
-
export function parseRationalePointer(stub) {
|
|
192
|
-
const match = stub.match(/borg:role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);
|
|
193
|
-
if (!match)
|
|
194
|
-
return null;
|
|
195
|
-
try {
|
|
196
|
-
return { role: JSON.parse(match[1]), section: JSON.parse(match[2]) };
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
/** The full safety-discipline corpus — a `… rationale:` section is NEVER
|
|
203
|
-
* compressed if its body contains ANY of these (⛔ safety-never-compress
|
|
204
|
-
* fail-safe). Over-inclusive on purpose: checks ALL role-scoped disciplines,
|
|
205
|
-
* not just the role's own, so a wrongly-placed LIVE rule can never be stubbed. */
|
|
206
|
-
const ALL_SAFETY_DISCIPLINES = [
|
|
207
|
-
...UNIVERSAL_SAFETY_DISCIPLINES,
|
|
208
|
-
...ROLE_SCOPED_SAFETY_DISCIPLINES,
|
|
209
|
-
];
|
|
210
|
-
/**
|
|
211
|
-
* gh#496-A(b) — compress a role's `detailed_description` for rendering.
|
|
212
|
-
*
|
|
213
|
-
* Splits the role text into sections (via the client port of the worker's
|
|
214
|
-
* `parseRoleSections`, parity-guarded) and replaces each `… rationale:`
|
|
215
|
-
* plain-label section's BODY with a one-line on-demand stub
|
|
216
|
-
* (`formatRationalePointer(role, heading)` verbatim — heading sans colon).
|
|
217
|
-
* Every other section — preamble, operational-rule sections, and ALL woven
|
|
218
|
-
* safety-discipline text — is emitted INLINE, fetch-free. No content is lost:
|
|
219
|
-
* `getRoleRationale(role, heading)` serves the full section on demand, so
|
|
220
|
-
* core-inline + Σ(every stub resolved) reconstructs the stored text.
|
|
221
|
-
*
|
|
222
|
-
* ⛔ SAFETY-NEVER-COMPRESS: a section is stubbed ONLY when (a) its heading,
|
|
223
|
-
* sans-colon/trimmed/lowercased, ends with `rationale`, AND (b) its body
|
|
224
|
-
* contains NONE of `ALL_SAFETY_DISCIPLINES`. Any ambiguity (a safety string
|
|
225
|
-
* present, or simply not a `rationale:` heading) fails safe to INLINE — a
|
|
226
|
-
* wrongly-compressed LIVE rule is the catastrophic mode, so we over-include.
|
|
227
|
-
*/
|
|
228
|
-
export function compressRoleText(roleName, detailedDescription) {
|
|
229
|
-
const text = detailedDescription ?? '';
|
|
230
|
-
const sections = parseRoleSections(text);
|
|
231
|
-
return sections
|
|
232
|
-
.map((section) => {
|
|
233
|
-
if (section.kind !== 'label' || section.heading == null)
|
|
234
|
-
return section.body;
|
|
235
|
-
const isRationale = section.heading.trim().toLowerCase().endsWith('rationale');
|
|
236
|
-
if (!isRationale)
|
|
237
|
-
return section.body;
|
|
238
|
-
// ⛔ fail-safe: never stub a section carrying any safety-discipline text.
|
|
239
|
-
if (ALL_SAFETY_DISCIPLINES.some((d) => section.body.includes(d)))
|
|
240
|
-
return section.body;
|
|
241
|
-
// Preserve the heading line verbatim; replace the rationale body with the stub.
|
|
242
|
-
const nlIdx = section.body.indexOf('\n');
|
|
243
|
-
const headingLine = nlIdx === -1 ? section.body + '\n' : section.body.slice(0, nlIdx + 1);
|
|
244
|
-
return headingLine + formatRationalePointer(roleName, section.heading) + '\n';
|
|
245
|
-
})
|
|
246
|
-
.join('');
|
|
247
|
-
}
|
|
248
|
-
export function formatRegenMarkdown(result, opts = {}) {
|
|
249
|
-
const mode = opts.mode ?? 'full';
|
|
250
|
-
const roleOverview = result.roles
|
|
251
|
-
.map((r) => `- **${r.name}**${r.is_default ? ' _(default)_' : ''} — ${r.short_description || '_(no short description)_'}`)
|
|
252
|
-
.join('\n');
|
|
253
|
-
const droneOverview = result.drones
|
|
254
|
-
.map((d) => {
|
|
255
|
-
const role = result.roles.find((r) => r.id === d.role_id);
|
|
256
|
-
const roleLabel = formatRoleAgentLabel(role?.name ?? '?', d.agent_kind);
|
|
257
|
-
return `- **${d.label}** (${roleLabel}) — last seen ${humanAgo(new Date(d.last_seen))}`;
|
|
258
|
-
})
|
|
259
|
-
.join('\n') || '_(no drones connected)_';
|
|
260
|
-
const droneById = new Map(result.drones.map((d) => [d.id, d]));
|
|
261
|
-
const roleById = new Map(result.roles.map((r) => [r.id, r]));
|
|
262
|
-
const logEntries = result.recentLog
|
|
263
|
-
.map((e) => {
|
|
264
|
-
return formatLogEntryMarkdown(e, droneById, roleById);
|
|
265
|
-
})
|
|
266
|
-
.reverse()
|
|
267
|
-
.join('\n\n') || '_(no activity yet)_';
|
|
268
|
-
const isEmptyCube = result.recentLog.length === 0 && result.drones.length <= 1;
|
|
269
|
-
const gettingStarted = isEmptyCube
|
|
270
|
-
? [
|
|
271
|
-
'## Getting started',
|
|
272
|
-
'',
|
|
273
|
-
'Welcome to your first cube. Here\'s how to get going:',
|
|
274
|
-
'',
|
|
275
|
-
'1. Post your first activity: `borg:log message="Starting work on <your task>"`',
|
|
276
|
-
'2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`',
|
|
277
|
-
'3. Check who\'s here: `borg:roster`',
|
|
278
|
-
'',
|
|
279
|
-
'---',
|
|
280
|
-
'',
|
|
281
|
-
].join('\n')
|
|
282
|
-
: '';
|
|
283
|
-
const taxonomyTip = nullTaxonomyTip(result.cube.message_taxonomy);
|
|
284
|
-
const roleTextHash = result.role.detailed_description_hash ?? null;
|
|
285
|
-
const directiveHash = result.cube.directive_hash ?? null;
|
|
286
|
-
// gh#496-A(b): full mode (and the lite emit-role-text branch) render the
|
|
287
|
-
// COMPRESSED-core role text — `… rationale:` sections become on-demand
|
|
288
|
-
// borg:role-rationale stubs; rules + all safety stay inline. The lite
|
|
289
|
-
// hash-gating path (shouldEmitRoleText, over the STORED detailed_description
|
|
290
|
-
// hash) and the lite-omitted safety set below are unchanged.
|
|
291
|
-
const roleText = result.role.detailed_description
|
|
292
|
-
? compressRoleText(result.role.name, result.role.detailed_description)
|
|
293
|
-
: '_(no detailed description set)_';
|
|
294
|
-
const directiveText = result.cube.cube_directive || '_(none)_';
|
|
295
|
-
const shouldEmitRoleText = mode === 'full' || roleTextHash == null || roleTextHash !== cachedRoleTextHash;
|
|
296
|
-
const shouldEmitDirective = mode === 'full' || directiveHash == null || directiveHash !== cachedDirectiveHash;
|
|
297
|
-
const shouldEmitPlaybook = mode === 'full' || !boilerplateEmittedThisSession;
|
|
298
|
-
const lines = [
|
|
299
|
-
gettingStarted + `# Cube: ${result.cube.name} — ${result.drone.label}`,
|
|
300
|
-
'',
|
|
301
|
-
`**Your role:** ${result.role.name}`,
|
|
302
|
-
'',
|
|
303
|
-
];
|
|
304
|
-
if (mode === 'lite') {
|
|
305
|
-
lines.push("_(lite regen — role playbook and cube directive may be omitted when unchanged. If they're NOT in your current context (e.g. after a context-compaction), call `borg:regen mode=\"full\"` to re-orient.)_", '');
|
|
306
|
-
}
|
|
307
|
-
lines.push(`## Cube directive`, shouldEmitDirective
|
|
308
|
-
? directiveText
|
|
309
|
-
: '_(unchanged since your last full/lite regen; omitted in lite mode)_', '', ...(taxonomyTip ? [taxonomyTip, ''] : []), `## Your role: ${result.role.name}`, shouldEmitRoleText
|
|
310
|
-
? roleText
|
|
311
|
-
: [
|
|
312
|
-
'_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_',
|
|
313
|
-
'',
|
|
314
|
-
...safetyDisciplinesForRole(result.role.detailed_description),
|
|
315
|
-
].join('\n'), '', `## Roles in this cube`, roleOverview, '', `## Connected drones`, droneOverview, '', `## Recent activity`, logEntries);
|
|
316
|
-
if (shouldEmitPlaybook) {
|
|
317
|
-
lines.push('', getDronePlaybook());
|
|
318
|
-
boilerplateEmittedThisSession = true;
|
|
319
|
-
}
|
|
320
|
-
if (shouldEmitRoleText && roleTextHash != null) {
|
|
321
|
-
cachedRoleTextHash = roleTextHash;
|
|
322
|
-
}
|
|
323
|
-
if (shouldEmitDirective && directiveHash != null) {
|
|
324
|
-
cachedDirectiveHash = directiveHash;
|
|
325
|
-
}
|
|
326
|
-
return lines.join('\n');
|
|
327
|
-
}
|
|
328
|
-
export function formatLogEntryMarkdown(entry, droneById, roleById) {
|
|
329
|
-
const d = droneById.get(entry.drone_id);
|
|
330
|
-
const r = d ? roleById.get(d.role_id) : null;
|
|
331
|
-
const ts = new Date(entry.created_at).toISOString();
|
|
332
|
-
const entryId = typeof entry.id === 'string' && entry.id.length > 0
|
|
333
|
-
? ` [entry_id: ${entry.id}]`
|
|
334
|
-
: '';
|
|
335
|
-
return `**[${ts}]**${entryId} ${d?.label ?? '?'} (${r?.name ?? '?'}): ${entry.message}`;
|
|
336
|
-
}
|
|
337
|
-
//# sourceMappingURL=regen-format.js.map
|
|
1
|
+
import{ROLE_SCOPED_SAFETY_DISCIPLINES as v,UNIVERSAL_SAFETY_DISCIPLINES as w}from"./templates.js";import{parseRoleSections as x}from"./role-section.js";import{formatRoleAgentLabel as T}from"./roster-render.js";function k(){return"## How to operate as a Drone\n\nYou're a Drone connected to a Cube. Other drones may be working in the same cube \u2014 coordinate through the activity log.\n\n**Tools available to you:**\n- `borg:regen` \u2014 refresh full state (cube directive, role, roster, recent log) in one call\n- `borg:cube` \u2014 re-read the cube directive and the role overview\n- `borg:role` \u2014 re-read your role's detailed playbook\n- `borg:roster` \u2014 see who else is connected\n- `borg:read-log [since] [limit]` \u2014 read recent log entries from all drones\n- `borg:log <message>` \u2014 append to the log\n- `borg:assimilate <cube>` \u2014 switch to a different cube\n\n**How coordination works:**\n\nThe Cube provides primitives, not workflows. Your role's detailed description (above) is your specific playbook \u2014 the conventions and signals it references come from there, not from the system. Different cubes use different conventions. The activity log is the coordination channel.\n\n**Default operating principle: act autonomously, coordinate through the log.**\n\nYou are part of a coordinating hive. When you need input, post your question to the log and continue with other actionable work \u2014 other drones will respond. Don't wait for user input; the human supervisor (if any) is reachable through the cube's human-seat role(s) \u2014 typically a Coordinator-class role in software-dev cubes \u2014 or through the platform Queen role when delegated (when the human Queen has stepped away). The Queen role is the autonomous variant of the human-seat role: same base responsibilities, plus additional autonomous-mode behaviors documented in the Queen role's own `detailed_description`. The seat is singular and continuous. Your role's `detailed_description` (above) tells you when to escalate to the human-seat / Queen seat and what kinds of decisions require human input \u2014 follow it.\n\n**Your operating loop:**\n\nEach time you receive fresh state (this regen, a tool result, or a user prompt), interpret the recent log (already included in the regen output above) through your role's conventions. Look for:\n- Other drones' questions \u2014 answer them if you can\n- Other drones stuck or blocked \u2014 help unblock them\n- Pending work you can pick up \u2014 claim it per your role's conventions\n- Recent decisions or context affecting how you'd respond\n\nIf you find an actionable signal, act on it \u2014 post the appropriate convention to the log and proceed. Don't wait to be asked.\n\nIf there's a user prompt waiting, respond to it informed by the cube context. Apply your role's log conventions to the work the same way you would for a task picked up from the log: substantive units (changes that ship, blockers you hit, findings worth sharing) get logged regardless of who initiated them. If nothing's actionable and no prompt is waiting, this iteration is done \u2014 wait for the next.\n\n**When you wake from a `<task-notification>`:** the event payload is a preview and may be truncated by the harness (appended with `...(truncated)`). The full entry is always in the DB. Before acting on a truncated entry, call `borg:read-log since=<timestamp from the notification>` or `borg:regen` to fetch the complete message \u2014 don't act on the truncated preview alone.\n\n**When you first wake in a session:** post one `ARRIVAL: <your-drone-label> (<your-role>) online on <hostname> at <project-path>` entry to the cube log so the Coordinator and other drones know you've joined. Run the `hostname` shell command for the hostname value; use your current working directory for the project path. This is a one-time-per-session post \u2014 subsequent wakes don't repeat it. Skip if you posted ARRIVAL earlier in this same session (rare but possible after a `/mcp` reconnect).\n\n**When a log entry routes work to you specifically:** call `borg:ack entry_id=<entry-id>` within ~60s of reading it. Applies to entries that explicitly mention your drone label and ask for action \u2014 typically `ASSIGN:`, `DISPATCH:`, `ROUTING:`, or a direct `<your-drone-label>:` mention requesting a response. The ack signals to the sender that the dispatch was received (not that the work is done \u2014 `STARTING` / `DONE` per your role's conventions still apply for that, posted as cube-log entries). Use the `borg:ack` tool, NOT an in-band `ACK:` log entry \u2014 `borg:ack` records the acknowledgement as a queryable DB flag (`activity_log_acks`) AND fans out an SSE notification to the original entry's author drone (Sprint 25 substrate + Sprint 26 ack-fan-out). The sender's Monitor wakes on your ack just like it would have on an in-band ACK post; the cube log stays clean of ack noise. Don't ack every entry that mentions your label \u2014 only routing-class signals. Mere broadcast information that happens to mention you doesn't need an ack.\n\n**When stuck:**\n\nPost your question or blocker to the log per your role's conventions. Continue with other actionable work in the meantime. Escalation to the Queen seat (if any) is handled by your role's specific instructions, not by stalling this session.\n\n**Anti-passive-waiting (when your lane goes idle):**\n\nWhen your role's lane goes idle \u2014 no in-flight dispatch addressed to you, no actionable signal in the recent log, no `STARTING` / `REVIEW-READY` / dispatch routing to you specifically \u2014 post `READY: <your-drone-label> (<your-role>) \u2014 capacity clean, awaiting next dispatch from the Coordinator` to the cube log. Don't sit silently; signal availability.\n\nAsking for next work goes through the cube's Coordinator, never directly to the human Queen \u2014 preserves the standard escalation hierarchy. Coordinator routes you to open queue items or peer-drone work as appropriate. The `READY` signal is a positive availability assertion (capacity-to-allocate input for routing), not a request for human attention. Don't spam `READY` \u2014 once per idle period is sufficient. If Coordinator doesn't dispatch within ~15 min, follow up with `PING: Coordinator \u2014 capacity available since <time>; any queue item I can pick up?` to surface the gap.\n\nEvent-driven roles (PM, Security Auditor, Visionary, UX Expert, QA Tester) satisfy the same rule differently: instead of `READY`, proactively surface lane-substantive work that doesn't wait on dispatch (RECAP / coherence sweep / threat-model write-up / proposal authoring / UX-courtesy review on relevant in-flight PRs / QA-courtesy verification on user-observable surfaces). \"Stand on signal\" is the *correct* steady state for these lanes \u2014 but lane-substantive-work-surfacing-when-cluster-events-warrant is the higher-value posture per your role's standing-cadence definition. Dispatch-from-queue roles (Builder, Code Reviewer) post `READY` when their lane goes idle; event-driven roles do not.\n\n**Verifying factual claims (Refinement #13 \u2014 cube-collective-validated, gh#68):**\n\nAny time you make a factual claim that could be verified \u2014 \"PR #X shipped as version Y\", \"function Z does W\", \"endpoint A returns B in prod\", \"package P is at version Q on npm\" \u2014 verify the claim against a SOURCE-OF-TRUTH surface BEFORE writing it, not against a derivative artifact (another post, doc, summary, or your own prior framing). Three sharpening levels emerged from cluster evidence:\n\n- **v1 (verify against the actual surface):** check the claim against the surface it describes. Caught PR #62's \"watchdog scans last_log_post\" factual error (claim made about `workers/heartbeat.ts` line behavior; grep against that file disproved the claim). Apply when the claim is about code-state.\n- **v2 (source-of-truth vs derivative artifacts):** when the verification surface itself could carry the original error chain (another post citing the same wrong claim, a doc copy-mirrored from the post you're checking), verify against the canonical source-of-truth: `git tag` for version-attribution, code-by-grep / direct file read for code-state, live `curl` or `wrangler tail` for prod-state, `npm view` for npm-state. Caught PR #70's \"v0.8.7 / PR #47\" version-attribution error (the version was verified against a prior post that itself carried the misattribution; `git tag --contains` was the source-of-truth that disambiguated). Apply when version numbers, deploy timestamps, or other discrete facts are in scope.\n- **v3 (end-to-end execution path vs originating mechanism):** when verifying a live-mechanism claim (\"the watchdog wakes silent drones\"), verify the END-TO-END execution path, not just each isolated component. Code-only review of gh#39 across multiple PRs verified watchdog scan + broadcast fan-out + DB INSERT + RLS scope (each isolated mechanism correct) but didn't trace the path through to SELF Monitor fire on the SELF target \u2014 which is where the gh#71 own-drone-filter gap silently blocked the wake. Apply when live-mechanism correctness is being claimed; trace the path the wake/value/state actually takes from origin to terminal observer.\n\n**Concrete verification surfaces by claim type:**\n- Version attribution \u2192 `git tag --contains <sha>` or `git log --oneline <tag>`\n- Code state \u2192 match the grep surface to the claim surface:\n - Local uncommitted claim \u2192 `grep -n \"<symbol>\" <file>` or direct file read in the working tree\n - `origin/main`, PR head, branch, merge-SHA, or tag claim \u2192 `git show <ref>:<path> | grep -n \"<symbol>\"` (examples: `git show origin/main:workers/heartbeat.ts | grep -n \"last_log_post\"`; `git show origin/feat/foo:client/src/log-stream.ts | grep -n \"ownDrone\"`; `git show abc1234:workers/cubes.ts | grep -n \"visibility\"`)\n- Prod state \u2192 `curl https://<endpoint>` or `wrangler tail --env production`\n- npm registry state \u2192 `npm view <package>@<version>` or `npm view <package>@latest`\n- DB state \u2192 query through the existing `db` interface; never trust a doc claim about row counts / column values\n- Cube log state \u2192 `borg:read-log since=<cursor>` directly; don't cite from memory or from another drone's summary\n\n**The discipline is universal to reviewer-class actions** (Code Reviewer formal gates + Security Auditor SR gates + PM-courtesy verifications + UX-courtesy reviews + any drone making a verification-worthy factual claim in their cube-log post). Refinement #13 lives in this playbook rather than in any one role's text because it applies to ALL reviewers.\n\n**Four-surface propagation (Refinement #13 sharpening \u2014 Sprint 8 PR-B + PR-D + Sprint 9 PR-A empirical evidence)**:\n\nThe discipline applies at FOUR surfaces. Catches at the surface closest to origin are cheapest; catches at later surfaces have already propagated through earlier consumers:\n\n- **Surface 1 (brainstorm-proposal time)**: when a brainstorm contribution names specific code identifiers / API field names / enum values / column names / function signatures, the PROPOSING drone source-grep's the referenced file BEFORE composing the proposal. If the proposal cites current `origin/main` or a branch/SHA, grep that ref via `git show <ref>:<path> | grep`; working-tree grep is only for explicitly local/uncommitted claims. Cheapest catch surface; one drone catches one error.\n- **Surface 2 (comment/JSDoc/docstring writing time)**: when an implementation comment cites cross-file invariants (other modules' thresholds, schema columns, enum values, semantic contracts), the WRITING drone source-grep's the referenced file BEFORE writing the comment. If the comment describes a merged/base/PR-head state, grep the named ref via `git show <ref>:<path> | grep`; don't let a stale local checkout stand in for the ref being described. Mid-cost catch; one drone catches one error but downstream reviewers may inherit the wrong mental model from the comment.\n- **Surface 3 (review-time verification)**: the existing review-class discipline (Code Reviewer formal gates + Security Auditor SR gates + PM/UX/QA courtesy reviews). Late catch opportunity; if the error propagated through Surfaces 1 + 2, multiple reviewers may have already trusted the framing instead of source-grepping themselves.\n- **Surface 4 (durable-tracking-artifact-writing time)**: when filing a deferred-tracking gh issue from a cube event payload, the FILING drone fetches the originating entry's full body via `borg:read-log since=<timestamp>` BEFORE composing the issue body. Cube event previews can truncate substantive content (mid-paragraph cuts on long entries); filing from the truncated preview trusts a derivative artifact instead of the source-of-truth full entry. Most expensive surface \u2014 the filed issue becomes the cube's durable cross-sprint memory; correcting it requires a follow-up issuecomment post-filing, and Sprint N+1 pickup drones inherit the incomplete framing if the correction is missed.\n\n**Empirical case studies (2026-05-17 Sprint 8 + Sprint 9)**: PR-B 5-drone cascade-failure on error-code casing \u2014 drone-8 proposed lowercase code names at brainstorm without grepping `workers/errors.ts:11 ErrorCode` enum (Surface 1 origin); drone-1 reproduced in dispatch verbatim; drone-6 implemented from dispatch; drone-2 CR + drone-3 QA initially approved without source-grepping (Surface 3 inherited the wrong framing); drone-8 caught their own origin gap at review-time via Surface 3 self-application. PR-D drone-2 CR-NIT #1 \u2014 drone-6 wrote JSDoc claim citing `gh#39 watchdog` semantics without grepping `workers/heartbeat.ts` (Surface 2 origin); drone-2 caught at Surface 3 review-time via direct file read. PR-#102 gh#103 filing \u2014 drone-1 filed gh#103 (PR #102 PM-NITs) composing the issue body from the TRUNCATED cube event preview of drone-7's 16:12:51Z entry (Surface 4 origin); NIT #2 substance dropped mid-paragraph; drone-7 closed the gap via issuecomment-4471465300 50 min post-filing. All three cases would have been cheapest to catch at the originating surface (1, 2, or 4 respectively).\n\n**Posting to the log:**\n\nEvery time you start a task, finish a task, get stuck, answer another drone, or learn something other drones should know \u2014 post to the log per your role's conventions. This applies regardless of who initiated the work: a log signal, your own scan of the cube, or a direct user prompt all produce the same logging duty. The conventions live in your role detail; the system stays vocabulary-agnostic.\n\n**Routing your posts \u2014 widen the directed default when delivery needs are broader (gh#16 / gh#675):**\n\nThe cube's message taxonomy routes most prefixes DIRECTED to the Coordinator by default; the `to:` / `visibility:` you pass ALWAYS overrides that default. Widen it whenever a post must reach more than \"the Coordinator's attention\":\n- **Coordinators:** when you post a `MERGED` / `REVIEW-FEEDBACK` / `QA-FAIL` (or any verdict) a specific drone is waiting on, add `to:[that drone]` so they're actually WOKEN on it. Directed-ness governs the WAKE/notification \u2014 a non-recipient isn't pushed it (and for posts you mark explicit `visibility:'direct'`, it's also kept out of their default `borg:read-log` view) \u2014 so without `to:[author]` they can be left UNAWARE of their own merge or feedback. It is NOT read-confidentiality: every cube member can read every entry \u2014 the cube is the trust boundary \u2014 so never post secrets relying on `to:[x]`.\n- **Any drone posting a multi-seat DELIVERABLE** \u2014 a spec, a security classification, a review artifact that 3+ gate seats build or review against \u2014 pass `visibility:broadcast` (or `to:[the seats]`), EVEN IF your prefix (`DONE`, etc.) is a directed status class. Otherwise only the Coordinator is woken on it (the taxonomy routes by prefix, not by payload) and the seats that must build or gate against it may never notice \u2014 `visibility:broadcast` wakes them all.\n\nThe default optimizes the COMMON case (routine status \u2192 the Coordinator's attention); you own widening the WAKE when your post must reach more \u2014 the taxonomy can't tell a bare \"`DONE:` finished\" from a \"`DONE:`\" that carries a load-bearing spec.\n\n**Pre-commit git hygiene (universal, gh#86):**\n\nAny drone that commits code: run `git diff --staged --stat` before `git commit` to verify file count + LOC direction + paths match your intent. Costs <100ms; catches anomalous diffs (deleted files, unexpected large -LOC, wrong paths) before they reach origin. This is universal hygiene \u2014 your role's specific playbook may layer additional git operational rules on top (Builder/Coordinator roles carry the full set per gh#86), but the pre-commit staged-diff check applies to any drone touching git state. Originates from the 2026-05-17 1dc8f01 production-main-corruption incident where a -528 LOC anomalous diff shipped to origin/main; reflexive staged-diff verification would have caught it pre-push."}const F=k();function I(e){const o=typeof e=="string"?new Date(e):e,i=Date.now()-o.getTime();if(!Number.isFinite(i)||i<0)return"just now";const n=Math.floor(i/1e3);if(n<60)return`${n}s ago`;const t=Math.floor(n/60);if(t<60)return`${t}m ago`;const a=Math.floor(t/60);return a<24?`${a}h ago`:`${Math.floor(a/24)}d ago`}function O(e){return e==null||Array.isArray(e)&&e.length===0?"Tip: no message taxonomy declared \u2014 set one to enable intent-based smart routing (#468). Use borg:update-cube with a taxonomy array, or add classes with borg:patch-taxonomy-class.":""}function j(e,o){return e.drone?.label??o??null}let d=!1,u=null,p=null;function q(){d=!1,u=null,p=null}function P(e){const o=e??"",i=v.filter(n=>o.includes(n));return[...w,...i]}function N(e,o){return`rationale \u2192 borg:role-rationale ${JSON.stringify(e)} ${JSON.stringify(o)}`}function Q(e){const o=e.match(/borg:role-rationale\s+("(?:(?:\\.)|[^"\\])*")\s+("(?:(?:\\.)|[^"\\])*")/);if(!o)return null;try{return{role:JSON.parse(o[1]),section:JSON.parse(o[2])}}catch{return null}}const L=[...w,...v];function $(e,o){return x(o??"").map(t=>{if(t.kind!=="label"||t.heading==null||!t.heading.trim().toLowerCase().endsWith("rationale")||L.some(m=>t.body.includes(m)))return t.body;const s=t.body.indexOf(`
|
|
2
|
+
`);return(s===-1?t.body+`
|
|
3
|
+
`:t.body.slice(0,s+1))+N(e,t.heading)+`
|
|
4
|
+
`}).join("")}function U(e,o={}){const i=o.mode??"full",n=e.roles.map(r=>`- **${r.name}**${r.is_default?" _(default)_":""} \u2014 ${r.short_description||"_(no short description)_"}`).join(`
|
|
5
|
+
`),t=e.drones.map(r=>{const C=e.roles.find(_=>_.id===r.role_id),D=T(C?.name??"?",r.agent_kind);return`- **${r.label}** (${D}) \u2014 last seen ${I(new Date(r.last_seen))}`}).join(`
|
|
6
|
+
`)||"_(no drones connected)_",a=new Map(e.drones.map(r=>[r.id,r])),s=new Map(e.roles.map(r=>[r.id,r])),g=e.recentLog.map(r=>M(r,a,s)).reverse().join(`
|
|
7
|
+
|
|
8
|
+
`)||"_(no activity yet)_",R=e.recentLog.length===0&&e.drones.length<=1?["## Getting started","","Welcome to your first cube. Here's how to get going:","",'1. Post your first activity: `borg:log message="Starting work on <your task>"`',"2. Invite another agent session: open a new terminal and run `borg assimilate --worktree <name>`","3. Check who's here: `borg:roster`","","---",""].join(`
|
|
9
|
+
`):"",f=O(e.cube.message_taxonomy),c=e.role.detailed_description_hash??null,l=e.cube.directive_hash??null,E=e.role.detailed_description?$(e.role.name,e.role.detailed_description):"_(no detailed description set)_",S=e.cube.cube_directive||"_(none)_",y=i==="full"||c==null||c!==u,b=i==="full"||l==null||l!==p,A=i==="full"||!d,h=[R+`# Cube: ${e.cube.name} \u2014 ${e.drone.label}`,"",`**Your role:** ${e.role.name}`,""];return i==="lite"&&h.push('_(lite regen \u2014 role playbook and cube directive may be omitted when unchanged. If they\'re NOT in your current context (e.g. after a context-compaction), call `borg:regen mode="full"` to re-orient.)_',""),h.push("## Cube directive",b?S:"_(unchanged since your last full/lite regen; omitted in lite mode)_","",...f?[f,""]:[],`## Your role: ${e.role.name}`,y?E:["_(role playbook unchanged since your last full/lite regen; omitted in lite mode)_","",...P(e.role.detailed_description)].join(`
|
|
10
|
+
`),"","## Roles in this cube",n,"","## Connected drones",t,"","## Recent activity",g),A&&(h.push("",k()),d=!0),y&&c!=null&&(u=c),b&&l!=null&&(p=l),h.join(`
|
|
11
|
+
`)}function M(e,o,i){const n=o.get(e.drone_id),t=n?i.get(n.role_id):null,a=new Date(e.created_at).toISOString(),s=typeof e.id=="string"&&e.id.length>0?` [entry_id: ${e.id}]`:"";return`**[${a}]**${s} ${n?.label??"?"} (${t?.name??"?"}): ${e.message}`}export{F as DRONE_PLAYBOOK,q as __resetRegenSessionState,$ as compressRoleText,M as formatLogEntryMarkdown,N as formatRationalePointer,U as formatRegenMarkdown,k as getDronePlaybook,I as humanAgo,O as nullTaxonomyTip,Q as parseRationalePointer,j as regenWakePathDroneLabel};
|