@zerwiz/ymir 0.1.16 → 0.1.18
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/.agents/config/app-repos.yaml +38 -0
- package/.claude/settings.json +31 -0
- package/.codex/hooks.json +43 -0
- package/.cursor/hooks.json +33 -0
- package/.pi/extensions/README.md +39 -0
- package/.pi/extensions/gna-pi-watch.ts +15 -0
- package/.pi/extensions/lib/ro-assistant-layout.ts +96 -0
- package/.pi/extensions/lib/ro-operational-user-layout.ts +145 -0
- package/.pi/extensions/lib/ro-visibility.ts +99 -0
- package/.pi/extensions/lib/ro-working-ship.ts +243 -0
- package/.pi/extensions/lib/rodd-operational-input.ts +62 -0
- package/.pi/extensions/lib/skuld-branch-dispatch.ts +252 -0
- package/.pi/extensions/lib/skuld-branch-model-picker.ts +77 -0
- package/.pi/extensions/lib/vordr-sessionstart-supervisor.mjs +48 -0
- package/.pi/extensions/ro.ts +11 -0
- package/.pi/extensions/skuld-branch-supervision.ts +11 -0
- package/.pi/extensions/syn-turnend-guard.ts +11 -0
- package/.pi/mcp.json +16 -0
- package/.pi/mcp.json.example +11 -0
- package/.pi/npm/.gitignore +2 -0
- package/.pi/settings.json +8 -0
- package/.pi/shared/extensions/gna-pi-watch.ts +750 -0
- package/.pi/shared/extensions/herdr-agent-state.ts +257 -0
- package/.pi/shared/extensions/open-editor.ts +509 -0
- package/.pi/shared/extensions/ro.ts +504 -0
- package/.pi/shared/extensions/skuld-branch-supervision.ts +1535 -0
- package/.pi/shared/extensions/syn-turnend-guard.ts +683 -0
- package/.pi/shared/extensions/todo.ts +133 -0
- package/.pi/shared/extensions/ymir-subagents.ts +287 -0
- package/.pi/shared/extensions/ymir-well.ts +130 -0
- package/CHANGELOG.md +23 -0
- package/assets/Ymir.md +4654 -0
- package/assets/healer.html +717 -0
- package/assets/icon-family/family-sheet.png +0 -0
- package/assets/icon-family/hlidskjalf.svg +1 -0
- package/assets/icon-family/sessrumnir.svg +1 -0
- package/assets/icon-family/smidja.svg +1 -0
- package/assets/icon-family/ymir.svg +1 -0
- package/assets/mimirswell.html +880 -0
- package/assets/mock/README.md +16 -0
- package/assets/mock/feeds.ts +175 -0
- package/assets/mock/mock.ts +408 -0
- package/assets/reference/docs/agent-control.md +122 -0
- package/assets/reference/docs/apodex/Appdex_as_Orchestrator_Planner_Reviewer_Assessment.md +176 -0
- package/assets/reference/docs/apodex/Final Report(1).md +198 -0
- package/assets/reference/docs/apodex/Final Report(2).md +54 -0
- package/assets/reference/docs/apodex/Final Report(3).md +40 -0
- package/assets/reference/docs/apodex/Final Report(4).md +234 -0
- package/assets/reference/docs/apodex/Final Report(5).md +13 -0
- package/assets/reference/docs/apodex/Final Report.md +82 -0
- package/assets/reference/docs/architecture.md +392 -0
- package/assets/reference/docs/arm-pretool-check.md +247 -0
- package/assets/reference/docs/calm-mode-feasibility.md +541 -0
- package/assets/reference/docs/calm.md +59 -0
- package/assets/reference/docs/captain-hold-lifecycle.md +96 -0
- package/assets/reference/docs/cd-guard.md +165 -0
- package/assets/reference/docs/cmux-backend.md +133 -0
- package/assets/reference/docs/codex-app-backend.md +57 -0
- package/assets/reference/docs/configuration.md +924 -0
- package/assets/reference/docs/documentation-audiences.json +468 -0
- package/assets/reference/docs/documentation-audiences.md +28 -0
- package/assets/reference/docs/examples/crew-dispatch.json +26 -0
- package/assets/reference/docs/examples/process-event-extension/file-signal.mjs +96 -0
- package/assets/reference/docs/examples/process-event-extension/firstmate-extension.json +15 -0
- package/assets/reference/docs/examples/watched-tools.json +24 -0
- package/assets/reference/docs/examples/wedge-alarm +22 -0
- package/assets/reference/docs/extension-bindings.md +237 -0
- package/assets/reference/docs/fm-test-isolation-proof.json +37 -0
- package/assets/reference/docs/fm-test-isolation-proof.md +149 -0
- package/assets/reference/docs/fm-test-portable-shards.md +118 -0
- package/assets/reference/docs/gitlab-merge-watch.md +275 -0
- package/assets/reference/docs/herdr-backend.md +346 -0
- package/assets/reference/docs/orca-backend.md +84 -0
- package/assets/reference/docs/pi-supervision-branch-poster.svg +125 -0
- package/assets/reference/docs/pi-supervision-branch.md +96 -0
- package/assets/reference/docs/plans/agentic-engineering-workflow.md +395 -0
- package/assets/reference/docs/plans/agentic-factory-implementation-plan.md +615 -0
- package/assets/reference/docs/plans/command-factory-enhancement-plan.md +420 -0
- package/assets/reference/docs/plans/phase-1-layer3-program-design.md +134 -0
- package/assets/reference/docs/plans/phase-2-vertical-slices.md +100 -0
- package/assets/reference/docs/plans/phase-3-measurable-goals.md +106 -0
- package/assets/reference/docs/plans/phase-4-context-budget.md +106 -0
- package/assets/reference/docs/plans/phase-5-pre-mortem-confidence.md +100 -0
- package/assets/reference/docs/plans/phase-6-incident-to-agent.md +109 -0
- package/assets/reference/docs/remote-secondmates.md +268 -0
- package/assets/reference/docs/scripts.md +146 -0
- package/assets/reference/docs/sessionstart-nudge.md +115 -0
- package/assets/reference/docs/subagent-guard.md +383 -0
- package/assets/reference/docs/supervision-protocols/claude.md +27 -0
- package/assets/reference/docs/supervision-protocols/codex.md +16 -0
- package/assets/reference/docs/supervision-protocols/cursor.md +31 -0
- package/assets/reference/docs/supervision-protocols/grok.md +39 -0
- package/assets/reference/docs/supervision-protocols/opencode.md +17 -0
- package/assets/reference/docs/supervision-protocols/pi.md +33 -0
- package/assets/reference/docs/supervision-protocols/unknown.md +13 -0
- package/assets/reference/docs/tmux-backend.md +115 -0
- package/assets/reference/docs/trace-context.md +117 -0
- package/assets/reference/docs/turnend-guard.md +170 -0
- package/assets/reference/docs/verification/dispatch-auth.md +204 -0
- package/assets/reference/docs/verification/muse.md +221 -0
- package/assets/reference/docs/verification/process-event-sources.md +208 -0
- package/assets/reference/docs/verification/public-followup.md +119 -0
- package/assets/reference/docs/verification/runtime-backends.md +1022 -0
- package/assets/reference/docs/verification/stow-memory.md +53 -0
- package/assets/reference/docs/verification/supervision.md +534 -0
- package/assets/reference/docs/verification/trace-context.md +31 -0
- package/assets/reference/docs/voice-relay.md +295 -0
- package/assets/reference/docs/watcher-continuity.md +119 -0
- package/assets/reference/docs/wedge-alarm.md +39 -0
- package/assets/reference/docs/zellij-backend.md +114 -0
- package/assets/reference/index.html +1172 -0
- package/assets/reference/skills/afk/SKILL.md +241 -0
- package/assets/reference/skills/ahoy/SKILL.md +56 -0
- package/assets/reference/skills/ask-user-authority/SKILL.md +56 -0
- package/assets/reference/skills/bearings/SKILL.md +161 -0
- package/assets/reference/skills/bearings/assets/board-template.html +731 -0
- package/assets/reference/skills/bootstrap-diagnostics/SKILL.md +68 -0
- package/assets/reference/skills/captain-hold-lifecycle/SKILL.md +54 -0
- package/assets/reference/skills/decision-hold-lifecycle/SKILL.md +15 -0
- package/assets/reference/skills/diagnostic-reasoning/SKILL.md +53 -0
- package/assets/reference/skills/firstmate-codexapp/SKILL.md +110 -0
- package/assets/reference/skills/firstmate-codexapp/agents/openai.yaml +4 -0
- package/assets/reference/skills/firstmate-coding-guidelines/SKILL.md +127 -0
- package/assets/reference/skills/firstmate-orca/SKILL.md +92 -0
- package/assets/reference/skills/fmx-respond/SKILL.md +283 -0
- package/assets/reference/skills/harness-adapters/SKILL.md +95 -0
- package/assets/reference/skills/harness-adapters/references/common/control-and-recovery.md +37 -0
- package/assets/reference/skills/harness-adapters/references/common/dispatch.md +32 -0
- package/assets/reference/skills/harness-adapters/references/common/model-and-effort.md +42 -0
- package/assets/reference/skills/harness-adapters/references/common/primary-hooks.md +40 -0
- package/assets/reference/skills/harness-adapters/references/harness/claude.md +55 -0
- package/assets/reference/skills/harness-adapters/references/harness/codex.md +43 -0
- package/assets/reference/skills/harness-adapters/references/harness/cursor.md +75 -0
- package/assets/reference/skills/harness-adapters/references/harness/grok.md +69 -0
- package/assets/reference/skills/harness-adapters/references/harness/kimi.md +51 -0
- package/assets/reference/skills/harness-adapters/references/harness/muse.md +70 -0
- package/assets/reference/skills/harness-adapters/references/harness/opencode.md +42 -0
- package/assets/reference/skills/harness-adapters/references/harness/pi.md +56 -0
- package/assets/reference/skills/process-event-sources/SKILL.md +137 -0
- package/assets/reference/skills/project-management/SKILL.md +92 -0
- package/assets/reference/skills/quota-array-dispatch/SKILL.md +131 -0
- package/assets/reference/skills/secondmate-provisioning/SKILL.md +254 -0
- package/assets/reference/skills/stow/SKILL.md +309 -0
- package/assets/reference/skills/stuck-crewmate-recovery/SKILL.md +54 -0
- package/assets/reference/skills/updatefirstmate/SKILL.md +64 -0
- package/assets/skills/README.md +5 -0
- package/assets/yggdrasil.html +649 -0
- package/assets/ymir-banner-01.png +0 -0
- package/assets/ymir-banner-02.png +0 -0
- package/assets/ymir-banner-03.png +0 -0
- package/assets/ymir-banner-04.png +0 -0
- package/assets/ymir-banner-05.png +0 -0
- package/assets/ymir-banner-06.png +0 -0
- package/assets/ymir-emblem-darksteel.svg +63 -0
- package/assets/ymir-emblem-forged.svg +55 -0
- package/assets/ymir-emblem-slate.svg +55 -0
- package/assets/ymir-mark-algiz-anvil.svg +9 -0
- package/assets/ymir-stave.svg +53 -0
- package/assets/ymir.html +563 -0
- package/bin/ymir-install.sh +6 -1
- package/data/learnings.md +33 -0
- package/data/local-models.md +34 -0
- package/data/operator.md +15 -0
- package/data/projects.md +28 -0
- package/data/realm.md +1 -0
- package/deploy/Containerfile +27 -0
- package/deploy/README.md +104 -0
- package/deploy/compose/compose.yaml +45 -0
- package/deploy/env.example +30 -0
- package/deploy/quadlet/ymir.container +57 -0
- package/package.json +10 -8
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
# Runtime backend verification
|
|
2
|
+
|
|
3
|
+
Audience: maintainer verification.
|
|
4
|
+
|
|
5
|
+
This record contains reusable version-scoped evidence for active runtime guarantees.
|
|
6
|
+
The backend guides own current setup, safety boundaries, and limitations.
|
|
7
|
+
Exact task chronology, branch names, temporary homes, local paths, process ids, thread ids, and delivery transcripts remain in private reports or PR evidence.
|
|
8
|
+
|
|
9
|
+
## tmux
|
|
10
|
+
|
|
11
|
+
Foreground-process behavior was verified on 2026-07-07 with tmux 3.6a on macOS.
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
tmux new-session -d -s fmtest -n testwin
|
|
15
|
+
tmux display-message -p -t fmtest:testwin '#{pane_current_command}'
|
|
16
|
+
tmux send-keys -t fmtest:testwin 'sleep 30' Enter
|
|
17
|
+
tmux display-message -p -t fmtest:testwin '#{pane_current_command}'
|
|
18
|
+
tmux send-keys -t fmtest:testwin C-c
|
|
19
|
+
tmux display-message -p -t fmtest:testwin '#{pane_current_command}'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Observed output:
|
|
23
|
+
|
|
24
|
+
```text
|
|
25
|
+
zsh
|
|
26
|
+
sleep
|
|
27
|
+
zsh
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
A persistent parent shell waiting for a child remained reported as the parent process, while a shell that directly execed a simple command changed identity with the process itself.
|
|
31
|
+
Pi and pi-signed 0.82.0 were reverified on 2026-07-27 through real isolated `fm-spawn.sh` launches.
|
|
32
|
+
|
|
33
|
+
### Agent liveness name sources
|
|
34
|
+
|
|
35
|
+
The earlier record that every harness is observed under its own `#{pane_current_command}` no longer holds and has been replaced by the per-harness evidence below.
|
|
36
|
+
In this macOS run that reading reflected a rewritable process title rather than stable executable identity, so it is now one of two independent name sources rather than the sole basis of a verdict.
|
|
37
|
+
|
|
38
|
+
The seven primary-capable adapters were relaunched on 2026-08-03 with tmux 3.6a on macOS 26.5.2 arm64, each on a private socket in an isolated lab.
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
tmux -L "$socket" new-window -d -t "$session:" -n "$harness" -c "$wt" -- "$bin"
|
|
42
|
+
tmux -L "$socket" display-message -p -t "$session:$harness" '#{pane_current_command}'
|
|
43
|
+
ps -t "${tty#/dev/}" -o pgid=,tpgid=,comm= # rows where pgid = tpgid
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Observed identities, and the resulting verdict:
|
|
47
|
+
|
|
48
|
+
| Harness | Version | `#{pane_current_command}` | Foreground `comm` | Verdict |
|
|
49
|
+
| --- | --- | --- | --- | --- |
|
|
50
|
+
| claude | 2.1.220 | `2.1.220` | `claude` | alive |
|
|
51
|
+
| codex | codex-cli 0.146.0 | `codex` | `codex` | alive |
|
|
52
|
+
| opencode | 1.18.11 | `opencode` | `opencode` | alive |
|
|
53
|
+
| pi | 0.82.0 | `pi-launcher` | `pi-signed`, `pi` | alive |
|
|
54
|
+
| pi-signed | 0.82.0 | `pi-launcher` | `pi-signed`, `pi` | alive |
|
|
55
|
+
| grok | 0.2.118 | `grok-0.2.118-ma` | `grok` | alive |
|
|
56
|
+
| kimi | 0.31.1 | `kimi` | `kimi` | alive |
|
|
57
|
+
|
|
58
|
+
Claude Code is the harness whose title no longer attributes it at all; every other adapter is currently attributed by both sources.
|
|
59
|
+
Codex reported `codex-aarch64-a` at 0.145.0 and `codex` at 0.146.0, and Kimi Code reported `kimi-code` as its foreground `comm` at 0.29.1 and `kimi` at 0.31.1, so these identities move between ordinary patch releases in both directions.
|
|
60
|
+
That is the evidence for treating any single process name as a surface under vendor control rather than a stable contract.
|
|
61
|
+
|
|
62
|
+
The crewmate-only Muse Code 0.1.0-R708.1 adapter was verified separately on 2026-08-05 against tmux on macOS arm64.
|
|
63
|
+
Its installed `muse-bin-0.1.0-R708.1` foreground identity classified `alive`, while `musescore`, `amuse`, `muse-binary`, and `muse-bind` remained ambiguous in the portable regression.
|
|
64
|
+
[`muse.md`](muse.md#process-identity) owns the artifact identity and launcher evidence for that verification.
|
|
65
|
+
|
|
66
|
+
Bounded observed output:
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
foreground comms:
|
|
70
|
+
zsh
|
|
71
|
+
.../instbin/muse-bin-0.1.0-R708.1
|
|
72
|
+
classify each:
|
|
73
|
+
zsh -> shell
|
|
74
|
+
muse-bin-0.1.0-R708.1 -> agent
|
|
75
|
+
fm_backend_agent_state tmux museliv:zsh
|
|
76
|
+
alive
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`#{pane_current_command}` and foreground `ps -o comm=` read different name fields, but which one preserves executable identity is platform-dependent.
|
|
80
|
+
On macOS the pane command reflected the rewritable title while the full install path could survive in `ps -o comm=`; in the Linux portable regression those roles reversed for the version-named native executable, with the identifying path retained in argv[0].
|
|
81
|
+
The classifier therefore accepts a harness basename first, then an exact harness path component in the full executable path, then the same component in argv[0], without depending on which field carries it on a given platform.
|
|
82
|
+
|
|
83
|
+
The portable regression is CI-enforced, while the real-harness drift guard is opt-in under the policy in `.agents/skills/firstmate-coding-guidelines/SKILL.md`.
|
|
84
|
+
Run the live guard after any harness upgrade and before trusting or refreshing the table above:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
FM_HARNESS_LIVENESS_DRIFT=1 bin/fm-test-run.sh tests/fm-harness-liveness-drift-live-e2e.test.sh
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Bounded output from the run that produced the table:
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
ok - harness liveness: claude 2.1.220 (Claude Code) classifies alive
|
|
94
|
+
# claude 2.1.220 (Claude Code): title='2.1.220' foreground=[claude ]
|
|
95
|
+
# checked 7 installed harness(es)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Installed-wrapper checks:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
basename "$(command -v pi-signed)"
|
|
102
|
+
pi-signed --version
|
|
103
|
+
pi --version
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Observed bounded output:
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
pi-signed
|
|
110
|
+
0.82.0
|
|
111
|
+
0.82.0
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Harness-adapter instruction routing
|
|
115
|
+
|
|
116
|
+
Two checks keep the evidence boundaries separate.
|
|
117
|
+
`tests/fm-harness-adapter-references.test.sh` parses the router's declared JSON contract as normalized data and proves every selected reference is readable, which is structural evidence only.
|
|
118
|
+
`tests/fm-harness-adapter-instructions-live-e2e.test.sh` is an opt-in development check that sends the directly loaded router and every operation scenario across all nine harness identities to a local Ollama model, requires the generated plan as normalized JSON, and makes no external-provider call.
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
FM_HARNESS_ADAPTER_INSTRUCTION_EVAL=1 FM_HARNESS_ADAPTER_LOCAL_MODEL=ambient-router-gemma4:e4b bin/fm-test-run.sh tests/fm-harness-adapter-instructions-live-e2e.test.sh
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
That local evaluation demonstrates instruction-driven scenario selection, but it does not claim that a native harness loaded the selected files.
|
|
125
|
+
The guard prints the exact installed version or unavailable status for every native harness so absent tools and unexercised provider transports remain explicit rather than becoming passes.
|
|
126
|
+
Native loader behavior still requires the applicable live agent-tool check; no uniform deterministic zero-provider transport currently spans Claude, Codex, OpenCode, and Pi, and the other five tools remain unavailable where their binaries are absent.
|
|
127
|
+
|
|
128
|
+
Bounded output from the 2026-08-29 local run:
|
|
129
|
+
|
|
130
|
+
```text
|
|
131
|
+
ok - local model ambient-router-gemma4:e4b selected every operation scenario and all nine harness identities
|
|
132
|
+
# native loader not claimed: claude 2.1.220 (Claude Code) is installed, but this harness-neutral evaluation does not exercise its provider transport
|
|
133
|
+
# native loader not claimed: codex 0.147.0-alpha.6+local.4 is installed, but this harness-neutral evaluation does not exercise its provider transport
|
|
134
|
+
# native loader not claimed: opencode 1.14.48 is installed, but this harness-neutral evaluation does not exercise its provider transport
|
|
135
|
+
# native loader not claimed: pi 0.84.0 is installed, but this harness-neutral evaluation does not exercise its provider transport
|
|
136
|
+
# unverified native loader: pi-signed is not installed on this machine
|
|
137
|
+
# unverified native loader: grok is not installed on this machine
|
|
138
|
+
# unverified native loader: kimi is not installed on this machine
|
|
139
|
+
# unverified native loader: cursor is not installed on this machine
|
|
140
|
+
# unverified native loader: muse is not installed on this machine
|
|
141
|
+
# installed native tools recorded without overstating loader coverage: 4
|
|
142
|
+
# unavailable native tools: pi-signed grok kimi cursor muse
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The isolated process and endpoint checks used:
|
|
146
|
+
|
|
147
|
+
```sh
|
|
148
|
+
tmux display-message -p -t "$target" '#{pane_current_command}'
|
|
149
|
+
ps -o comm= -p "$wrapper_pid"
|
|
150
|
+
ps -o comm= -p "$engine_pid"
|
|
151
|
+
FM_HOME="$fixture_home" bin/fm-crew-state.sh "$task_id"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Observed bounded shapes:
|
|
155
|
+
|
|
156
|
+
```text
|
|
157
|
+
pi-launcher
|
|
158
|
+
.../pi-signed
|
|
159
|
+
.../Pi Launcher.app/Contents/Resources/pi/pi
|
|
160
|
+
state: done ...
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Both launches executed a submitted tool instruction and touched the generated `turn_end` marker.
|
|
164
|
+
The pi-signed launch retained `harness=pi-signed`, while the plain comparison retained `harness=pi`.
|
|
165
|
+
The exact wrapper ancestry was `pi-signed` parent to Pi engine child, and the plain Pi Launcher path also traversed the signed wrapper on this installation.
|
|
166
|
+
That shared plain-Pi path is retained as disconfirming evidence against using ancestry as runtime-selection authority.
|
|
167
|
+
Firstmate therefore sets the exact `FM_PI_HARNESS` selection marker on both worker launch paths, while an unmarked Pi-family process remains `pi`.
|
|
168
|
+
Both recorded runtime identities now classify the exact `pi-launcher` foreground command as `alive`.
|
|
169
|
+
|
|
170
|
+
Backend applicability was reviewed across every spawn adapter.
|
|
171
|
+
Tmux needs the exact `pi-launcher`, `pi-signed`, `pi`, and `Pi` process identities for recovery-grade liveness.
|
|
172
|
+
Herdr uses native registered-agent state and needs no process-name branch.
|
|
173
|
+
Zellij has no verified recovery-grade agent process probe, while Orca and cmux do not support secondmate spawns, so those three retain their existing generic ordinary-launch semantics without a new liveness matcher.
|
|
174
|
+
|
|
175
|
+
The current classifier matrix and its refresh guard are recorded in [Composer classification matrix](#composer-classification-matrix), with portable shape coverage in `tests/fm-composer-lib.test.sh` and `tests/fm-composer-ghost.test.sh`.
|
|
176
|
+
Kimi pointer delivery and OpenCode 1.18.4 busy-queue behavior remain pinned by `tests/fm-kimi-harness.test.sh`, `tests/fm-tmux-submit-busy.test.sh`, and `tests/fm-composer-lib.test.sh`.
|
|
177
|
+
Herdr's Claude idle-native submit confirmation is pinned by `tests/fm-backend-herdr.test.sh` and refreshed by `FM_HERDR_SUBMIT_CONFIRM_LIVE=1 tests/fm-herdr-submit-confirm-live-e2e.test.sh`.
|
|
178
|
+
|
|
179
|
+
### Cleanup endpoint identity
|
|
180
|
+
|
|
181
|
+
The cleanup identity boundary was validated on 2026-07-28 with tmux 3.6a and metadata fixtures for every supported backend.
|
|
182
|
+
|
|
183
|
+
```sh
|
|
184
|
+
tests/fm-teardown-endpoint-safety.test.sh
|
|
185
|
+
tests/fm-teardown.test.sh
|
|
186
|
+
tests/fm-backend-herdr.test.sh
|
|
187
|
+
tests/fm-backend-zellij.test.sh
|
|
188
|
+
tests/fm-backend-orca.test.sh
|
|
189
|
+
tests/fm-backend-cmux.test.sh
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Bounded output from the incident regression:
|
|
193
|
+
|
|
194
|
+
```text
|
|
195
|
+
ok - fm-teardown: missing, empty, malformed, ambiguous, and task-mismatched endpoints refuse before every mutation or runtime call
|
|
196
|
+
ok - cleanup identity: valid tmux, Herdr, Zellij, Orca, and cmux records validate while every empty backend target refuses
|
|
197
|
+
ok - tmux backend: direct empty target returns nonzero without invoking tmux
|
|
198
|
+
ok - process cleanup: creation-time PID identity removes only the exact child and preserves the control child
|
|
199
|
+
ok - fm-teardown: dedicated-socket invalid cleanup preserves target/control and valid cleanup removes only the exact target
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
The dedicated tmux cell removed ambient tmux variables, required a socket-bound wrapper, kept one target and one independent control window, and proved the wrapper was not called for invalid metadata or a direct empty target.
|
|
203
|
+
Valid cleanup removed only the exact task-bound target and left the control window live.
|
|
204
|
+
The metadata-only validation covers tmux, Herdr, Zellij, Orca, and cmux before backend dispatch.
|
|
205
|
+
Claude, Codex, OpenCode, Pi, pi-signed, Grok, Kimi, Cursor, and Muse share that backend cleanup boundary; their harness-specific hook files, tokens, transcript bindings, and session-log sidecars are cleaned only after it, so no harness needs a separate endpoint parser.
|
|
206
|
+
|
|
207
|
+
## Composer classification matrix
|
|
208
|
+
|
|
209
|
+
The shared composer classifier (`bin/fm-composer-lib.sh`, `fm_composer_classify_screen`) owns every composer shape fleet-wide; each backend contributes only a capture and a capability descriptor.
|
|
210
|
+
The live half of that guarantee was verified on 2026-08-10 from an already-trusted checkout at the branch's final validated head, against every installed harness then covered by the empty-composer matrix on tmux 3.6a, macOS arm64, on an isolated private socket, with no prompt submitted to any harness.
|
|
211
|
+
An earlier untrusted-worktree run left Claude, Grok, and Muse unverified because the guard treats first-launch trust dialogs as an unreadable-composer state and never confirms them; this trusted-checkout rerun supersedes those missing results.
|
|
212
|
+
|
|
213
|
+
```sh
|
|
214
|
+
FM_COMPOSER_MATRIX_LIVE=1 tests/fm-composer-matrix-live-e2e.test.sh
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Observed output:
|
|
218
|
+
|
|
219
|
+
```text
|
|
220
|
+
ok - claude (2.1.227 (Claude Code)): real idle composer classifies empty
|
|
221
|
+
ok - codex (codex-cli 0.146.0): real idle composer classifies empty
|
|
222
|
+
ok - opencode (1.14.46): real idle composer classifies empty
|
|
223
|
+
ok - pi (0.84.0): real idle composer classifies empty
|
|
224
|
+
ok - grok (grok 1.0.0 (3cd0d0cbcebe)): real idle composer classifies empty
|
|
225
|
+
# harness absent, not verified here: kimi
|
|
226
|
+
ok - muse (Muse Code 0.1.0 (0.1.0-R708.1)): real idle composer classifies empty
|
|
227
|
+
ok - strict posture live: a blank shell row classifies unknown and injection defers
|
|
228
|
+
ok - zellij (zellij 0.44.0): unrelated pane change never confirms delivery (verdict: unknown)
|
|
229
|
+
ok - live composer-matrix guard verified 8 live surface(s)
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
All six installed harnesses' real idle composers reached a proven `empty` (Claude auto-updated to 2.1.227 between the audit and this rerun, so the shipped classifier is proven against the newer release as well), including Pi through the tmux foreground-process identity probe, Grok through the titled-bottom-border tolerance, and OpenCode through the left-bar shape; Codex and OpenCode first parked on vendor update-available modals that the strict classifier correctly refused until the guard's single non-submitting Escape dismissed them.
|
|
233
|
+
The strict blank-row posture held live (a blank shell row deferred injection), and a zellij pane changing for reasons unrelated to submission never confirmed a delivery, replacing the retired content-diff heuristic's false positive.
|
|
234
|
+
Kimi was not installed on the verification machine; its bordered shape is pinned by the portable byte-capture regressions in `tests/fm-composer-lib.test.sh`, which also carry the other five adapters' capability profiles for every harness under both a UTF-8 locale and `LC_ALL=C`.
|
|
235
|
+
This guard is the refresh command after an upgrade to any matrix-covered harness; rerun it and update the versions above rather than trusting this table across releases.
|
|
236
|
+
Known staleness: on 2026-08-23 the steering-inbox doorbell run observed grok 1.0.5's idle composer classifying `unknown` (and sometimes pending-family), never `empty`, so the grok row above is stale for 1.0.5 and owes a refresh; steering is unaffected because the send path's composer check is advisory, but empty-requiring consumers (away-daemon injection, spawn readiness) should not trust the 1.0.0 grok result.
|
|
237
|
+
Cursor is deliberately outside this cursor-anchored empty-composer matrix because its terminal cursor is parked outside the composer; tmux's Cursor-specific, process-identity-gated cursorless fallback is covered by the [Cursor Agent CLI](#cursor-agent-cli) section's separate live evidence and drift guard.
|
|
238
|
+
|
|
239
|
+
`zellij action dump-screen --pane-id <id> --ansi` was verified at zellij 0.44.0 to preserve ANSI styling (real Claude Code rendered inside a zellij pane dumped `ESC[m` `❯` U+00A0 for its idle composer row), which is the capability the zellij composer classifier reads.
|
|
240
|
+
|
|
241
|
+
## Steering-inbox doorbell
|
|
242
|
+
|
|
243
|
+
The steering channel's one behavioral assumption - a real worker agent follows the constant self-describing doorbell line (list the inbox, read and act on its records in numeric order, then `mv` each into `handled/`) - was verified on 2026-08-23 against every installed verified harness, on tmux 3.6a, macOS arm64, on an isolated private socket, driving the REAL `bin/fm-send.sh` end to end (durable record plus doorbell, with one mid-wait re-ring playing the watcher's role).
|
|
244
|
+
|
|
245
|
+
```sh
|
|
246
|
+
FM_SEND_INBOX_LIVE_E2E=1 tests/fm-send-inbox-doorbell-live-e2e.test.sh
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Observed output (combined across the full run and the grok rerun after the advisory-skip narrowing landed):
|
|
250
|
+
|
|
251
|
+
```text
|
|
252
|
+
ok - claude (2.1.241 (Claude Code)): the doorbell reached a real worker, which acted and acked with the mv
|
|
253
|
+
ok - codex (codex-cli 0.147.0): the doorbell reached a real worker, which acted and acked with the mv
|
|
254
|
+
ok - opencode (1.18.21): the doorbell reached a real worker, which acted and acked with the mv
|
|
255
|
+
ok - pi (0.84.1): the doorbell reached a real worker, which acted and acked with the mv
|
|
256
|
+
# grok (grok 1.0.5 (5115b46bc909) [stable]): idle composer never classified empty; proceeding as production does (advisory check skips only on pending)
|
|
257
|
+
ok - grok (grok 1.0.5 (5115b46bc909) [stable]): the doorbell reached a real worker, which acted and acked with the mv
|
|
258
|
+
# harness absent, not verified here: kimi
|
|
259
|
+
ok - muse (Muse Code 0.2.1 (0.2.1-R1215.1)): the doorbell reached a real worker, which acted and acked with the mv
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
All six installed harnesses honored the doorbell contract with real model turns: each listed the inbox named by the doorbell, read its record, executed the instruction inside it, and acknowledged with the atomic `mv`.
|
|
263
|
+
Two findings from the run shaped the shipped behavior: an OpenCode vendor update modal swallowed the first doorbell and the single re-ring recovered it, which is exactly the watcher ladder's job; and grok 1.0.5's idle composer never classifies `empty` (a classifier drift owned by the [Composer classification matrix](#composer-classification-matrix) guard, whose refresh for grok 1.0.5 is still owed), which is why the ring's advisory pre-check skips only on an exact proven `pending` verdict - a doorbell into an ambiguous composer is a recoverable constant line, while skipping on ambiguity would starve steering for any harness the classifier cannot positively identify.
|
|
264
|
+
Kimi was not installed on the verification machine; its receive path is the same one-line-plus-shell contract, and the portable ladder and enqueue regressions in `tests/fm-task-inbox.test.sh` and `tests/fm-send-inbox.test.sh` cover every harness-independent half.
|
|
265
|
+
This guard is the refresh command after any harness upgrade; it spends a small number of real tokens per installed harness, reports an absent harness explicitly, and refuses a run that verified nothing.
|
|
266
|
+
|
|
267
|
+
## Herdr
|
|
268
|
+
|
|
269
|
+
The compatibility floor is protocol 14.
|
|
270
|
+
The whole real-Herdr lane's latest active verification uses both Herdr 0.7.4 protocol 16 and Herdr 0.8.0 protocol 19 on macOS aarch64, while focused Herdr 0.7.5 protocol 17, earlier protocol-16, protocol-14, and 0.7.3 evidence is retained where it defines current behavior or fallbacks.
|
|
271
|
+
Protocol 17 keeps every protocol-16 feature gate satisfied; the event and workspace-move floors remain 16.
|
|
272
|
+
Default-on presentation projection has its own floor at Herdr 0.8.0, protocol 19, verified below.
|
|
273
|
+
|
|
274
|
+
Core read-only probes:
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
herdr --version
|
|
278
|
+
herdr status --json | jq -c '{client:.client.protocol,server:.server.protocol}'
|
|
279
|
+
herdr api schema --json | jq -c '.schemas.subscription_event["$defs"].SubscriptionEventKind.enum'
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Observed protocol-16 compatibility shapes:
|
|
283
|
+
|
|
284
|
+
```text
|
|
285
|
+
herdr 0.7.5
|
|
286
|
+
{"client":17,"server":17}
|
|
287
|
+
["pane.output_matched","pane.agent_status_changed","pane.scroll_changed"]
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
The CLI matrix was checked directly:
|
|
291
|
+
|
|
292
|
+
| Guarantee | Command shape | Result |
|
|
293
|
+
| --- | --- | --- |
|
|
294
|
+
| Explicit session routing | `herdr <verb> ... --session <name>` | Reached the named session even while another server was running. |
|
|
295
|
+
| Literal send | `herdr pane send-text <pane> <text> --session <name>` | Left text unsubmitted until Enter. |
|
|
296
|
+
| Keys | `herdr pane send-keys <pane> enter|escape|ctrl+c --session <name>` | Enter and Escape worked; Ctrl-C interrupted foreground work. |
|
|
297
|
+
| Capture | `herdr pane read <pane> --source recent --lines N` | Small N could return empty below viewport height; a 200-line request plus local trim was stable. |
|
|
298
|
+
| Native state | `herdr agent get <pane>` | Working and done transitions were visible on some harnesses; live Claude Code 2.1.236 on Herdr 0.8.0 kept `agent_status=idle` for an entire landed turn, including a multi-second tool call, so submit confirmation falls through to the shared composer verdict. Native `busy` remains positive activity evidence, while native `idle` cannot close a turn and the adapter's semantic lifecycle decides worker state. |
|
|
299
|
+
| Restart | guarded named-session stop then start | Workspace, tab, pane, and labels persisted; the agent process and registration did not. |
|
|
300
|
+
| Close | `herdr pane close <pane> --session <name>` | The exact one-pane task tab closed; closing a final tab could remove the workspace. |
|
|
301
|
+
|
|
302
|
+
All destructive verification used `bin/fm-herdr-lab.sh` with a non-default `fm-lab-` name and a byte-identical default-session tripwire.
|
|
303
|
+
No ambient `herdr server stop` command is a supported test operation.
|
|
304
|
+
|
|
305
|
+
### Submit confirmation
|
|
306
|
+
|
|
307
|
+
Measured 2026-08-19 against Herdr 0.8.0 and Claude Code 2.1.236 in an isolated `fm-lab-` session.
|
|
308
|
+
|
|
309
|
+
`herdr agent get` reported `agent_status=idle` on every sample across a landed one-word turn and an 8-second `sleep` tool call, while the pane rendered `Pontificating…` then `Sock-hopping… (11s · ↓ 234 tokens)`.
|
|
310
|
+
`fm_backend_herdr_send_text_submit` therefore cannot treat native idle as proof of a swallow.
|
|
311
|
+
The portable regressions in `tests/fm-backend-herdr.test.sh` and `tests/fm-composer-lib.test.sh` pin the verdicts: native idle plus a cleared composer is delivery, proven pending plus idle is a swallow, and proven pending plus a generating busy signal is a queued Enter.
|
|
312
|
+
Refresh the live Claude proof with:
|
|
313
|
+
|
|
314
|
+
```sh
|
|
315
|
+
FM_HERDR_SUBMIT_CONFIRM_LIVE=1 tests/fm-herdr-submit-confirm-live-e2e.test.sh
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Observed 2026-08-19:
|
|
319
|
+
|
|
320
|
+
```text
|
|
321
|
+
ok - live Herdr submit confirm: Claude Code (2.1.236 (Claude Code)) on herdr 0.8.0 reports empty for a landed idle steer
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Prune and respawn
|
|
325
|
+
|
|
326
|
+
The real label-collision reproduction is owned by:
|
|
327
|
+
|
|
328
|
+
```sh
|
|
329
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
330
|
+
tests/fm-backend-herdr-prune-safety-e2e.test.sh
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Observed guarantee: a pre-existing captain-owned workspace with a seed-shaped tab was adopted for routing but its tab was never eligible for prune because the current create call did not return that seed id.
|
|
334
|
+
|
|
335
|
+
Restart-husk replacement is owned by:
|
|
336
|
+
|
|
337
|
+
```sh
|
|
338
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
339
|
+
tests/fm-backend-herdr-respawn-idem-e2e.test.sh
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Observed guarantee: a restored no-agent tab was replaced create-before-close, while a registered live agent caused refusal.
|
|
343
|
+
|
|
344
|
+
### Launcher workspace placement
|
|
345
|
+
|
|
346
|
+
Herdr exports its pane identity into every process it manages, checked on 2026-07-30 against Herdr 0.7.5 protocol 17 inside a guarded lab pane:
|
|
347
|
+
|
|
348
|
+
```sh
|
|
349
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh
|
|
350
|
+
"$HERDR_LAB_HELPER" run "$LAB" pane run "$PANE" "sh -c 'env | grep ^HERDR | sort > /tmp/env.txt'"
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
```text
|
|
354
|
+
HERDR_ENV=1
|
|
355
|
+
HERDR_PANE_ID=w1:p1
|
|
356
|
+
HERDR_SESSION=fm-lab-fm-herdr-env-pro-65961-25535
|
|
357
|
+
HERDR_SOCKET_PATH=/Users/kunchen/.config/herdr/sessions/fm-lab-fm-herdr-env-pro-65961-25535/herdr.sock
|
|
358
|
+
HERDR_TAB_ID=w1:t1
|
|
359
|
+
HERDR_WORKSPACE_ID=w1
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
This complete injection shape is verified only for Herdr 0.7.5.
|
|
363
|
+
Firstmate requires both `HERDR_PANE_ID` and `HERDR_SOCKET_PATH` before accepting claimed launcher ancestry.
|
|
364
|
+
|
|
365
|
+
`pane get` reports the pane's current owning tab and workspace, which is what placement resolves from; the injected `HERDR_TAB_ID` and `HERDR_WORKSPACE_ID` are creation-time snapshots and are not read as current identity:
|
|
366
|
+
|
|
367
|
+
```sh
|
|
368
|
+
"$HERDR_LAB_HELPER" run "$LAB" pane get w1:p1 | jq -c '.result.pane | {pane_id,tab_id,workspace_id}'
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
```text
|
|
372
|
+
{"pane_id":"w1:p1","tab_id":"w1:t1","workspace_id":"w1"}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
Placement is owned by:
|
|
376
|
+
|
|
377
|
+
```sh
|
|
378
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
379
|
+
tests/fm-backend-herdr-launcher-workspace-e2e.test.sh
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
Observed guarantees on 2026-07-30 against Herdr 0.7.5 protocol 17:
|
|
383
|
+
|
|
384
|
+
```text
|
|
385
|
+
ok - real herdr E2E: with one 'firstmate' workspace and no herdr parent, a crewmate still lands in this home's own workspace without stealing focus
|
|
386
|
+
ok - real herdr E2E: the normal unique-label path is unchanged when the launcher's own pane identifies the workspace
|
|
387
|
+
ok - real herdr E2E: presentation spaces still create the isolated child workspace and bind it under the launcher's exact parent, without stealing focus
|
|
388
|
+
ok - real herdr E2E: with two 'firstmate' workspaces, a worker spawned from inside the second one lands in that exact workspace
|
|
389
|
+
ok - real herdr E2E: the duplicate-labeled sibling workspace is left entirely untouched and focus is preserved
|
|
390
|
+
ok - real herdr E2E: with a duplicated home label, a projected worker still hangs off the launcher's exact workspace and the sibling stays untouched
|
|
391
|
+
ok - real herdr E2E: an ambiguous home label with no launcher identity refuses before any worker endpoint exists
|
|
392
|
+
ok - real herdr E2E: a launcher pane that no longer exists refuses before any worker endpoint exists
|
|
393
|
+
ok - real herdr E2E: a secondmate launching its own worker gets the same exact-workspace guarantee, and its same-labeled sibling is untouched
|
|
394
|
+
ok - real herdr E2E: a --secondmate launch still stands up that secondmate's own workspace instead of inheriting the launcher's
|
|
395
|
+
ok - real herdr E2E: teardown closes only the worker's own pane and leaves the launcher, its workspace, and the same-labeled sibling intact
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
That suite's headline case runs `bin/fm-spawn.sh` inside a real Herdr pane, so the parent identity comes from Herdr's own injection rather than a composed environment.
|
|
399
|
+
Cross-session and contradictory bindings are covered deterministically in `tests/fm-backend-herdr.test.sh`, which can script a second server's socket without provisioning one.
|
|
400
|
+
|
|
401
|
+
### Per-home and presentation topology
|
|
402
|
+
|
|
403
|
+
Per-home behavior is owned by:
|
|
404
|
+
|
|
405
|
+
```sh
|
|
406
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
407
|
+
tests/fm-backend-herdr-workspace-per-home-e2e.test.sh
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
Observed guarantee: the primary and secondmate used distinct home workspaces, a child launched by the secondmate stayed in that secondmate workspace, list-live remained home-scoped, and exact cleanup did not affect sibling homes.
|
|
411
|
+
|
|
412
|
+
The complete projection suite ran on 2026-07-21 against Herdr 0.7.4 protocol 16:
|
|
413
|
+
|
|
414
|
+
```sh
|
|
415
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
416
|
+
tests/fm-backend-herdr-presentation-e2e.test.sh
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
Observed guarantees included:
|
|
420
|
+
|
|
421
|
+
```text
|
|
422
|
+
ok - real Herdr lab: primary and two secondmate homes each own a top-level contiguous child block
|
|
423
|
+
ok - real Herdr lab: concurrent primary/A/B spawns stay session-locked with zero focus drift
|
|
424
|
+
ok - real Herdr lab: session lock contention from a secondmate home falls back flat with no journal
|
|
425
|
+
ok - real Herdr lab: legacy projection labels and flat secondmate tabs are left unmigrated
|
|
426
|
+
ok - real Herdr lab: multi-home exact-pane teardowns restore captain focus without workspace close authority
|
|
427
|
+
ok - real Herdr lab validation completed on Herdr 0.7.4 with the default-session tripwire intact
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
The suite also covers lost or failed move responses, active-tab refusal, restart husks, missing and duplicate tokens, manual renames, concurrent cleanup, and exact focus restoration.
|
|
431
|
+
|
|
432
|
+
The mandatory projection suite ran again on 2026-07-24 against Herdr 0.7.5 protocol 16:
|
|
433
|
+
|
|
434
|
+
```sh
|
|
435
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
436
|
+
tests/fm-backend-herdr-presentation-e2e.test.sh
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
Observed restart-reclaim guarantees:
|
|
440
|
+
|
|
441
|
+
```text
|
|
442
|
+
ok - real Herdr lab: Hi Bit and Wheelhouse-style same-identity restarts reclaim one nested space with exact focus and idempotence
|
|
443
|
+
ok - real Herdr lab: secondmate restart binding and reclaim stay isolated to the exact child home and parent
|
|
444
|
+
ok - real Herdr lab: concurrent cross-home recoveries replace exact husks under one session lock with no focus drift
|
|
445
|
+
ok - real Herdr lab: missing, renamed, and duplicate tokens trigger zero destructive or adoptive calls, and live duplicate risk refuses launch
|
|
446
|
+
ok - real Herdr lab validation completed on Herdr 0.7.5 with the default-session tripwire intact
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
The projection suite ran again on 2026-08-04 against Herdr 0.8.0 protocol 19 for the default-on flip, where an absent `config/herdr-presentation-spaces` enables the projection and the value `off` opts out; since 2026-08-05 an absent file enables the projection only at or above the 0.8.0 floor recorded under "Presentation version floor" below, and `on` is the explicit opt-in that survives the floor:
|
|
450
|
+
|
|
451
|
+
```sh
|
|
452
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
453
|
+
tests/fm-backend-herdr-presentation-e2e.test.sh
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Observed default and opt-out guarantees:
|
|
457
|
+
|
|
458
|
+
```text
|
|
459
|
+
ok - real Herdr lab: an opted-out spawn retains the Stage 1 Herdr command sequence with zero ordering calls
|
|
460
|
+
ok - real Herdr lab: a home that configured nothing is projected by default
|
|
461
|
+
ok - real Herdr lab: the primary presentation setting inherits into real secondmate homes
|
|
462
|
+
ok - real Herdr lab validation completed on Herdr 0.8.0 with the default-session tripwire intact
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
The projected spawn in that run used the historical empty opt-in file, so a home that had already enabled the projection keeps it without any migration step.
|
|
466
|
+
One concurrent cross-home recovery case refused under contention on a loaded machine and passed on an immediate rerun; recovery-path presentation lock contention is a deliberate hard refusal rather than a flat fallback, which default-on now makes reachable from any Herdr home.
|
|
467
|
+
That run measured the default-on projection on Herdr 0.8.0 only, while the focus-flash regression below was last run on 0.7.5 before the flip, so neither run covered a defective release under default-on projection; the version floor and the focus-flash suite's Part C close that gap.
|
|
468
|
+
|
|
469
|
+
The restored-shell session-start cleanup ran on 2026-07-24 against Herdr 0.7.5 protocol 17:
|
|
470
|
+
|
|
471
|
+
```sh
|
|
472
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
473
|
+
tests/fm-herdr-session-cleanup-e2e.test.sh
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Observed guarantee: one exact home-local, journal-correlated, one-tab and one-pane childless idle shell was closed after restoration while the exact non-target focus and default fleet session remained unchanged, and a repeat run was a no-op.
|
|
477
|
+
|
|
478
|
+
### Workspace-removal focus safety
|
|
479
|
+
|
|
480
|
+
The focus-flash regression ran on 2026-08-05 against both Herdr 0.7.5 protocol 17 and Herdr 0.8.0 protocol 19 on macOS aarch64, with the 0.7.5 run using the pinned upstream release binary first on `PATH`:
|
|
481
|
+
|
|
482
|
+
```sh
|
|
483
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
484
|
+
tests/fm-backend-herdr-focus-flash-e2e.test.sh
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
Observed output on Herdr 0.7.5:
|
|
488
|
+
|
|
489
|
+
```text
|
|
490
|
+
ok - old path: the explicit last-pane close of a non-focused workspace stole focus (w3 w3:t1 -> w2 w2:t1)
|
|
491
|
+
ok - mitigation: every in-operation sample preserved exact focus while the doomed workspace was removed
|
|
492
|
+
ok - mitigation: no explicit close and no corrective focus were needed on the defective release
|
|
493
|
+
ok - fallback: a doomed pane holding a persistent child exhausts the proof and takes the plain explicit close
|
|
494
|
+
ok - fallback on a defective release: a bounded wrong-focus window of 4 samples was fully restored to the anchor
|
|
495
|
+
ok - version floor: herdr 0.7.5 protocol 17 remains conservatively below the floor with steal_live=1
|
|
496
|
+
ok - version floor: an unconfigured home falls back flat on herdr 0.7.5 and the explicit opt-in still projects
|
|
497
|
+
evidence: herdr=0.7.5 protocol=17 steal_live=1 floor_verdict=1 default-session-tripwire=armed
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Observed output on Herdr 0.8.0:
|
|
501
|
+
|
|
502
|
+
```text
|
|
503
|
+
ok - old path note: this Herdr release preserves focus across the explicit close; continuing with outcome-only assertions
|
|
504
|
+
ok - mitigation: every in-operation sample preserved exact focus while the doomed workspace was removed
|
|
505
|
+
ok - fallback: a doomed pane holding a persistent child exhausts the proof and takes the plain explicit close
|
|
506
|
+
ok - fallback on a focus-preserving release: the plain explicit close preserved exact focus throughout
|
|
507
|
+
ok - version floor: herdr 0.8.0 protocol 19 is at or above the floor and preserves focus
|
|
508
|
+
ok - version floor: an unconfigured home stays projected on herdr 0.8.0 and the explicit opt-in agrees
|
|
509
|
+
evidence: herdr=0.8.0 protocol=19 steal_live=0 floor_verdict=0 default-session-tripwire=armed
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Part C is the case the suite could not reach before: a doomed pane whose shell holds a persistent background child fails the lone-idle-shell proof on every sample, so the plan takes the plain explicit close, in the geometry where the closing workspace's right neighbour is a spacer rather than the focused anchor.
|
|
513
|
+
On 0.7.5 that fallback exposed a bounded four-sample wrong-focus window and restored the anchor exactly; on 0.8.0 the same fallback exposed none, which is why default-on projection is floored at 0.8.0 rather than mitigated further below it.
|
|
514
|
+
The suite also cross-checks its own Part A measurement against the floor classifier on whatever release it runs, so a drifted protocol-to-release mapping fails there rather than silently gating on the wrong thing.
|
|
515
|
+
|
|
516
|
+
### Presentation version floor
|
|
517
|
+
|
|
518
|
+
Default-on presentation projection is floored at Herdr 0.8.0.
|
|
519
|
+
The floor's structural signal is the selected running server's protocol number, falling back to the client protocol only when that selected session positively reports no running server, and the release mapping was measured on 2026-08-05 by running each pinned upstream macOS aarch64 release asset's own `status --json` through the guarded lab helper:
|
|
520
|
+
|
|
521
|
+
| Release | Reported version | Protocol | Carries both upstream focus fixes | Floor verdict |
|
|
522
|
+
|---|---|---|---|---|
|
|
523
|
+
| v0.7.3 | 0.7.3 | 16 | no | below |
|
|
524
|
+
| v0.7.4 | 0.7.4 | 16 | no | below |
|
|
525
|
+
| v0.7.5 | 0.7.5 | 17 | no | below |
|
|
526
|
+
| preview-2026-07-21-0f10e1453a7f | 0.7.5-preview.2026-07-21-0f10e1453a7f | 17 | no | below |
|
|
527
|
+
| preview-2026-07-29-44b3adb12552 | 0.7.5-preview.2026-07-29-44b3adb12552 | 18 | yes | below |
|
|
528
|
+
| preview-2026-08-04-d78e3d3b5126 | 0.8.0-preview.2026-08-04-d78e3d3b5126 | 19 | yes | above |
|
|
529
|
+
| v0.8.0 | 0.8.0 | 19 | yes | above |
|
|
530
|
+
|
|
531
|
+
No build lacking both fixes reaches protocol 19, and every pre-fix build tops out at 17, so protocol 19 is a safe structural expression of the 0.8.0 floor.
|
|
532
|
+
The one post-fix build below it is a preview that still reports a 0.7.5 version, so it is conservatively treated as below the floor, which costs a preview build its projection and never lets an unfixed build through.
|
|
533
|
+
The 2026-08-05 named-lab cross-version probe started a server from Herdr 0.7.5 and queried it with the installed 0.8.0 client; status reported client version 0.8.0 protocol 19, server version 0.7.5 protocol 17, server running true, and server compatible false.
|
|
534
|
+
That ordinary post-upgrade shape proves the running server owns the focus behavior, so the unconfigured default composes client and selected-server verdicts conservatively and rechecks after server ensure before publishing a journal or creating a workspace.
|
|
535
|
+
|
|
536
|
+
Refresh this table with the opt-in guard, which re-downloads the pinned assets, verifies their digests, and fails naming any release whose reported version, protocol, or verdict has moved:
|
|
537
|
+
|
|
538
|
+
```sh
|
|
539
|
+
FM_HERDR_VERSION_FLOOR_LIVE_E2E=1 tests/fm-herdr-version-floor-live-e2e.test.sh
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
The classifier itself, the config preference it composes with, and the one-warning-per-release behavior are pinned portably with no Herdr installed:
|
|
543
|
+
|
|
544
|
+
```sh
|
|
545
|
+
tests/fm-backend-herdr.test.sh
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
Observed guarantees: every measured release classifies as the table records; either the protocol or the version signal alone carries an at-or-above verdict, and each divergent pair flips once the carrying signal is removed; client and running selected-session server verdicts compose conservatively, an unreadable server-running state and losing both release signals report indeterminate and fall back flat, the default is rechecked after server ensure before projection publication, an unconfigured home is projected only at or above the floor, an explicit `on`, including the historical empty opt-in file, is honored below it, and the below-floor warning is emitted once per home per detected release rather than once per spawn.
|
|
549
|
+
|
|
550
|
+
The whole real-Herdr lane was run on 2026-08-05 against both the CI-pinned Herdr 0.7.4 protocol 16, which is below the floor, and Herdr 0.8.0 protocol 19, which is at it:
|
|
551
|
+
|
|
552
|
+
```sh
|
|
553
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh bin/fm-test-run.sh --lane real-herdr-gated
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Both runs reported `family=real-herdr-gated count=11 failed=0`.
|
|
557
|
+
The projection suite's unconfigured-home case is release-aware rather than pinned to one outcome, so it proves the projected default on 0.8.0 and the flat fallback with its naming warning on 0.7.4:
|
|
558
|
+
|
|
559
|
+
```text
|
|
560
|
+
ok - real Herdr lab: a home that configured nothing is projected by default on herdr 0.8.0
|
|
561
|
+
ok - real Herdr lab: a home that configured nothing falls back flat on below-floor herdr 0.7.4 with one naming warning
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
Every other case in that suite uses an explicit opt-in or opt-out, so the floor leaves them unchanged on both releases.
|
|
565
|
+
|
|
566
|
+
Direct lab probes on 2026-07-28 established the removal rules the emptying-close plan relies on, each verified with `workspace list` focus reads around one mutation in a guarded `fm-lab-` session:
|
|
567
|
+
|
|
568
|
+
- An explicit `pane close` that emptied a non-focused workspace moved focus off the focused workspace in both before-focus and after-focus geometries.
|
|
569
|
+
- Ending a workspace's lone shell preserved the focused workspace exactly when the dying workspace sat behind it or the focused workspace was last, and moved focus to the focused workspace's right neighbor otherwise.
|
|
570
|
+
- The production focus-preserving close in the dangerous geometry repositioned the doomed workspace, ended its proved shell, and left every concurrent focus sample on the exact anchor with no corrective `tab focus` issued.
|
|
571
|
+
|
|
572
|
+
Two real-hardware conditions were required for the pane-death path to engage and are now encoded in the adapter and its unit fixtures: BSD `ps` reports a login shell's `comm` as `-zsh`, and an idle shell transiently hosts a prompt helper (starship) as a second foreground process immediately after a `workspace.move` relayout, which the bounded settle window absorbs.
|
|
573
|
+
|
|
574
|
+
The rules match the v0.7.5 tag source (`close_selected_workspace` reassigns focus from the closing workspace's index; `handle_pane_died` only clamps the stale focused index), and the upstream default branch resolves both paths by workspace id (PR #1877, commit `165dca45`, for the explicit close; PR #1912, commit `a979916`, for pane death), so the plan degrades to a harmless reorder-then-remove once a release carries them.
|
|
575
|
+
|
|
576
|
+
The full projection and restored-shell suites were re-run on 2026-07-28 on Herdr 0.7.5 with the updated close path; the presentation suite completed with `real Herdr lab validation completed on Herdr 0.7.5 with the default-session tripwire intact`, and the restored-shell cleanup guarantee above was unchanged.
|
|
577
|
+
|
|
578
|
+
The teardown-level record-retention gate was verified on 2026-07-28 with metadata fixtures and a live contending lock holder:
|
|
579
|
+
|
|
580
|
+
```sh
|
|
581
|
+
tests/fm-teardown.test.sh
|
|
582
|
+
tests/fm-backend-herdr.test.sh
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
Observed guarantees: a contended presentation lock refused the teardown before the isolated copy was returned, with the task branch, every durable record, and the endpoint intact and no pane close attempted; the retry after the contention cleared returned the copy, closed the pane under the lock, and removed the records; an unknown structured-presence result after an attempted projected close retained the journal and every record with a nonzero exit; and every presence-gate mode accepted only a structured not-found as gone.
|
|
586
|
+
|
|
587
|
+
The same fixtures verified three further boundaries on 2026-07-29: missing or malformed endpoint identity and an unparseable pane presence refused record removal with everything retained; the SIGKILL escalation re-read the exact pane's process information and refused to signal when a different shell pid owned the pane, falling back to the plain close with the original process untouched; and a reposition whose removal then failed on every path restored the exact original workspace order through a second verified move and reported the close as failed.
|
|
588
|
+
|
|
589
|
+
The teardown fixture was re-run on 2026-07-31 after extending the same fail-closed boundary through forced secondmate cleanup, including recursive cleanup of a nested secondmate whose Herdr grandchild close remains unconfirmed.
|
|
590
|
+
|
|
591
|
+
Observed output:
|
|
592
|
+
|
|
593
|
+
```text
|
|
594
|
+
ok - forced secondmate teardown preflights every Herdr child before cleanup mutation
|
|
595
|
+
ok - forced secondmate teardown retains Herdr child identity until exact pane disappearance
|
|
596
|
+
ok - forced teardown retains a nested secondmate home and its grandchild's Herdr identity when the grandchild close is unconfirmed
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
### Composer and operational input
|
|
600
|
+
|
|
601
|
+
Real captures verified these active distinctions:
|
|
602
|
+
|
|
603
|
+
- Claude and Codex use bare `❯` and `›` agent composers.
|
|
604
|
+
- Pi uses content between complete separator rows and requires exact native Pi identity.
|
|
605
|
+
- Dim or faint suggestion text is ghost content, while normally styled text is pending input.
|
|
606
|
+
- Grok dark truecolor placeholders are ghost content, while bright truecolor typed input remains pending.
|
|
607
|
+
- A bare shell prompt has no safe agent-composer container and is unknown.
|
|
608
|
+
|
|
609
|
+
`tests/fm-composer-ghost.test.sh`, `tests/fm-composer-lib.test.sh`, and the Herdr composer cases pin the exact captured ANSI bytes.
|
|
610
|
+
The U+2063 operational and routed-request separators were exercised through a real Pi-on-Herdr path; the byte-exact active regression is:
|
|
611
|
+
|
|
612
|
+
```sh
|
|
613
|
+
FM_SEND_MARKER_HERDR_E2E=1 \
|
|
614
|
+
tests/fm-send-secondmate-marker-herdr-e2e.test.sh
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### Native blocked event
|
|
618
|
+
|
|
619
|
+
The protocol-16 event path was measured on 2026-07-11 with Herdr 0.7.3 and Python 3.13:
|
|
620
|
+
|
|
621
|
+
```sh
|
|
622
|
+
HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
623
|
+
tests/fm-backend-herdr-eventwait-smoke.test.sh
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
Observed output:
|
|
627
|
+
|
|
628
|
+
```text
|
|
629
|
+
ok - real herdr: events.subscribe capability gate passes
|
|
630
|
+
ok - real herdr: a driven idle->blocked transition returns the blocked record in 0.129s
|
|
631
|
+
ok - real herdr: the watcher fast-path enqueues a stale wake naming the task window
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
Polling remained active and is covered as the fallback for capability, connect, subscribe, and repeated reader failure.
|
|
635
|
+
|
|
636
|
+
### Agent lifecycle control
|
|
637
|
+
|
|
638
|
+
Herdr is one of the two backends whose recovery-grade agent-state classifier the control plane may trust ([agent-control.md](../agent-control.md)), so its lifecycle gating is measured against the real binary; reverified 2026-08-08 on Herdr 0.8.0, and first measured 2026-08-02 on Herdr 0.7.5 with identical results:
|
|
639
|
+
|
|
640
|
+
```sh
|
|
641
|
+
tests/fm-control-herdr-smoke.test.sh
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
Observed output:
|
|
645
|
+
|
|
646
|
+
```text
|
|
647
|
+
ok - real herdr: exit on a pane with no registered agent is idempotent success
|
|
648
|
+
ok - real herdr: interrupt refuses when herdr's own agent registry reports no agent
|
|
649
|
+
ok - real herdr: interrupt delivers the harness's key and proves the agent survived it
|
|
650
|
+
ok - real herdr: no control verb removed the endpoint or the task's local copy
|
|
651
|
+
ok - real herdr: an agent that does not stop fails closed instead of being reported as stopped
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
The registry read through `herdr pane report-agent` is the same source `fm_backend_herdr_agent_state` classifies, so registering and not registering an agent on a plain shell pane exercises exactly the gate every lifecycle verb depends on, with no real agent launched.
|
|
655
|
+
That command is the guard that refreshes this record; run it after every Herdr upgrade rather than trusting the version above.
|
|
656
|
+
|
|
657
|
+
### Away-mode transport
|
|
658
|
+
|
|
659
|
+
The Pi/Herdr return and injection path was reverified on Herdr 0.7.3 and Pi 0.80.7:
|
|
660
|
+
|
|
661
|
+
```sh
|
|
662
|
+
FM_AFK_PI_HERDR_E2E=1 HERDR_LAB_HELPER=bin/fm-herdr-lab.sh \
|
|
663
|
+
tests/fm-afk-pi-herdr-return-e2e.test.sh
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Observed guarantees: pending composer input refused injection and raised one alert; idle Pi accepted one marked escalation; the return gate refused ordinary work while a live blocker remained; resolving the blocker allowed the return flow.
|
|
667
|
+
The dedicated Herdr daemon workspace topology is covered by `tests/fm-afk-launch.test.sh` and preserves the captain tab's pane count.
|
|
668
|
+
|
|
669
|
+
## Zellij
|
|
670
|
+
|
|
671
|
+
The current compatibility floor and latest verification are Zellij 0.44.0 with `jq` on macOS aarch64.
|
|
672
|
+
All real tests use a uniquely named session and `tests/zellij-test-safety.sh`; they never touch a session named `firstmate` or call all-session deletion.
|
|
673
|
+
|
|
674
|
+
| Guarantee | Command shape | Result |
|
|
675
|
+
| --- | --- | --- |
|
|
676
|
+
| Headless session | `zellij attach -b <name>` without a TTY | Created a persistent background session and returned. |
|
|
677
|
+
| Session list | `zellij list-sessions --short --no-formatting` | Returned one plain name per line without starting a session. |
|
|
678
|
+
| Create tab | `zellij action new-tab --cwd <dir> --name <title>` | Returned a numeric tab id and focused the new tab when a client was attached. |
|
|
679
|
+
| Pane discovery | `zellij action list-panes --json` | Included terminal pane id, tab id, plugin flag, and top-level `pane_cwd`. |
|
|
680
|
+
| Literal send | `zellij action paste --pane-id <id> -- <text>` | Left text unsubmitted. |
|
|
681
|
+
| Keys | `send-keys --pane-id <id> Enter`, `Esc`, and one argument `Ctrl c` | All three shared operations worked. |
|
|
682
|
+
| Capture | `dump-screen --pane-id <id>` or `--full` | Worked with no attached client; no line-bound flag exists. |
|
|
683
|
+
| Styled capture | `dump-screen --pane-id <id> --ansi` | Preserved ANSI styling ("Composer classification matrix" above); feeds the zellij composer classifier. |
|
|
684
|
+
| Close | `close-tab-by-id <id>` | Removed the live task pane and tab together. |
|
|
685
|
+
| Failure exit | actions against missing targets | Returned exit 0, requiring structural preflight and output-shape validation. |
|
|
686
|
+
|
|
687
|
+
`pane_cwd` stayed frozen when a foreground subshell changed directory.
|
|
688
|
+
The marker-delimited `pwd` probe returned the live nested cwd and is covered by the real smoke.
|
|
689
|
+
The focus mitigation restored the previously active tab after `new-tab`, with the unavoidable narrow race documented in the operator guide.
|
|
690
|
+
|
|
691
|
+
```sh
|
|
692
|
+
tests/fm-backend-zellij.test.sh
|
|
693
|
+
tests/fm-backend-zellij-smoke.test.sh
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
The real lifecycle smoke proved spawn, metadata, nested-subshell worktree discovery, send, capture, unlanded-work refusal, approved local landing, exact tab cleanup, and session cleanup without retaining task-specific ids or branch names here.
|
|
697
|
+
|
|
698
|
+
## Orca
|
|
699
|
+
|
|
700
|
+
Real readiness was verified against `/usr/local/bin/orca` with `/Applications/Orca.app` bundle version 1.4.116.
|
|
701
|
+
|
|
702
|
+
```sh
|
|
703
|
+
orca status --json
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
Observed fields:
|
|
707
|
+
|
|
708
|
+
```text
|
|
709
|
+
result.runtime.reachable=true
|
|
710
|
+
result.runtime.state=ready
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
`orca terminal create --json` returned `result.terminal.handle`.
|
|
714
|
+
`orca worktree create` returned `result.worktree.id` and `result.worktree.path`.
|
|
715
|
+
Speculative bare ids and nested terminal fields were deliberately rejected.
|
|
716
|
+
|
|
717
|
+
```sh
|
|
718
|
+
tests/fm-backend-orca.test.sh
|
|
719
|
+
tests/fm-backend.test.sh
|
|
720
|
+
tests/fm-bootstrap.test.sh
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
The fake-Orca suite covers readiness, registration, create response parsing, metadata routing, popup-safe submit, and path-matched release refusal.
|
|
724
|
+
|
|
725
|
+
## cmux
|
|
726
|
+
|
|
727
|
+
The current compatibility floor is cmux 0.64, and the active live evidence uses 0.64.17 build 97 on macOS aarch64.
|
|
728
|
+
Real tests use only exact `fm-test-` workspaces guarded by `tests/cmux-test-safety.sh` and never quit or relaunch the captain's app.
|
|
729
|
+
|
|
730
|
+
```sh
|
|
731
|
+
cmux version
|
|
732
|
+
cmux ping
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
Observed version:
|
|
736
|
+
|
|
737
|
+
```text
|
|
738
|
+
cmux 0.64.17 (97) [9ed29d81a]
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
Source and live checks established the five control modes:
|
|
742
|
+
|
|
743
|
+
- `off` starts no listener.
|
|
744
|
+
- `cmuxOnly` rejects an external Firstmate process by ancestry.
|
|
745
|
+
- `automation` uses an owner-only 0600 socket with no handshake.
|
|
746
|
+
- `password` uses the same 0600 socket plus `auth <password>`.
|
|
747
|
+
- `allowAll` uses a 0666 socket with no authentication.
|
|
748
|
+
|
|
749
|
+
The live default rejection was `Access denied - only processes started inside cmux can connect`.
|
|
750
|
+
The live password challenge was `Authentication required - send auth <password> first`.
|
|
751
|
+
The app configuration writer did not retain a hand-added socket password, which is why the operator guide requires Settings and a local Firstmate password source.
|
|
752
|
+
|
|
753
|
+
Current active CLI findings:
|
|
754
|
+
|
|
755
|
+
| Guarantee | Command shape | Result |
|
|
756
|
+
| --- | --- | --- |
|
|
757
|
+
| Create | `new-workspace --name <title> --cwd <dir> --focus false --id-format uuids` | Created one workspace with one surface without focusing it. |
|
|
758
|
+
| Fresh readiness | `list-panes --workspace <id> --json --id-format uuids` | Found a brand-new surface before content existed. |
|
|
759
|
+
| Fresh read counterexample | `read-screen` before any write | Returned `internal_error: Failed to read terminal text`. |
|
|
760
|
+
| Literal send | `send --workspace <id> --surface <id> -- <text>` | Left text unsubmitted. |
|
|
761
|
+
| Keys | `send-key ... enter|escape|ctrl-c` | All shared key operations worked. |
|
|
762
|
+
| Nested cwd | `current_directory` plus foreground subshell | Structured cwd froze; the marker-delimited `pwd` probe found the live cwd. |
|
|
763
|
+
| Last surface | `close-surface` on the only surface | Refused with `invalid_state: Cannot close the last surface`. |
|
|
764
|
+
| Last workspace | `close-workspace` on the only workspace in a window | Printed success but left the workspace present. |
|
|
765
|
+
|
|
766
|
+
The last-workspace workaround was reverified on 2026-07-10 in Automation mode.
|
|
767
|
+
After creating one unfocused unnamed sibling in the same window, `close-workspace` removed the exact task workspace and left only cmux's default sibling.
|
|
768
|
+
A selected non-last workspace closed directly, proving that window cardinality rather than selection is the trigger.
|
|
769
|
+
|
|
770
|
+
Source inspection confirmed each workspace constructor creates a new UUID with no restored-id input.
|
|
771
|
+
Recovery therefore remains title-based.
|
|
772
|
+
The bundled Claude wrapper was observed stripping `CMUX_*` variables on its failed socket-probe path while retaining the app bundle id, supporting the macOS-only bundle-id and ancestry fallbacks.
|
|
773
|
+
|
|
774
|
+
```sh
|
|
775
|
+
tests/fm-backend-cmux.test.sh
|
|
776
|
+
tests/fm-backend-cmux-smoke.test.sh
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
The real smoke proves socket access, fresh readiness, current-path probing, send and keys, bounded capture, title identity, and guarded exact cleanup.
|
|
780
|
+
|
|
781
|
+
### Claude composer confirmation
|
|
782
|
+
|
|
783
|
+
The borderless Claude composer confirmation was verified on 2026-08-09 with cmux 0.64.22 build 102 and Claude Code 2.1.226 on macOS aarch64.
|
|
784
|
+
An isolated real Claude worker rendered a bare `❯` plus U+00A0 row between horizontal rules.
|
|
785
|
+
The cmux classifier returned `empty`, and one `fm-send.sh --resolve-key <key> ALBATROSS` command - which used the typed path before ordinary task steers moved to the inbox - appended the matching `resolved` event before the worker reported completion.
|
|
786
|
+
The terminal capture contained exactly one submitted `❯ ALBATROSS` row.
|
|
787
|
+
The dated proof used this command:
|
|
788
|
+
|
|
789
|
+
```sh
|
|
790
|
+
FM_CMUX_CLAUDE_COMPOSER_LIVE=1 bin/fm-test-run.sh tests/fm-cmux-claude-composer-live-e2e.test.sh
|
|
791
|
+
```
|
|
792
|
+
|
|
793
|
+
That guard still addresses the worker by task selector, so it no longer reaches the typed submit path and is not a current refresh entry point for this guarantee.
|
|
794
|
+
The portable classifier regression is `tests/fm-backend-cmux.test.sh`.
|
|
795
|
+
|
|
796
|
+
## Codex App host tools
|
|
797
|
+
|
|
798
|
+
A reusable Desktop host-tool smoke ran on 2026-07-06 against Codex Desktop bundle version 26.623.101652, build 4674, bundle id `com.openai.codex`.
|
|
799
|
+
Local paths and task-specific ids are intentionally not retained here.
|
|
800
|
+
|
|
801
|
+
The host-tool sequence was:
|
|
802
|
+
|
|
803
|
+
1. list a saved project;
|
|
804
|
+
2. create a Desktop-owned worktree thread;
|
|
805
|
+
3. recover and read the thread while active and after completion;
|
|
806
|
+
4. verify the thread appended a Firstmate status line and wrote its report;
|
|
807
|
+
5. send a follow-up to the same thread;
|
|
808
|
+
6. read the completed follow-up;
|
|
809
|
+
7. archive the exact thread;
|
|
810
|
+
8. read the archived transcript with state `notLoaded`.
|
|
811
|
+
|
|
812
|
+
Observed guarantee: a Desktop-owned thread can write Firstmate lifecycle files when the prompt provides an authorized absolute path, and create, send, read, and archive work at the Desktop host-tool layer.
|
|
813
|
+
The missing guarantee remains a supported shell-callable bridge that lets Firstmate perform those operations against the same visible Desktop endpoint.
|
|
814
|
+
App-server partial methods and raw socket experiments do not satisfy that bridge contract.
|
|
815
|
+
|
|
816
|
+
## Cursor Agent CLI
|
|
817
|
+
|
|
818
|
+
Cursor runs crewmate, scout, secondmate, and primary work; [`supervision.md`](supervision.md#cursor-primary-park-2026-08-13) owns the primary evidence.
|
|
819
|
+
The evidence below was produced on 2026-08-11 against the installed signed CLI on macOS 26.5.2 arm64 with tmux 3.6a, running as `kunchenguid`, and extended on 2026-08-13 with the tmux composer verdict below.
|
|
820
|
+
|
|
821
|
+
- Binary: `~/.local/bin/cursor-agent`, canonicalizing into `~/.local/share/cursor-agent/versions/2026.08.11-e8db854/cursor-agent`.
|
|
822
|
+
- Version: `cursor-agent --version` reported `2026.08.11-e8db854`, and `cursor-agent status` reported a logged-in account.
|
|
823
|
+
- Both installed names, `cursor-agent` and the legacy alias `agent`, resolve into that same versioned install tree.
|
|
824
|
+
|
|
825
|
+
Resolution prints the STABLE launcher rather than the canonical target, because the canonical path carries a version the CLI replaces on its own auto-update.
|
|
826
|
+
|
|
827
|
+
### Process identity
|
|
828
|
+
|
|
829
|
+
`#{pane_current_command}` and `ps -o comm=` disagree for cursor, which is why identity reads both:
|
|
830
|
+
|
|
831
|
+
| Source | Observed value |
|
|
832
|
+
| --- | --- |
|
|
833
|
+
| `#{pane_current_command}` | `node` |
|
|
834
|
+
| `ps -o comm=` | `/Users/<user>/.local/bin/cursor-agent` |
|
|
835
|
+
| child argv | `.../bin/cursor-agent --use-system-ca .../versions/2026.08.11-e8db854/index.js --trust --yolo` |
|
|
836
|
+
|
|
837
|
+
`node` matches no harness name pattern, so a cursor pane is identified from Cursor's own name or install tree in the path or argv[0].
|
|
838
|
+
An unrelated `node` or `agent` matches neither and classifies `other`, which the liveness callers fold into `ambiguous` rather than `dead`.
|
|
839
|
+
A live cursor pane returned `alive`; a plain shell pane in the same run returned `dead`.
|
|
840
|
+
|
|
841
|
+
### Environment markers and detection ordering
|
|
842
|
+
|
|
843
|
+
Read from the live agent process and from a tool subprocess it spawned:
|
|
844
|
+
|
|
845
|
+
| Marker | Where observed |
|
|
846
|
+
| --- | --- |
|
|
847
|
+
| `CURSOR_INVOKED_AS=cursor-agent` | the agent process itself, and its children |
|
|
848
|
+
| `CURSOR_AGENT=1` | child/tool processes only |
|
|
849
|
+
| `CURSOR_CONVERSATION_ID=<uuid>` | child/tool processes |
|
|
850
|
+
| `AGENT_TRANSCRIPTS=<projects-root>/<slug>/agent-transcripts` | child/tool processes |
|
|
851
|
+
|
|
852
|
+
Cursor does not clear an inherited `CLAUDECODE`, so ordering decides the verdict.
|
|
853
|
+
With both markers set, `bin/fm-harness.sh` reports `cursor`; with `CLAUDECODE` alone it still reports `claude`.
|
|
854
|
+
|
|
855
|
+
### Composer
|
|
856
|
+
|
|
857
|
+
Cursor's composer is a BARE row whose prompt glyph is `→` (U+2192); there is no border.
|
|
858
|
+
Its idle placeholder is `Plan, search, build anything` in a fresh session and `Add a follow-up` after a completed turn.
|
|
859
|
+
|
|
860
|
+
The styled capture of an idle composer row was:
|
|
861
|
+
|
|
862
|
+
```
|
|
863
|
+
ESC[48;2;21;21;21m ESC[2m→ ESC[0;7mESC[48;2;21;21;21mPESC[0;2mESC[48;2;21;21;21mlan, search, build anythingESC[0m
|
|
864
|
+
```
|
|
865
|
+
|
|
866
|
+
The glyph and the placeholder tail are dim (SGR 2), but the cell under the terminal cursor is reverse video (SGR 0;7).
|
|
867
|
+
Reverse video is neither dim nor a dark foreground, so ghost stripping leaves a lone `P` and an idle composer read `pending` before the fix.
|
|
868
|
+
After teaching the shared classifier the glyph, both placeholders, and the plain-row remnant rule, the same captures read `empty` on the styled cursorless backends, while real typed text - including text typed to exactly match the placeholder - still read `pending`.
|
|
869
|
+
An unstyled capture has no ghost-strip proof and correctly stays `unknown`.
|
|
870
|
+
|
|
871
|
+
#### tmux composer verdict, corrected 2026-08-13
|
|
872
|
+
|
|
873
|
+
The 2026-08-11 record that a Cursor pane's tmux composer verdict is `unknown` in every state described the cursor-ANCHORED read, which remains true: `#{cursor_y}` was 25 with `#{cursor_flag}` 0 on an idle pane, pointing below the footer, so tmux's cursor row is not a composer locator for Cursor.
|
|
874
|
+
Read cursorlessly, the same live capture classifies correctly, so the composite verdict is no longer `unknown`:
|
|
875
|
+
|
|
876
|
+
```text
|
|
877
|
+
cursor_y=25 cursor_flag=0
|
|
878
|
+
with-cursor : unknown cursorless : empty (idle composer)
|
|
879
|
+
with-cursor : unknown cursorless : pending (real typed text, not submitted)
|
|
880
|
+
with-cursor : unknown cursorless : unknown (agent exited to a shell)
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
`bin/fm-tmux-lib.sh` therefore reclassifies cursorlessly only when the pane's foreground process group is provably Cursor, so every other harness keeps the strict blank-cursor-row posture.
|
|
884
|
+
That supplies the genuine composer-empty proof required for away-mode escalation delivery.
|
|
885
|
+
A live injection through `bin/fm-supervise-daemon.sh`'s own `inject_msg` into a real Cursor pane returned 0 and the pane processed the typed `FIRSTMATE_OP: v1 away-supervisor:` escalation.
|
|
886
|
+
|
|
887
|
+
`tests/fm-tmux-agent-liveness.test.sh` pins this with real processes and no Cursor installed: it asserts the cursor-anchored source is blind, that the composite still reads `empty` idle and `pending` with typed text, that an identical screen stays `unknown` when the pane is not Cursor, and that a stale Cursor screen over a dead shell never reads `empty`.
|
|
888
|
+
|
|
889
|
+
### Busy state
|
|
890
|
+
|
|
891
|
+
Cursor writes a per-conversation transcript at `<projects-root>/<workspace-slug>/agent-transcripts/<conversation-id>/<conversation-id>.jsonl`.
|
|
892
|
+
Each turn is bracketed by a `role:user` open and a typed `{"type":"turn_ended","status":...}` close.
|
|
893
|
+
Observed closes: `success` for a completed turn, and `aborted` with `"error":"User aborted/interrupted manually."` after a single Escape.
|
|
894
|
+
|
|
895
|
+
The trailing close landed 0 seconds after the pane's busy footer cleared on a normal turn.
|
|
896
|
+
The transcript does NOT accumulate one close per turn, so a count of closes is not a progress signal; only the trailing record is.
|
|
897
|
+
After an interrupt the aborted close was observed within seconds in some runs and not within twenty seconds in others, so `bin/fm-control-lib.sh` deliberately claims no cancellation acknowledgement for cursor.
|
|
898
|
+
|
|
899
|
+
Binding never reconstructs cursor's workspace-slug directory name, which collapses path separators.
|
|
900
|
+
Cursor records the exact absolute workspace path in each project directory's `.workspace-trusted`, and the binding matches on that value.
|
|
901
|
+
|
|
902
|
+
### Rendered busy token, delivery only
|
|
903
|
+
|
|
904
|
+
Mid-turn the pane showed a braille spinner plus a verb, and `ctrl+c to stop` on the composer row; both the verb line and that token were absent the instant the turn ended.
|
|
905
|
+
The same version rendered `Working` in one turn and `Running` in the next, so the TOKEN is matched and the verb is not.
|
|
906
|
+
This row is a delivery guard for submit acknowledgement only; recorded worker state comes from the transcript fold.
|
|
907
|
+
|
|
908
|
+
### Launch, lifecycle, and skills
|
|
909
|
+
|
|
910
|
+
| Fact | Observed |
|
|
911
|
+
| --- | --- |
|
|
912
|
+
| Workspace trust | `--trust` suppressed the prompt; `--yolo` alone did NOT, and the prompt blocks a fresh worktree |
|
|
913
|
+
| Autonomy | `--yolo` (alias of `--force`); the footer renders `Run Everything` |
|
|
914
|
+
| Worktree | `-w/--worktree` allocates a SECOND worktree under `~/.cursor/worktrees` and is never passed |
|
|
915
|
+
| Effort | no effort flag exists; requested effort stays in task metadata |
|
|
916
|
+
| Interrupt | single Escape; the pane showed `Cancelled` and the composer returned to its placeholder, so no clear key is needed |
|
|
917
|
+
| Exit | `/exit` |
|
|
918
|
+
| Skill invocation | `/<skill>`; cursor discovers firstmate's user-level skills, and `/no-mistakes` autocompleted with firstmate's own description and invoked the skill |
|
|
919
|
+
| Slash popup | real: the first Enter closes the popup and a SECOND Enter submits, the same hazard as grok, covered by the submit core's retried Enter |
|
|
920
|
+
|
|
921
|
+
### End-to-end
|
|
922
|
+
|
|
923
|
+
A throwaway scout was spawned through `bin/fm-spawn.sh --scout --backend tmux` on a real cursor worker and driven to completion:
|
|
924
|
+
|
|
925
|
+
1. the launch delivered its brief positionally and the agent executed it;
|
|
926
|
+
2. `state/<id>.cursor-session` was written with the task worktree;
|
|
927
|
+
3. the transcript fold read `busy` mid-turn and `idle` after it;
|
|
928
|
+
4. `bin/fm-send.sh` delivered a steer through the then-current typed path and exited 0;
|
|
929
|
+
5. `bin/fm-control.sh <id> interrupt` cancelled a running turn;
|
|
930
|
+
6. `bin/fm-control.sh <id> exit` stopped the agent;
|
|
931
|
+
7. `bin/fm-teardown.sh` refused until the scout's report and decision gate were satisfied, then removed the session record.
|
|
932
|
+
|
|
933
|
+
### Herdr backend
|
|
934
|
+
|
|
935
|
+
The tmux run above is the reference; this section is the separate Herdr proof, produced on 2026-08-12 against Herdr 0.8.0 (client and server, protocol 19) and the same signed `cursor-agent` 2026.08.11-e8db854 on macOS 26.5.2 arm64.
|
|
936
|
+
Every step ran inside an isolated `fm-lab-` session provisioned by `bin/fm-herdr-lab.sh`, launched from a neutral parent outside any Herdr pane, with the live default session's pane count checked before, during, and after; it stayed at 7 throughout.
|
|
937
|
+
|
|
938
|
+
**Herdr's native agent state is unusable for Cursor.**
|
|
939
|
+
A 60-sample probe of `agent get` across a full turn reported `agent_status=blocked` in every state - idle, mid-turn, and after.
|
|
940
|
+
The typed submit path's idle baseline is therefore structurally unreachable for Cursor, and every typed send falls into the composer branch.
|
|
941
|
+
|
|
942
|
+
| Pane state | Composer verdict | Rendered footer |
|
|
943
|
+
| --- | --- | --- |
|
|
944
|
+
| Idle | `empty` | no busy token |
|
|
945
|
+
| Text typed, not submitted | `pending` | no busy token |
|
|
946
|
+
| Mid-turn | `pending` (placeholder plus `ctrl+c to stop` on one row) | `ctrl+c to stop` |
|
|
947
|
+
|
|
948
|
+
Herdr draws the composer's rules with the half-block glyphs U+2584 and U+2580 rather than the box-drawing family.
|
|
949
|
+
Before those were taught to the shared edge detector, a bare composer's wrap region ran through its own closing rule and swallowed the model and path footer, so an idle pane read `pending`.
|
|
950
|
+
Measured as an A/B on the same live pane, the pre-fix classifier returned `pending` and the current one returned `empty`.
|
|
951
|
+
|
|
952
|
+
The idle fix alone did not confirm typed delivery, because the composer branch reads the mid-turn row instead.
|
|
953
|
+
With the rendered-footer transition in place, a typed-plane `bin/fm-send.sh` invocation exited 0 and the steer executed in the pane; the same send previously exited 1 with `delivery unconfirmed; verdict=pending` on a message that had actually landed.
|
|
954
|
+
|
|
955
|
+
The rest of the lifecycle was driven end to end on that worker:
|
|
956
|
+
|
|
957
|
+
1. `bin/fm-spawn.sh --scout --backend herdr` placed the worker and it executed its brief;
|
|
958
|
+
2. the transcript fold read `busy` mid-turn and `idle` after, unchanged from tmux, so the recorded worker state is backend-agnostic;
|
|
959
|
+
3. `bin/fm-control.sh <id> interrupt` reported `cancel=unconfirmed` by design and the pane showed `Cancelled`, with the footer and the fold both returning to idle;
|
|
960
|
+
4. `bin/fm-control.sh <id> exit` stopped the agent through the slash popup and the pane returned to its shell;
|
|
961
|
+
5. `bin/fm-teardown.sh` refused until the scout's report and decision gate were satisfied, then removed the session record and returned the worktree.
|
|
962
|
+
|
|
963
|
+
Other harnesses on Herdr are unaffected by the edge-detector change.
|
|
964
|
+
All seven live panes of the running default session - one Pi, four Claude, two plain shells - classified identically under the pre-fix and current classifiers.
|
|
965
|
+
|
|
966
|
+
**Typed-submit confirmation is verified on tmux and Herdr only.**
|
|
967
|
+
Zellij, cmux, and Orca share a submit core that never consults the busy footer, so a typed-plane Cursor send there lands but `fm-send` reports delivery unconfirmed and exits non-zero; ordinary text steers ride the durable inbox and exit 0 at enqueue.
|
|
968
|
+
Teaching that shared core the same transition is deliberately separate work, because it changes the submit path for every harness on those three backends and needs its own live validation on each.
|
|
969
|
+
|
|
970
|
+
The portable regression is `tests/fm-cursor-harness.test.sh`, the composer captures are pinned in `tests/fm-composer-lib.test.sh`, and the Herdr submit and footer behavior is pinned in `tests/fm-backend-herdr.test.sh`.
|
|
971
|
+
Refresh this harness-dependent proof before accepting a cursor upgrade:
|
|
972
|
+
|
|
973
|
+
```sh
|
|
974
|
+
FM_HARNESS_LIVENESS_DRIFT=1 bin/fm-test-run.sh tests/fm-harness-liveness-drift-live-e2e.test.sh
|
|
975
|
+
```
|
|
976
|
+
|
|
977
|
+
## Pi supervision branch
|
|
978
|
+
|
|
979
|
+
The supervision-branch extension (`.pi/extensions/fm-branch-supervision.ts`, [docs/pi-supervision-branch.md](../pi-supervision-branch.md)) builds its persistent second session through the Pi SDK surface: `createAgentSession` (including its `model`, `modelRuntime`, and `thinkingLevel` options), `DefaultResourceLoader` with `extensionFactories`, `SessionManager`, `createBashToolDefinition` with a `spawnHook`, `sendCustomMessage`, the `before_provider_request` hook, the command context's model registry for picker candidates, a fresh `ModelRuntime` for isolated-branch resolution, and Pi's own `getSupportedThinkingLevels`/`clampThinkingLevel` plus its `getThinkingLevel` and `thinking_level_select` extension surface for effort.
|
|
980
|
+
In TUI mode, its `/supervision-model` model list is drawn with Pi's own `SelectList`, `Input`, `fuzzyFilter`, and `DynamicBorder` through the extension context's `ui.custom` surface, which is what bounds and searches a long catalog.
|
|
981
|
+
|
|
982
|
+
Evidence produced 2026-08-25 on macOS 26.5.2 arm64, Node v24.13.1:
|
|
983
|
+
|
|
984
|
+
- Real-SDK guard: `FM_PI_BRANCH_LIVE_E2E=1 bin/fm-test-run.sh tests/fm-pi-branch-live-e2e.test.sh` against the globally installed `@earendil-works/pi-coding-agent` 0.81.1 printed `ok - real Pi SDK 0.81.1 accepts the branch session construction and preserves an unpromptable wake`.
|
|
985
|
+
The guard reads no credentials and makes no provider call: an isolated empty `PI_CODING_AGENT_DIR` leaves model resolution empty, so the branch's first prompt fails fast and must prove the fallback that returns the wake to main.
|
|
986
|
+
The same run confirms that a real `ModelRegistry` over that empty agent dir still exposes the picker-facing availability surface, then pins `openai/no-such-live-model` and proves that the branch's own `ModelRuntime` refuses the unresolvable pin instead of silently running supervision on main's model.
|
|
987
|
+
- Model-pin precedence: the same guard run printed `ok - real Pi SDK 0.81.1 applies an explicit branch model on create and over a reopened session's recorded model`.
|
|
988
|
+
It declares a local `fm-live-fake` provider in an isolated `models.json`, never contacts it, and proves through `session.model` that an explicit model is applied on create, still wins over the model a reopened session recorded, and is absent-pin-restorable - the exact behavior a pin that must survive `/new`, `/resume`, `/fork`, and reload depends on.
|
|
989
|
+
- Effort-pin vendor contract: the same guard run printed `ok - real Pi SDK 0.81.1 reports its own supported effort levels and applies an explicit branch effort over a reopened session's recorded level`.
|
|
990
|
+
Over its own local never-contacted provider it confirms that `getSupportedThinkingLevels` still returns `["off","minimal","low","medium","high","xhigh","max"]` for a model mapping every extended level, narrows to `["off","minimal","low","medium","high"]` for a reasoning model mapping none, returns `["off"]` for a non-reasoning model, and that `clampThinkingLevel` lowers `max` to `high` on the narrow model while collapsing an unrecognized token to `off` - which is why the extension rejects an unrecognized pin before that clamp can see it.
|
|
991
|
+
It then proves through `session.thinkingLevel` that an explicit effort is applied on create, that a reopened session with no override restores its own recorded level, that an explicit effort beats that recorded level, and that an over-ceiling effort is clamped rather than refused.
|
|
992
|
+
The recorded-level cases need a session file Pi will actually restore from, and Pi flushes one only once an assistant message exists, so the guard appends the level change and that message through the real `SessionManager` rather than hand-writing the format.
|
|
993
|
+
- Picker primitives: on 2026-08-26, after the final portable-shell and sentinel fixes, `bin/fm-test-run.sh tests/fm-pi-branch-extension.test.sh` again printed `ok - the installed Pi still bounds the picker's list and ranks its search` against the same installed 0.81.1 package.
|
|
994
|
+
That case imports the real `SelectList`, `Input`, `fuzzyFilter`, and `DynamicBorder`, renders a 42-row catalog through the real `SelectList` at the visible bound the extension asks for, and fails naming the installed version if Pi stops exporting a primitive or stops bounding what it renders; it skips when no npm package is installed, and the portable stubbed cases in the same file hold the ordering, search, and branch-only-pin behavior everywhere.
|
|
995
|
+
- Strict typecheck: `tests/fm-pi-primary-types.test.sh` printed `ok - tracked Pi extensions pass strict no-emit typecheck against Pi 0.81.1` with the branch extension and its imported libraries included.
|
|
996
|
+
This typecheck is also the enforcement for the extension's declared effort vocabulary: its bidirectional assertion against Pi's own `getThinkingLevel` return type fails the moment Pi adds or removes a thinking level, so the runtime list used to reject an unrecognized hand-edited pin cannot drift into a stale Firstmate catalog.
|
|
997
|
+
- Custom-message provider conversion: on 2026-08-26, `FM_PI_BRANCH_LIVE_E2E=1 bin/fm-test-run.sh tests/fm-pi-branch-live-e2e.test.sh` against installed `@earendil-works/pi-coding-agent` 0.84.1 printed `ok - real Pi SDK 0.84.1 delivers a custom message to the provider as user text carrying only content, so the captain outcome's typed envelope is what reaches the model`.
|
|
998
|
+
The guard passes a typed captain outcome and a plain rendered routine note through Pi's exported `convertToLlm`, proves that `customType` and `display` are not model-visible identity, and classifies the resulting provider text with `bin/fm-operational-input.sh`.
|
|
999
|
+
|
|
1000
|
+
### 2026-08-28 Pi 0.84.4 SDK compatibility refresh
|
|
1001
|
+
|
|
1002
|
+
The credential-free live guard and strict typecheck were rerun against the installed `@earendil-works/pi-coding-agent` 0.84.4 package after the Pi primary compatibility repair.
|
|
1003
|
+
The live guard used an isolated empty `PI_CODING_AGENT_DIR`, inspected no credentials, and made no provider call.
|
|
1004
|
+
|
|
1005
|
+
```sh
|
|
1006
|
+
npm exec --yes --package=typescript@5.9.3 -- bash tests/fm-pi-primary-types.test.sh
|
|
1007
|
+
FM_PI_BRANCH_LIVE_E2E=1 bin/fm-test-run.sh tests/fm-pi-branch-live-e2e.test.sh
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
```text
|
|
1011
|
+
ok - tracked Pi extensions pass strict no-emit typecheck against Pi 0.84.4
|
|
1012
|
+
ok - real Pi SDK 0.84.4 accepts the branch session construction and preserves an unpromptable wake
|
|
1013
|
+
ok - real Pi SDK 0.84.4 applies an explicit branch model on create and over a reopened session's recorded model
|
|
1014
|
+
ok - real Pi SDK 0.84.4 reports its own supported effort levels and applies an explicit branch effort over a reopened session's recorded level
|
|
1015
|
+
ok - real Pi SDK 0.84.4 delivers a custom message to the provider as user text carrying only content, so the captain outcome's typed envelope is what reaches the model
|
|
1016
|
+
FM_TEST_END 2026-08-29T01:01:01Z tests/fm-pi-branch-live-e2e.test.sh exit=0 duration_ms=2520 gate_skip=false
|
|
1017
|
+
```
|
|
1018
|
+
|
|
1019
|
+
The focused extension suite also exercised the installed Pi 0.84.4 picker and outcome-renderer consumers; [`calm-mode-feasibility.md`](../calm-mode-feasibility.md#2026-08-28-pi-0844-outcome-renderer-compatibility-verification) owns the version-scoped renderer evidence.
|
|
1020
|
+
|
|
1021
|
+
Scope of the earlier evidence: the installed signed `pi` CLI (0.82.0 at verification time) is a compiled binary whose bundled SDK is not importable from Node, so the importable npm package is the only surface the guard and the typecheck can pin.
|
|
1022
|
+
The extension executes inside the signed CLI's own runtime, so a CLI upgrade can drift ahead of the pinned npm surface; refresh this record after every Pi upgrade by re-running the live guard, picker regression, and strict typecheck above (point `FM_PI_PACKAGE_DIR` at a matching npm install when one exists) and by watching the branch's own fallback line - every branch failure degrades to the pre-branch wake-to-main path by construction, which `tests/fm-pi-branch-extension.test.sh` holds with a broken generator and the live guard holds with the real SDK.
|