mirror of
https://github.com/profullstack/logicsrc.git
synced 2026-10-01 20:33:50 +00:00
* OpenFleet reference implementation: @logicsrc/openfleet 0.1.0 and logicsrc fleet Ship what docs/openfleet.md describes. The new workspace package holds the record (write once, never overwrite, 0600), the ledger (append-only JSON Lines, merged across ledger*.jsonl by at), the ceiling rules (whole fleet ceiling, narrowed swarm keys, a merge that never widens, refusals by key), claiming and deriving exactly as the spec's "Claiming and deriving" and rule 13, and fold(), which turns any $OPENFLEET_HOME plus the engine rosters into the tree the landing page shows. logicsrc fleet open|cap|tree|stop|log are the sysop's verbs, every one with --json. open and cap exit 4 when OPENFLEET_MEMBER is set; stop exits 4 outside the caller's subtree, ends nested swarms first, goes through each member's own engine (claude stop, moshcode herd kill, tmux kill-pane, a signal for claude-p) and writes one swarm.end per swarm. tree reads claude agents --json --all and ~/.moshcode/herd/sessions.json when it can, draws recordless sessions as roster roots of the implicit fleet, and writes member.end lost for a recorded member its engine no longer lists. Claude Code takes part through hooks: logicsrc fleet hooks install merges SessionStart, UserPromptSubmit, PreToolUse, Stop and SessionEnd into ~/.claude/settings.json without clobbering it, and logicsrc fleet hook <Event> runs each one. SessionStart claims, derives or writes a root record and hands the member its variables through CLAUDE_ENV_FILE; UserPromptSubmit checks the ceiling with the permission mode the engine reports and writes member.start, or refuses the first prompt with exit 2 and ceiling.refuse; PreToolUse denies an edit outside piece.owns; Stop and SessionEnd write member.end. A hand-started root takes the engine's reported approvals before member.start, since the command line only guesses them. Hooks never fail the engine: everything is caught and logged to hooks.log. The spec and the landing page now say what ships, keep Status 0.1, and record the two verified Claude Code limits: a background job dispatched from claude agents gets no launcher environment, and OPENFLEET_* exported at SessionStart reach the member's tools but not later hooks, so hooks key on session_id through $OPENFLEET_HOME/sessions/<session_id>.json. PRD 0008 covers the work. CLI 0.2.1 -> 0.3.0; build and build:cli chains build the package before the CLI; README and docs/cli.md list the group. Tests: 95 in the package (record, ledger merge, every narrower case, the worked example's claim and derive, the folded tree, hook install idempotence, each hook handler including the exit-2 refusal and the PreToolUse deny, every verb with fake deps) and 4 in the CLI. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV * OpenFleet fix round: rebuild the ceiling from the ledger, once-markers, rule 6 in tree, lost only for what a roster can hold The review of the reference implementation against moshcode found the two readers disagreeing on the same files. This round applies the shared rulings so both sides read a ledger the same way. Ceiling (R-A, R-B, R-C, R1, R6, R10, R15, R17): memberCeiling rebuilds the effective ceiling from the ledger on every read. The latest fleet-target fleet.cap (else fleet.open, else the implicit fleet's) replaces the copy in a record, so a sysop's widening cap reaches running members; then each swarm.spawn narrowing down the path, then swarm caps last. In the implicit fleet a parentless record's own approvals enters at the root; a ceiling a writer left without the key is never read as native, and startMember fills it with the engine's word while the record is unclaimed. A fleet.open or cap with no hosts means the host it was written on (R23). Once-markers (R-G, R28): member.start, member.end and swarm.end each take an exclusive create under fleets/<fleet>/marks/<event>.<id> before the append; a lost end takes <id>.lost so a real end can still supersede it. The hooks let a real end follow a lost line (R9). tree (R-F, R20): run by the sysop it enforces rule 6, stopping a member past its effective until with state timeout and the members of a swarm or fleet at its budget with state budget, then writes swarm.end for each swarm touched once it is complete. An agent's tree stops nothing. lost is written only for a member its engine's roster can hold: a claude-code background job (8-hex member or session) or a moshcode pane, never an interactive session claude agents does not list (R-E, R3, R14). A nested swarm is drawn under the member that spawned it and its row shows the effective ceiling (R25). stop and cap (R-D, R-H, R22, R27): swarm.end is written only once every member and every nested swarm has an end line that counts; an engine that will not end a member leaves it without an end line and the verb exits non-zero. claude stop takes the job id: the member of a background job, else the first eight characters of a session UUID; an interactive session with no job id cannot be stopped and the tool says so. cap on a swarm refuses a key that would widen. A derived claude-code job is named by its job id and carries no pid. Also: R-I (endMember ends only the engine-minted swarm of one), R35 (a derived record's guessed approvals corrected at UserPromptSubmit), R32 (the UserPromptSubmit hook passes only exit 2 through), R31 (package README), R36 (rule 13 says the launcher test is unimplemented in 0.1), docs and PRD 0008 updated for lost, rule 6 and the markers. 113 openfleet tests, 93 CLI tests, contract green. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV * openfleet hooks: no member.end for a member that never started A first prompt refused by the ceiling still lets the session wind down through Stop and SessionEnd; those handlers now write nothing when the ledger holds no member.start for the member, so a refused member is never drawn as done. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV * logicsrc-mcp test: the next free PRD id is 0009 now that PRD 0008 exists Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RZV4zJ2pDZLNN3kE5jFCmV --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
116 lines
4.9 KiB
Markdown
116 lines
4.9 KiB
Markdown
# @logicsrc/openfleet
|
|
|
|
Reference implementation of [OpenFleet](https://logicsrc.com/docs/openfleet),
|
|
the record an agent session carries about where it sits: which human answers
|
|
for it, who spawned it, for what task, at what depth, and under what ceiling.
|
|
A fleet is every agent session one human, its sysop, is answerable for. A
|
|
swarm is the set of sessions one spawner starts to do one task.
|
|
|
|
This package holds the record, the ledger, the ceiling rules, claiming and
|
|
deriving, the folded tree, the five sysop verbs, and the Claude Code hooks.
|
|
moshcode writes the same files from `moshcode swarm` and reads them with
|
|
`moshcode fleet`; one tree shows both engines.
|
|
|
|
## Install
|
|
|
|
```bash
|
|
npm install -g @logicsrc/cli
|
|
logicsrc fleet --help
|
|
```
|
|
|
|
The verbs live in this package and the umbrella CLI wraps them as
|
|
`logicsrc fleet`. To use the library directly:
|
|
|
|
```bash
|
|
npm install @logicsrc/openfleet
|
|
```
|
|
|
|
## The files
|
|
|
|
Everything lives under `$OPENFLEET_HOME`, default `~/.openfleet`:
|
|
|
|
| Path | What |
|
|
| --- | --- |
|
|
| `fleets/<fleet>/members/<member>.json` | One record per member, written before it starts, never changed after `member.start`. |
|
|
| `fleets/<fleet>/ledger.jsonl` | The append-only ledger for this host. `ledger.<host>.jsonl` copies from other hosts are merged by `at`. |
|
|
| `fleets/<fleet>/marks/<event>.<id>` | Once-markers for `member.start`, `member.end` and `swarm.end`, so two writers never double a line. |
|
|
| `current` | The fleet the account's next root member joins. Absent means the implicit `<user>@<host>`. |
|
|
| `sessions/<session_id>.json` | The Claude Code hooks' own lookup, not part of the spec. |
|
|
|
|
Files are 0600 and directories 0700.
|
|
|
|
## The five verbs
|
|
|
|
```bash
|
|
# Sysop only: mint a fleet, set its whole ceiling, write fleet.open and current.
|
|
logicsrc fleet open team --approvals bypass --budget "20 USD" --depth 2 --fan-out 4 --hosts dev,netcup --until 2h
|
|
|
|
# Sysop only: set a fleet's whole ceiling, or narrow a running swarm's. Members now above it are stopped.
|
|
logicsrc fleet cap team-20260913 --approvals native
|
|
logicsrc fleet cap create-two-0541 --fan-out 2
|
|
|
|
# Anyone: the tree, from the ledger, the records and the engine rosters. Run by the sysop it also
|
|
# stops what is past its deadline or over its budget (rule 6) and marks lost what its engine no longer lists.
|
|
logicsrc fleet tree
|
|
logicsrc fleet tree anthony@dev --json
|
|
|
|
# Anyone, within reach: end a member, a swarm (nested swarms first), or a whole fleet through each member's engine.
|
|
logicsrc fleet stop create-two-0541
|
|
logicsrc fleet stop team-20260913 --fleet
|
|
|
|
# Anyone: the ledger, in order, with who did it.
|
|
logicsrc fleet log --swarm create-two-0541
|
|
logicsrc fleet log --since 2h --json
|
|
```
|
|
|
|
Every verb takes `--json`. `open` and `cap` refuse with exit 4 when the
|
|
process carries `OPENFLEET_MEMBER`: that process is an agent. `stop` refuses
|
|
outside the caller's own subtree the same way. An engine that will not end a
|
|
member leaves it without an end line and the verb exits 3.
|
|
|
|
`stop` goes through the member's own engine: `claude stop <job id>` for
|
|
`claude-code`, `moshcode herd kill` for `moshcode/*`, `tmux kill-pane` for
|
|
`tmux`, a signal to the pid for `claude-p`.
|
|
|
|
## Claude Code hooks
|
|
|
|
```bash
|
|
logicsrc fleet hooks install # merges five entries into ~/.claude/settings.json, never clobbers
|
|
logicsrc fleet hooks status
|
|
logicsrc fleet hooks remove # takes out only ours
|
|
```
|
|
|
|
With the hooks installed every Claude Code session becomes a recorded member:
|
|
SessionStart claims the record `OPENFLEET_RECORD` names, derives a child under
|
|
a claimed one, or writes a root record; UserPromptSubmit checks the ceiling
|
|
with the permission mode the engine reports and writes `member.start`, or
|
|
refuses the first prompt with exit 2; PreToolUse denies an Edit or Write
|
|
outside `piece.owns`; Stop and SessionEnd write `member.end`. A hook never
|
|
fails the engine. `logicsrc fleet hook <Event>` is the entry point the
|
|
settings file calls.
|
|
|
|
## Environment
|
|
|
|
| Variable | Meaning |
|
|
| --- | --- |
|
|
| `OPENFLEET_HOME` | The root directory. Default `~/.openfleet`. |
|
|
| `OPENFLEET_RECORD` | The absolute path of this member's record. |
|
|
| `OPENFLEET_FLEET` | A copy of the record's `fleet`; in the sysop's shell, the fleet new roots join. |
|
|
| `OPENFLEET_MEMBER` | A copy of the record's `member`. Present means this process is an agent. |
|
|
| `OPENFLEET_SWARM` | A copy of the record's `swarm`, when it carries one. |
|
|
|
|
## Library
|
|
|
|
```js
|
|
import { context, claimOrDerive, startMember, endMember, fold, renderTree, readLedger } from "@logicsrc/openfleet";
|
|
import { registerOpenFleetCommands } from "@logicsrc/openfleet/commands";
|
|
```
|
|
|
|
`context(env)` answers which fleet and which member a process is. `claimOrDerive`
|
|
is the engine-side rule from the spec's "Claiming and deriving". `fold` builds
|
|
the tree `tree` renders. `registerOpenFleetCommands(command, deps)` mounts the
|
|
verbs on a commander command with every world-touching dependency injectable.
|
|
|
|
## License
|
|
|
|
MIT. The specification text is CC BY 4.0.
|