Skip to content

OpenFleet: a swarm leaves a record behind (PRD 0016), moshcode fleet, 0.99.0 - #515

Merged
ralyodio merged 4 commits into
mainfrom
openfleet
Sep 13, 2026
Merged

OpenFleet: a swarm leaves a record behind (PRD 0016), moshcode fleet, 0.99.0#515
ralyodio merged 4 commits into
mainfrom
openfleet

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

OpenFleet: a swarm leaves a record behind (PRD 0016)

Implements the moshcode side of the OpenFleet specification (https://logicsrc.com/openfleet), written this morning after a moshcode swarm run whose workers could not name their parent.

  • moshcode swarm mints the swarm id before the plan call, runs the planner without OPENFLEET_SWARM, asks the planner for the files each piece may write and stores them as piece.owns, writes swarm.spawn before the first pane starts, writes one unclaimed record per pane (member id <swarm>-<n>, also the pane name, session the pane), passes the five OPENFLEET_* variables into the pane environment beside the herd variables and keeps them through the credential strip, writes member.start on the pane's behalf when a prompt goes in and nothing has claimed the record, and at the end member.end for every pane, one swarm.end with the synthesis and --verify verdicts, then the kills in a finally. --keep writes no end lines and no kills. A swarm that would exceed the inherited ceiling is refused before anything starts, with ceiling.refuse. A parentless swarm carries its own approvals into its members' ceilings.
  • herd: startSession takes extra env; herd start --env KEY=VALUE; the manifest records approvals truthfully (a pane started with the engine's bypass flag is agent: true); herd ps groups by fleet and swarm and marks bypass; herd serve therefore withholds swarm panes unless --expose-autonomous.
  • moshcode fleet open|cap|tree|stop|log, the sysop tool over $OPENFLEET_HOME, sharing the file format with logicsrc fleet. tree joins the herd roster and enforces deadlines and budgets; stop ends a swarm as one unit through herd kill.
  • Wiring: CLI dispatch, pit /fleet, command schema, completion, moshscript verb, README table regenerated, PRD 0016, PRD 0015 naming updated. Version 0.98.2 to 0.99.0.

Verification. node --test whole suite 0 fail (3,035 tests before the fix round), fleet --help and every verb's help exit 0, README table identical to help --markdown, an end-to-end run of the verbs against a temp home, and a cross-tool interop run in which this branch's runSwarm wrote a two-piece swarm through fake engines and both moshcode fleet tree and logicsrc fleet tree rendered the identical tree. Three adversarial review rounds; every blocker and major fixed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV

ralyodio and others added 3 commits September 13, 2026 10:07
…`moshcode fleet` reads it (PRD 0016)

Implements the moshcode side of the OpenFleet 0.1 spec (logicsrc.com/docs/openfleet).

src/openfleet.mjs: the vendored IO over $OPENFLEET_HOME, read on every call like
herdDir(): records (written once at 0600, never overwritten), the append-only
per-fleet ledger with per-host merge by `at`, claim detection, the end line that
counts, the ceiling merge and every narrower case, checkCeiling refusals, context()
(own record, else OPENFLEET_FLEET, else current, else the implicit fleet), the
<slug>-<HHMM> swarm id, and fold() plus renderTree() for the tree.

src/swarm.mjs: the swarm id is minted before the plan; the planner, the skeptic and
the synthesis run with OPENFLEET_SWARM omitted; the planner's `files` becomes
piece.owns; the effective ceiling (parent's chain, narrowed by --agents and
--timeout) is checked before anything is written, a refusal appends ceiling.refuse
and starts nothing; swarm.spawn precedes the first start; one unclaimed record per
member, panes named after member ids, the four OPENFLEET_* variables plus the home
in the pane env; member.start on submit for a pane that has none; member.end lines,
then one swarm.end (synthesis as summary, --verify as verdict), then the kills in a
finally; --keep writes no end lines and names the swarm. --json carries swarm and
fleet.

src/herd.mjs, src/herd-cli.mjs: startSession extraEnv on both substrates, kept
through the strip; `herd start --env KEY=VALUE`; carriesBypass() so the manifest,
the JSON and the warning tell the truth about a pane started with the engine's
bypass flags; roster rows carry fleet, swarm, member and approvals; `herd ps`
groups by fleet then swarm and marks bypass; restore hands the env back.

src/fleet-cli.mjs: `moshcode fleet open|cap|tree|stop|log`, every verb --json.
open and cap refuse with exit 4 when OPENFLEET_MEMBER is set; stop refuses outside
what that member spawned, ends nested swarms first, members through their engines
(herd kill, `claude stop`, a pid), then one swarm.end; tree joins the herd roster,
draws recordless sessions as implicit-fleet roots and writes member.end lost for a
claimed pane the roster dropped; cap stops members above the new ceiling.

Wiring: bin dispatch, pit /fleet, schema entry + FLEET_VERBS + pit entry,
moshscript fleet(), README table regenerated plus a fleet section, PRD 0016, PRD
0015 R3/R6 naming, version 0.99.0.

Tests: test/openfleet.test.mjs, test/fleet-cli.test.mjs, and the swarm, herd and
herd-cli suites extended; every fleet test runs under a mkdtemp OPENFLEET_HOME.
Suite: 3035 tests, 0 fail (was 2980).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
…rule 6 in tree, honest swarm.end

The review of the two OpenFleet implementations found the two sysop tools
disagreeing on the same ledger. This is moshcode's half of the fix round,
under the shared rulings R-A to R-J.

The ceiling (R4, R5, R7, R11, R18, R1, R16, R-A, R-B, R-C):
- A fleet.cap never opens a fleet; `opened` is true only for fleet.open. A cap
  on the implicit fleet that names no approvals leaves each root's own in
  place, so `moshcode fleet cap anthony@dev --depth 2` no longer stops every
  bypass root.
- effectiveCeiling is base (latest fleet-target cap, else fleet.open, else
  implicit), then every swarm.spawn on the path, then the latest cap on any
  swarm on the path, applied last. The record's copy is a snapshot and never
  an input, so a later widening fleet cap takes effect. mergeCeiling never
  widens and carries unknown keys through.
- rootOf walks records, then member.start lines, and a broken chain reads
  native. A parentless record supplies its own approvals.
- `moshcode swarm` writes the member's own approvals into ceiling.approvals
  for a swarm the sysop runs by hand, so logicsrc's hook no longer refuses the
  pane's first prompt; each prompt's wait is clamped to an inherited until.

The ledger (R28, R-G): member.start, member.end and swarm.end take a
once-marker under fleets/<fleet>/marks/<event>.<id> (0600, dir 0700) before
appending; EEXIST writes nothing and reports already. lost takes
member.end.<id>.lost so a real end still supersedes it. Same paths as logicsrc.

The verbs (R2, R8, R12, R13, R19, R20, R26, R37, R-D, R-E, R-F, R-H):
- stop writes swarm.end only when every started member and every nested swarm
  has an end line; a member its engine would not end gets no end line, the
  verb exits 3 and says the swarm is left open. A never-started member
  writes nothing.
- A claude-code member is stopped by its job id: the 8-hex member id, else
  the first eight of a session UUID; an interactive session has none and the
  tool says so.
- lost is written only for a member the herd roster can hold (moshcode/*,
  tmux, on this host) when the manifest is readable; never for claude-code.
- tree enforces rule 6: a working member past its effective until is stopped
  with member.end timeout, members under a swarm or fleet whose summed spend
  in the budget's unit has reached the budget with member.end budget, then
  swarm.end once the swarm is complete.
- The implicit fleet is injectable, so the stop --fleet test holds on CI.

Also: tree prints `working`, keeps the herd's state out of the state column
(a [gone] mark instead), quotes and clips a task the way logicsrc does, and
pads columns per fleet (R24); a blocked pane is not `done` (R38); herd restore
hands a fresh session only OPENFLEET_HOME and OPENFLEET_FLEET (R39); the serve
flag text says swarm members are withheld too (R29); no em dashes in swarm.mjs
(R30, R40). PRD 0016 documents the lost rule, the markers and enforcement.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
Comment thread src/cli-schema.mjs
],
seeAlso: ["swarm", "herd", "ps"],
note: "the records and the ledger live under $OPENFLEET_HOME (default ~/.openfleet), the same files `logicsrc fleet` and claude code read. "
+ "open and cap are the sysop's alone and refuse when OPENFLEET_MEMBER is set, because a process carrying it is an agent; "
Comment thread src/cli-schema.mjs
],
note: "on a fleet the keys given are the whole new ceiling; on a swarm they must narrow what it has. members already above the new "
+ "ceiling, a bypass member under a now-native ceiling or one on a now-forbidden host, are stopped with member.end stopped. "
+ "refuses with exit 4 when OPENFLEET_MEMBER is set." },
Comment thread src/cli-schema.mjs
flags: [["--fleet <id>", "stop every swarm and root member of this fleet", ""], ["--json", "machine-readable", ""]],
note: "each member ends through its own engine: a moshcode pane through the herd, a claude code job through `claude stop <job id>` "
+ "(the 8-hex id; an interactive claude session has none and is reported), a `claude -p` through its pid; one it cannot reach is "
+ "reported, never faked, and its swarm stays open until it has an end line. an agent (OPENFLEET_MEMBER set) may stop only a swarm "
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

7 finding(s) in the 19 file(s) this pull request changes.

MEDIUM: 7

Severity Rule Location
MEDIUM sql-string-concatenation src/cli-schema.mjs:161
MEDIUM sql-string-concatenation src/cli-schema.mjs:218
MEDIUM sql-string-concatenation src/cli-schema.mjs:590
MEDIUM sql-string-concatenation src/cli-schema.mjs:623
MEDIUM sql-string-concatenation src/cli-schema.mjs:730
MEDIUM sql-string-concatenation src/cli-schema.mjs:1474
MEDIUM sql-string-concatenation src/cli-schema.mjs:1492
90 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 8 | **MEDIUM**: 71 | **LOW**: 11

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:86
HIGH sh-remote-script-execution install.sh:90
HIGH sh-remote-script-execution install.sh:258
HIGH sh-remote-script-execution install.sh:269
HIGH sh-remote-script-execution install.sh:275
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:153
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:179
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:373
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:377
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:422
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:671
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:867
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:869
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:928
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:974
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1044

…and 70 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

…is machine's

The harness passes implicit: anthony@dev; comparing against the real user@host only passed on the dev box and failed on CI as runner@<vm>.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV
@ralyodio
ralyodio merged commit 7c27afa into main Sep 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants