pmacs/tests
Levi Neuwirth 689fb8333d
feat(workers): a required purpose on every job and process — worker identity Stage 1
`COHERENCE.md` §9 grades the worker model "mechanism without identity",
and §0 names step 11 (background-work ownership) as one of the two
remaining thin ends of the golden journey. The mechanism half is solid —
cancellation, supersession, streaming, frame-aware draining, `*workers*`.
The identity half was absent: `PendingJob` carried no description of what
it was doing, `pmacs.workers.dispatch` discarded the registered handler
name three layers above anything that takes one, and §9's "no progress
indicator exists anywhere" was checkable and true.

Framing: `docs/worker-identity-framing.md` (revision 4, approved).

What lands:

**A required `purpose`, on the job and on the process.** Non-optional,
with no `Default`, so the compiler — not a test — is what proves every
dispatcher supplied one. `allocate` / `allocate_with_resource` collapse
into ONE private `JobSpec`-taking funnel (Q#W-1): the two-function split
existed only because one prior lane needed one extra parameter, and a
second lane doing the same produces `allocate_with_resource_and_identity`.
`register_external` gains a `purpose` parameter rather than deriving one,
because its `JobKind` is `McpRequest`/`LspRequest` for every method — a
category, not a description.

**A dispatch-name ambient (Q#W-2), read at that same single funnel.** The
capture point is Rust, not the Lua wrapper layer, because a handler
reaching straight for `pmacs._async._dispatch_*` bypasses the wrappers
entirely — and those are precisely the callers attribution exists for.
Seven rules; the ones that decide whether it is honest:

- **Rule 1 — the extent is NON-YIELDABLE, and that is ENFORCED.** Both
  supported yield APIs refuse inside it, modelled on the `commit_to`
  refusal already in `async.lua`. The guards reject BEFORE parking and
  reject UNCONDITIONALLY: one placed after `_is_complete` would fire only
  when a yield really occurred, passing under test and failing
  intermittently in production.
- **A raw `coroutine.yield` is NOT covered, and nothing here claims it
  is.** R46 is a convention, and the scheduler inspects the yielded value
  only after `coroutine.resume` returns — by which point the coroutine has
  already suspended — so no refusal sited in a yield helper is ever
  consulted. The residual is recorded in the framing §2 and in the
  suite's module docs rather than papered over with a test that would
  imply coverage this design lacks.
- **Rule 5 — unwind-safe.** A raising handler still pops. A version that
  did not would let one failure poison every later dispatch in the session
  with a stale name: the feature would stop failing loudly and start lying
  silently. The bracketing also has to preserve the tail call it replaced:
  `dispatch` was `return handler(args, opts)` and propagated EVERY return
  value, so the pop/rethrow runs behind a varargs boundary rather than a
  `local ok, result = pcall(...)` that would silently truncate a
  multi-value handler. Varargs rather than `table.pack`, because that is
  Lua 5.2 surface and LuaJIT is this project's default backend.
- **Rule 6 — compose, do not replace.** `"<name>: <purpose>"`, because
  letting the dispatcher's purpose win loses the third party again and
  letting the name win discards the only description of the actual work.

**A statusline activity indicator** — the fourth `pmacs.statusline.register`
adopter, after `mode`, `terminal` and `lsp`. A count plus the OLDEST
in-flight job's purpose ("busiest" is not a defined quantity; jobs carry
no cost estimate), and **absent entirely** when idle rather than a
zero-width segment that costs modeline width forever to say nothing is
happening. Gated by one setting, `ui.activity-indicator` (boolean, default
true, Q#W-6) — a permanently-visible modeline element is a preference
someone genuinely holds on day one. No setting for purpose capture
itself: that is substrate.

**NO WIRE CHANGE.** The indicator rides the existing `StatuslineSegments`
vector, so a fourth provider adds an element, not a variant.
`PROTOCOL_VERSION` and `ADVERTISED_PROTOCOL_VERSION` are untouched — which
is the property that lets this run beside the two lanes holding the bump
slot.

**Q#W-7 — a pre-existing defect, repaired here, and NOT one anybody has
observed.** `Handle:await()` refuses inside `pmacs.window.commit_to`
precisely so a coroutine cannot park with the frontend scope pushed
(Journey Stage 1a, Q#JR14b). But `pmacs.async.yield_to_next_tick()` also
yields, is public, and carried no such refusal — so that invariant had a
second entrance, and a coroutine could produce exactly the misrouting the
`await` guard exists to prevent. It gains both refusals here: the same
supported yield helper, the same invariant, the same edit family, so
splitting it would have preserved a known hole without reducing
integration risk.

**Reachability by a real caller is UNPROVEN.** This was found by reading
the guard family while scouting rule 1, not by reproducing a fault. No
production caller is known to yield through that door inside a commit,
and the test pins the guard rather than reproducing a user-visible bug.
Nobody should later cite this commit as evidence the bug was observed in
the wild. Its witness is a PAIR, like rule 1's: the refusal fires **and**
the commit scope is restored afterwards — a guard that raises while
leaving the scope pushed converts a silent fault into a loud one and
fixes neither.

`journey_acceptance` carries the established `commit_to` pins —
forged-destination refusal, scope-and-restore on normal return and on
raise, the await refusal, delivery to the requesting frontend. It passes
**untouched**, which is what says this closed a gap in Journey Stage 1a's
semantics rather than altering them.

What is deliberately NOT here, and why it is worth saying:

- **No `owner`, in any spelling** — not `origin`, not `subsystem` (§3).
  Populated from static per-subsystem constants it would be an origin,
  not an owner, and would confidently misattribute third-party work to a
  builtin at exactly the point §9 wants attribution. A field that asserts
  a falsehood is worse than an absent one. The slot stays empty until P3
  can fill it with a real package signal.
- **No `parent`** (Q#W-5). An unpopulated field renders as `None`
  everywhere and reads as "this job has no parent" rather than "this
  system does not track parents". Stage 3 builds the lifetime model and
  the field together.

Consequences worth recording:

- `ProcessSpec::new` takes a third argument. The 40-odd call sites are
  almost all tests; the three production ones (LSP, MCP, terminal) supply
  real descriptions. `pmacs.process.spawn`'s Lua surface keeps `purpose`
  OPTIONAL, falling back to the label — requiring it there would break
  every existing caller for no coverage the compiler is not already
  providing, and a caller's own label is not a fabrication.
- `pmacs.process.list` gains a `purpose` KEY on each row and enumerates
  exactly the same processes (Q#W-4). Terminal PTYs stay hidden: three
  acceptance suites use `#pmacs.process.list()` as a leak baseline, and
  widening the accessor would inflate all three. Stage 2's unified view
  owns that decision.
- `statusline_segments_acceptance`'s builtin-provider inventory grows to
  `["activity", "mode", "terminal", "lsp"]`. That assertion exists to
  grow when a builtin provider is added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
2026-08-10 14:51:54 +02:00
..
common test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
fixtures V0.2-prerequisite pull-forward + M10.11 clean audit round 2026-05-18 10:31:31 -04:00
support docs: record the pipe-masking gate trap in the handoff 2026-07-29 12:25:29 -04:00
acceptance.rs Collapse if-let nests into let-chains (MSRV-1.95 collapsible_if sweep) 2026-05-18 14:29:36 -04:00
ambient_isolation_acceptance.rs fix(isolation): the isolation suite must not itself be ambient 2026-07-31 19:46:18 -04:00
auto_indent_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
auto_indent_crdt_acceptance.rs fix(lint): make the crdt targets pass clippy for the first time 2026-08-01 09:38:36 -04:00
auto_pair_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
auto_pair_crdt_acceptance.rs feat(edit): auto-pairing (Arc 2) 2026-07-11 17:11:56 +01:00
autosave_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
bottom_panel_stage1_acceptance.rs test(panel): pin that the OMITTED default degrades on a pre-panel frontend 2026-08-03 11:16:08 -04:00
bottom_panel_stage2a_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
bottom_panel_stage2b_daemon_acceptance.rs fix(tests): eight version assertions the v22 bump broke, five of them defects 2026-08-07 20:15:33 +02:00
bottom_panel_stage2b_gpu_acceptance.rs feat(discovery): M-x rows carry descriptions — protocol v22 -> v23 2026-08-10 13:52:32 +02:00
bottom_panel_stage2b_protocol_acceptance.rs fix(tests): eight version assertions the v22 bump broke, five of them defects 2026-08-07 20:15:33 +02:00
comment_toggle_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
compile_mode_acceptance.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
compile_mode_crdt_acceptance.rs review round 1: the crdt suite Stage 3 missed, and two stale explanations 2026-08-04 12:08:28 +02:00
completion_popup_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
config_registry_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
cua_region_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
desktop_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
dired_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
discovery_acceptance.rs fix(help): forwarders must work programmatically, not only from M-x 2026-07-31 20:31:00 -04:00
discovery_stage2_acceptance.rs fix(discovery): clip command descriptions at the single-row surfaces 2026-08-10 13:52:32 +02:00
editops_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
find_file_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
folding_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
folding_stage2_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
full_grid_resync_acceptance.rs fix(frontend): honor full_grid — the flag existed and nothing read it 2026-08-06 15:05:59 +02:00
gate_script_acceptance.rs test(gate): assert the real plan's (name, command) pair, not one half 2026-08-09 18:02:36 +02:00
gpu_font_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
gpu_initial_target_acceptance.rs Implement session-scoped GPU initial targets 2026-07-23 19:03:25 -04:00
gpu_invocation_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
gui_zoom_acceptance.rs docs(zoom): the ties both round up, they do not oppose 2026-08-06 17:50:03 +02:00
horizontal_scroll_acceptance.rs docs(test): five adopters, four of them decorator families 2026-08-07 22:49:59 +02:00
injection_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
journey_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
kill_ring_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
lean4_server_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
lean4_stage1_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
lean_input_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
line_wrap_acceptance.rs fix(editor): the renderer never got the mode everything else was reading 2026-08-07 18:48:11 +02:00
listview_acceptance.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
long_line_readable_acceptance.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
lsp_dispatch_seams_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
lsp_latex_acceptance.rs fix(lsp): a `/` search boundary no longer disables the LaTeX marker walk 2026-08-10 10:48:49 +02:00
lsp_multi_root_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
lsp_spawn_guidance_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m3_acceptance.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
m4_acceptance.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
m5_5_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m5_6_acceptance.rs Initial commit: v0.1.0 2026-05-03 19:51:06 -04:00
m5_7_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m5_8_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m5_perf_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m6_4_repl_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m6_5_repl_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m6_7_scrollback_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m6_8_multi_repl_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m6_perf_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m7_3_acceptance.rs Fix CI and Documentation issues 2026-05-04 10:19:19 -04:00
m7_5_acceptance.rs fix(packages): basename-collision reject, SHA-256 cache key, timeout thread join, commit→revision, dead-code (F-005/F-009–F-012) 2026-07-03 18:47:23 -04:00
m7_6_acceptance.rs fix(packages): basename-collision reject, SHA-256 cache key, timeout thread join, commit→revision, dead-code (F-005/F-009–F-012) 2026-07-03 18:47:23 -04:00
m7_7_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m7_8_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m7_9_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m7_10_acceptance.rs fix(packages): basename-collision reject, SHA-256 cache key, timeout thread join, commit→revision, dead-code (F-005/F-009–F-012) 2026-07-03 18:47:23 -04:00
m7_11_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m7_review_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m8_1_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_1c_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m8_1d_acceptance.rs M7 tail: package system, audit lint, lockfile, resolver 2026-05-07 16:50:37 -04:00
m8_2_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_3_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_5_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_6_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_7_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_9_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m8_10_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_1_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_2_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_3_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_4_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_5_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_6_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_7_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m9_8_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
m10_2_perf.rs M10.10 ship gate 2026-05-13 16:28:46 -04:00
m10_10_perf.rs M10.10 ship gate 2026-05-13 16:28:46 -04:00
m10_10_postcard_unknown_variant.rs M10.10 ship gate 2026-05-13 16:28:46 -04:00
m10_11_acceptance.rs M11.1: semantic-frontend protocol scaffolding (wire + capability) 2026-05-18 19:39:07 -04:00
m10_11_perf.rs M10.11: adversarial two-laptop acceptance + jitter; the M10 arc verified 2026-05-15 20:51:00 -04:00
m11_5_semantic_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
mode_system_wiring_acceptance.rs Keep the v21 panel wire dark for v20 clients 2026-07-28 14:08:17 -04:00
overlay_reattach_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
persistence_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
query_replace_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
resource_reconciliation_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
save_clobber_guard_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
statusline_segments_acceptance.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
tab_width_acceptance.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
terminal_config_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
terminal_copy_mode_acceptance.rs feat(view): horizontal scroll, text and decorations together 2026-08-07 22:43:17 +02:00
theme_faces_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
typed_edit_chain_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00
vterm_stage1_acceptance.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
vterm_stage2_acceptance.rs fix(test): retire R2 and R4 — two readiness predicates weaker than their assertions 2026-08-05 13:41:05 +02:00
vterm_stage3_acceptance.rs feat(discovery): M-x rows carry descriptions — protocol v22 -> v23 2026-08-10 13:52:32 +02:00
worker_identity_acceptance.rs feat(workers): a required purpose on every job and process — worker identity Stage 1 2026-08-10 14:51:54 +02:00
worker_shutdown_acceptance.rs test(isolation): migrate the corpus off the ambient roots 2026-07-31 18:48:45 -04:00