cyberfleet 0.4.0 → 0.5.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberfleet",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Fleet & crew personas over cyberlegion — Pod (ship's bridge), Operator (command center), Crimp (crew recruitment), and Mechanic (automaton building).",
5
5
  "author": {
6
6
  "name": "unional"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyberfleet",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Fleet & crew personas over cyberlegion — Pod (ship's bridge), Operator (command center), Crimp (crew recruitment), and Mechanic (automaton building).",
5
5
  "author": {
6
6
  "name": "unional"
@@ -13,8 +13,5 @@
13
13
  "inter-agent",
14
14
  "fleet"
15
15
  ],
16
- "skills": "./skills",
17
- "agents": [
18
- "./agents/headless-operator.md"
19
- ]
16
+ "skills": "./skills"
20
17
  }
@@ -0,0 +1,3 @@
1
+ {
2
+ "cyberlegion": "1.1.0"
3
+ }
@@ -32,7 +32,13 @@ while capacity K and ready:
32
32
  mission-graph append node/edge ... (claim) # SINGLE WRITER: status=in-progress, before spawn
33
33
  cyberlegion unit spawn <ship> # AFK -> autonomous ship; HITL -> human channel
34
34
  on mission-done(m): # m reports through its existing HANDOFF relay
35
+ if the summons carries no Council words authorizing this tick's merges:
36
+ hold m: no merge, no retire, pod left running; batch a decision-request naming the merge
37
+ continue
35
38
  merge per merge-backstop-governance # Operation order + speculative-CI gate + bisect-on-red
39
+ if the harness or host refuses the merge: # never re-run it, never another command/API/unit/setting
40
+ hold m as above, naming the refusal in the decision-request
41
+ continue
36
42
  cyberlegion unit close <id> # tear down the pod that ran it (spawn's inverse)
37
43
  mail every other open pod: trunk moved # rebase, adapt, re-verify, report (merge-backstop §5)
38
44
  mission-graph append (retire + discovered edges/nodes) # SINGLE WRITER
@@ -53,9 +59,14 @@ on mission-done(m): # m reports through its existing
53
59
  human-attributed verdict, a publish, a history rewrite, a settings or secret change, a widened
54
60
  delegation, or a minted owner identity, load **`authority-governance`** by name and follow it — the
55
61
  same way the merge step loads `merge-backstop-governance`. Never carry the authority judgment inline.
56
- - **The tick is the delegation.** Being summoned for a tick is what permits this loop to retire that
57
- tick's missions; the merge still lands only on green speculative CI. It covers that tick's missions and
58
- no other class of action — a release, a settings change, or anything else on **`authority-governance`**'s
62
+ - **The summons alone is not the delegation.** Being summoned for a tick does not permit this loop to
63
+ merge. It merges that tick's missions only when the summons carries the **Council's own words**
64
+ authorizing those merges (relayed as `authority-governance` §3 requires), and the merge still lands only
65
+ on green speculative CI. Without those words, hold each clean merge: no merge, no retirement, its pod
66
+ left running, and a decision-request naming that merge batched into the return packet — the merge waits
67
+ for a tick whose summons carries the Council's answer. A merge the harness or host refuses is never
68
+ retried or reached another way; hold it and report the refusal. The delegation covers that tick's
69
+ missions and no other class of action — a release, a settings change, or anything else on **`authority-governance`**'s
59
70
  ratification-class list needs its own Council decision, batched up the relay rather than assumed. The
60
71
  SDD leash is not this delegation: it governs which SDD gate an agent may self-assert, never a merge.
61
72
  - **Two orderings split.** `ready` governs **issue**; **retirement is Operation-ordered merge**. Load
@@ -72,13 +83,18 @@ all spawning is Operator's, and **Pod never spawns**, so there is no intra-missi
72
83
  contrast against. Never invoke a rule of the in-ship Pod persona; never type into a dispatched ship's
73
84
  pane. Dispatch is `cyberlegion unit spawn` with a brief
74
85
  that stands on its own (the new Pod starts cold and reads it through its own SessionStart hook) and
75
- `--at workspace` so each ship opens in its own workspace.
86
+ `--at workspace` so each ship opens in its own workspace. The brief names its thread id and spells out
87
+ the exact reply command, as the in-session Operator's does: `cyberlegion mail send --to <return handle>
88
+ --thread <thread id> --subject "<done|blocked>: <work item>" --body "<pull request URL, CI state, anything held>"`,
89
+ the same send with `--to operator` when the return handle resolves to no live unit, and never the
90
+ harness's own messaging tool, which cannot reach cyberlegion handles.
76
91
 
77
92
  ## Report and ask via the relay
78
93
 
79
94
  The Operator never asks live. It **batches** into its return packet every point the in-session Operator
80
95
  would surface to the Council — an ambiguous rank tie it has no policy to break, a mission whose brief
81
- is missing, a HITL mission it has no human channel to serve — and whatever spawned it owns the relay and
96
+ is missing, a HITL mission it has no human channel to serve, a clean merge it holds for want of the
97
+ Council's words, a merge the harness refused — and whatever spawned it owns the relay and
82
98
  re-invokes once answers land. If it was started **frameless** (a bare scheduler run with no spawner
83
99
  awaiting its return), push the report to the standing owner inbox and exit. This is the same relay
84
100
  contract the headless-legate uses; do not re-derive it here.
@@ -92,6 +108,27 @@ reaches for an MCP messaging server, and never assumes every ship runs the same
92
108
  single graph writer and the read-only consumer of `ready` — the loop and the frontier consumption are
93
109
  the Operator's; the per-unit spawn mechanism is cyberlegion's.
94
110
 
111
+ ## Resolving `cyberlegion`
112
+
113
+ Every `cyberlegion …` command in this loop runs whichever CLI resolves below, resolved afresh in each
114
+ session and again after any plugin reload. Never hardcode a versioned
115
+ `~/.claude/plugins/cache/…/<version>/` path: a reload installs a new version beside the old one, and a
116
+ remembered path keeps running the old CLI. The pin is the `cyberlegion` entry in this plugin's
117
+ bundled `<this agent's directory>/../.plugin/pins.json` — read it, never invent or scrape a version. Take the first rung that
118
+ resolves and whose `cyberlegion --version` is at or above the pin; skip a rung that reports an older
119
+ version:
120
+
121
+ 1. `cyberlegion` on `PATH`.
122
+ 2. The installed cyberlegion plugin: read `~/.claude/plugins/installed_plugins.json` now, take the
123
+ `installPath` of its `cyberlegion@<marketplace>` entry, and run `node
124
+ <installPath>/bin/cyberlegion.mjs`.
125
+ 3. `npx -y cyberlegion@<pin>`.
126
+
127
+ With no pin (no `pins.json`, no `cyberlegion` key, or a malformed map), no rung has a version floor
128
+ and the last rung is the unpinned `npx -y cyberlegion`. When no rung resolves, stop the tick and report it up the relay with the
129
+ install hint: install the `cyberlegion` plugin beside this one, or `npm install -g
130
+ cyberlegion@<pin>`.
131
+
95
132
  ## Stateless per tick
96
133
 
97
134
  Spawned cold for each tick and carrying no memory across ticks: derive everything from the current
@@ -1,6 +1,23 @@
1
1
  #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from 'node:fs'
2
3
  import { dirname, join } from 'node:path'
3
4
  import { fileURLToPath } from 'node:url'
4
5
 
5
6
  const dir = dirname(fileURLToPath(import.meta.url))
6
- await import(join(dir, '..', 'dist', 'cli.mjs'))
7
+ const cli = join(dir, '..', 'dist', 'cli.mjs')
8
+
9
+ // A plugin installed from a source checkout carries whatever the checkout carries, and no install
10
+ // step runs after the copy. Without the built CLI, a bare import dies with ERR_MODULE_NOT_FOUND and
11
+ // no way forward; say what is missing and name the exact published version that runs instead.
12
+ if (!existsSync(cli)) {
13
+ const { version } = JSON.parse(readFileSync(join(dir, '..', 'package.json'), 'utf8'))
14
+ process.stderr.write(
15
+ `cyberfleet: the built CLI is missing: ${cli}\n` +
16
+ 'This install has no dist/ — it is a source checkout that was never built.\n' +
17
+ `Run the published CLI of the same version instead: npx -y cyberfleet@${version}\n` +
18
+ `Or build it in place: pnpm install && pnpm build (in ${join(dir, '..')})\n`,
19
+ )
20
+ process.exit(1)
21
+ }
22
+
23
+ await import(cli)