@plurnk/plurnk-service 0.64.0 → 0.68.0
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/.env.example +110 -54
- package/INSTALL.md +69 -0
- package/README.md +1 -1
- package/SPEC.md +51 -46
- package/dist/Paths.js +5 -5
- package/dist/Paths.js.map +1 -1
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +17 -0
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +5 -0
- package/dist/core/Dispatcher.d.ts +1 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +12 -5
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +1 -0
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +65 -59
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +1 -0
- package/dist/core/ExecutorRegistry.js +2 -2
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/PacketBuilder.js +11 -11
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProposalLifecycle.js +1 -1
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts +2 -0
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +36 -1
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/SchemeRegistry.js +2 -2
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +5 -1
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/fork.js +1 -1
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +2 -2
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +18 -6
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.js +2 -2
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/packet-inject.js +8 -8
- package/dist/core/packet-inject.js.map +1 -1
- package/dist/core/packet-wire.js +2 -2
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/run-cap.js +3 -3
- package/dist/core/run-cap.js.map +1 -1
- package/dist/core/run-ops.sql +1 -1
- package/dist/core/session-settings.js +3 -3
- package/dist/core/session-settings.js.map +1 -1
- package/dist/core/teaching.d.ts.map +1 -1
- package/dist/core/teaching.js +2 -2
- package/dist/core/teaching.js.map +1 -1
- package/dist/digest/Digest.js +2 -2
- package/dist/digest/Digest.js.map +1 -1
- package/dist/schemes/Plurnk.d.ts.map +1 -1
- package/dist/schemes/Plurnk.js +8 -19
- package/dist/schemes/Plurnk.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +10 -4
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +3 -0
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-manifest.d.ts +7 -0
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +87 -33
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +4 -0
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.d.ts +2 -1
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +32 -13
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-semantic.sql +15 -0
- package/dist/schemes/exec-abort.js +1 -1
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/server/ClientConnection.js +2 -2
- package/dist/server/ClientConnection.js.map +1 -1
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +7 -6
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/methods/loop_inject.js +3 -3
- package/dist/server/methods/loop_inject.js.map +1 -1
- package/dist/server/methods/loop_run.js +5 -5
- package/dist/server/methods/loop_run.js.map +1 -1
- package/dist/server/methods/mcp_install.js +4 -4
- package/dist/server/methods/mcp_install.js.map +1 -1
- package/dist/server/methods/op_copy.js +1 -1
- package/dist/server/methods/op_copy.js.map +1 -1
- package/dist/server/methods/op_dispatch.js +1 -1
- package/dist/server/methods/op_dispatch.js.map +1 -1
- package/dist/server/methods/op_edit.js +1 -1
- package/dist/server/methods/op_edit.js.map +1 -1
- package/dist/server/methods/op_exec.js +1 -1
- package/dist/server/methods/op_exec.js.map +1 -1
- package/dist/server/methods/op_move.js +1 -1
- package/dist/server/methods/op_move.js.map +1 -1
- package/dist/server/methods/session_create.js +1 -1
- package/dist/server/methods/session_create.js.map +1 -1
- package/dist/server/version-info.js +1 -1
- package/dist/server/version-info.js.map +1 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +25 -11
- package/dist/service.js.map +1 -1
- package/migrations/0000-00-00.01_schema.sql +1 -0
- package/package.json +19 -16
package/SPEC.md
CHANGED
|
@@ -144,9 +144,9 @@ Server posture: this package is the runtime. User-facing CLI lives in `plurnk` a
|
|
|
144
144
|
|
|
145
145
|
**Migration path.** Largely realized: `Engine.dispatch` is origin-agnostic; client ops run in a per-connection client loop (`_dispatchAsClient`); plurnk EDITs already carry `origin=plurnk`. The keystone is **built** — `dispatchAsPlurnk` spawns the session's reserved `plurnk` run and fires ops through dispatch, mirroring `_dispatchAsClient`; its uses so far (operator docs below; the fs-divergence narration) land in the plurnk run's log. The line that remains is one of *kind*, not a list of pending dispatches: work **expressible as an op** belongs on the keystone; work that is **not** stays kernel. Disk→entry materialization is the latter — *ingestion* is the inverse of an EDIT (which proposes egress to disk, §membership-edit-membership-gate), so it has no actor op and remains fs-watch kernel, paired with the plurnk run's filtered `source=file` narration (§env-delta) so a sibling pulls only true divergences, not every re-sync; the manifest build is likewise the per-turn derivation pump — packet-assembly kernel, not an entry-creating op. The one outstanding *expressible* piece is **git auto-add** — a model-created file surfaced as a plurnk-run op — gated on the §membership repo-overlay still being built.
|
|
146
146
|
|
|
147
|
-
**The keystone's first use: operator reference docs.** `
|
|
147
|
+
**The keystone's first use: operator reference docs.** `PLURNK_SERVICE_MD_<ALIAS>=<path>` (§operator-config) materializes `<path>` as a `plurnk:///<ALIAS>.md` entry — a `dispatchAsPlurnk` EDIT in the plurnk run, **not** the model's — and the model's turn-0 foists a READ of it. The model reads the doc inline while the materializing EDIT stays out of its log: idiomatic context injection, an ordinary entry + READ rather than a bespoke packet section. The same `PLURNK_SERVICE_MD_*` convention cascades to clients. {§actor-boundary-doc-injection}
|
|
148
148
|
|
|
149
|
-
**Catalog preview.** `
|
|
149
|
+
**Catalog preview.** `PLURNK_SERVICE_FILES_ITEMS` foists a turn-0 `FIND(scheme:///**)` per scheme into the run's first turn (the same plurnk-origin foist as the docs), so a run opens with its catalog instead of blank. The model's own surface — `known`/`unknown` (memory), `run` (scratch), `plurnk` (docs) — always foists **full**; the first-`N` cap applies **only to `file`** (the **bare** `FIND(**)` — the project-relative path shape plurnk.md teaches — over the external, arbitrarily-large tracked-file tree), so the model's own memory is never truncated (a partial view of memory reads as withheld). `-1` = everything full; a positive `N` = the file list capped to its first `N` (FIND's `<L>`, clamped so the strict marker never 416s; memory still full); unset / `0` = no preview (the model FINDs on demand). `log://` is absent — present-mode (the `# Log` section), not a catalog scheme. {§actor-boundary-catalog-preview}
|
|
150
150
|
|
|
151
151
|
### §machine-processes The machine and its processes: session, run, fork
|
|
152
152
|
|
|
@@ -190,7 +190,7 @@ Beyond the three creation ops:
|
|
|
190
190
|
- **Scratch KILL (delete)** — `KILL(run://<owner>/<path>)` with an entry **path present** deletes that scratch entry (200; 404 if absent), self-only like EDIT (a cross-run delete is **403**) — the model's curation lever over its own workspace, distinct from the path-ABSENT `KILL(run://<name>)` which terminates the run (§run-scheme-terminate). The discriminator is the entry path, never the op. {§run-scheme-scratch-kill}
|
|
191
191
|
- **Perspective** — a run's own scratch is catalogued in **its** manifest alone — `Manifest(run) = session-scope ∪ this-run's-run-scope`, foisted as `FIND(run://self/**)` at turn 0 — so a sibling reaches it only by explicit `FIND(run://<name>/**)` and never sees it in its own perspective; isolation is structural (`scope='run'` is excluded from every session query, the owner opted back in only on its own read paths). {§run-scheme-find-perspective}
|
|
192
192
|
- **Terminate** — `KILL(run://<name>)` aborts a run by address (self is `run://self`): its active loop closes 499 and its subscriptions tear down; a name with no run is 404. The override to the fire-and-forget default — not a parent-power, whoever holds the address may end it; a run left alone simply ends at its own `SEND[200]`. {§run-scheme-terminate}
|
|
193
|
-
- **Cap** — `
|
|
193
|
+
- **Cap** — `PLURNK_SERVICE_SESSION_RUNS_MAX_ACTIVE` ceilings the *concurrent* active runs per session (a run with a non-terminal loop); a spawn or fork past it fails hard (508 — no queue, no retry), irc exempt; `-1` disables it. The fork-bomb brake, sized for sessions that live for months. {§run-scheme-cap}
|
|
194
194
|
- **Collect** — a run's loop reaching a terminal status surfaces to its sisters as an ambient delta (§env-delta): a `SEND` from `run://<name>` carrying the loop's deliverable — the `SEND[200]` body, or for an abandonment the reason. A **2xx deliverable is born OPEN** (its body materialized into the parent's packet, not hidden behind a fold): a child's success must reach the parent open and awakening, never a bodyless row. An abandonment (non-2xx) surfaces folded. Every death-path is stamped uniformly, so no termination is silent; collection is the shared world moving, never a verb. **Child orientation.** Beyond the conclusion delta, every turn the system packet surfaces the live things THIS run *currently holds* — open streams (`## Plurnk Service Child Streams`) and unconcluded child runs (`## Plurnk Service Child Runs`) — as terse `* <status> <path>` pointers (the same shape as the errors section), just above it. A worker is otherwise marked only at spawn and at conclusion; in between it goes silent, so a model loses track of what it holds and premature-terminates. This is ORIENTING STATE, never advice: the model SEES its live subtree (`* 102 run://worker-x`, `* active sh:///1/2/3`) and reasons for itself — READ/OPEN/KILL via the path — the error stays terse. Empty → omitted, like errors. {§child-orientation} The **pull** side mirrors the push: a path-absent `READ(run://<name>)` collects the same deliverable on demand — the latest loop's terminal message (the result, or the abandonment reason) for a concluded run; a run **still running** has not delivered, so the READ returns **425** steering the model to `SEND[202]` and await the push. A missing name is 404. So the model never needs to guess a scratch path to "check on" a worker — reading the run itself yields its outcome or a wait. {§run-scheme-collect}
|
|
195
195
|
|
|
196
196
|
### §run-lifecycle Run lifecycle: the drain, the reap, the passive wake
|
|
@@ -198,7 +198,7 @@ Beyond the three creation ops:
|
|
|
198
198
|
A run is a **log plus a cancellation scope** — one `AbortController` per run, reused while live and replaced only once aborted, so a cancel ends the run as a unit and a later `loop.run` is never born cancelled. A run's queued loops are advanced by a **drain**: a single per-run worker that claims loops atomically (status 100→102) and runs each under the run's scope. A loop may spawn **streams** (execs) that outlive it; each is a row in the subscription registry (§subscriptions) — the durable record of what the run holds open. Cancellation and conclusion are defined against these structures, never wall-clock timing.
|
|
199
199
|
|
|
200
200
|
- **One drain advances a run.** At most one drain is registered for a run at any instant: a `loop.run` or wake on a run with a live drain folds in (active→next-turn) or enqueues a loop that drain claims, never a second parallel drain. A drain's start and its empty-queue teardown relinquish run under one per-run lock, so the teardown's re-claim cannot race a concurrent start into a double-drain. {§run-lifecycle-single-drain}
|
|
201
|
-
- **A cancel reaps every stream the run holds — by the registry.** `loop.cancel` / `KILL` / shutdown abort the run scope AND iterate the run's open subscriptions, aborting each via its owning scheme; the registry is the source of truth, the in-process abort signal a fast-path optimization. A stream that is running, mid-spawn (its row written before it is killable), or spawned after the cancel is reaped alike. The teardown abort is a BOUNDED reap — the executor sends a polite signal then SIGKILL after a consumer-set grace (`
|
|
201
|
+
- **A cancel reaps every stream the run holds — by the registry.** `loop.cancel` / `KILL` / shutdown abort the run scope AND iterate the run's open subscriptions, aborting each via its owning scheme; the registry is the source of truth, the in-process abort signal a fast-path optimization. A stream that is running, mid-spawn (its row written before it is killable), or spawned after the cancel is reaped alike. The teardown abort is a BOUNDED reap — the executor sends a polite signal then SIGKILL after a consumer-set grace (`PLURNK_SERVICE_EXEC_KILL_GRACE_MS`), so a signal-ignoring stream can't wedge it; a model `KILL[code]` on a live stream instead delivers exactly that signal once (bare `KILL` → the executor's SIGHUP default, `KILL[9]` → SIGKILL), the model owning any escalation. {§run-lifecycle-total-reap}
|
|
202
202
|
- **A stream's kill binds to the scope it captured at spawn.** A stream captures the run's cancellation scope as it registers and wires its kill to it, re-checking `aborted` AFTER wiring — no check-then-listen gap can drop an abort that lands mid-registration. Because the scope is replaced only once aborted, a captured-then-replaced scope is necessarily already aborted, so replacement never strands a live stream. {§run-lifecycle-exec-epoch-bound}
|
|
203
203
|
- **A cancelled run is not resurrected by its own torn-down work.** A stream conclusion delivered to a cancelled, idle run starts no fresh drain: an aborted (499) conclusion is skipped, and a straggler that concluded cleanly surfaces its deliverable as an environment delta (§env-delta), never a revived loop. The cancel was deliberate; only an explicit `loop.run` resumes the run. {§run-lifecycle-no-resurrection}
|
|
204
204
|
- **A stream conclusion always reaches its run.** When a backgrounded stream concludes, the daemon routes it through the same inject seam as any loop source (§actor-boundary-passive-wake): an active run folds the conclusion into its next turn; a run parked at a **slept loop** (`SEND[202]`) **awakens that loop in place** — the slept loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. The result is never lost: a parked loop sleeps rather than ending, and the stream's status-transition is the OPEN event (§actor-boundary-passive-wake) that wakes it; on resume it reads the concluded stream's own state, not a synthetic prompt. {§run-lifecycle-wake-liveness}
|
|
@@ -254,6 +254,8 @@ Deferred (#249): grounding the attribution value in real per-turn value flow rat
|
|
|
254
254
|
|
|
255
255
|
Model alias parsing (`parseAliasesFromEnv` / `resolveActiveAlias`) lives in [`@plurnk/plurnk-providers`](https://github.com/plurnk/plurnk-providers). {§provider-instantiation-alias-resolution} Dynamic provider instantiation (`instantiateProvider` / `loadActiveProvider`) lives in `src/core/ProviderInstantiate.ts` here — `import()` resolves package specifiers relative to the calling module, so the dynamic-import path stays in the consumer where the `@plurnk/plurnk-providers-<vendor>` packages are installed.
|
|
256
256
|
|
|
257
|
+
**Grammar enforcement is verified at boot.** When the operator sets `PLURNK_PROVIDERS_GBNF`, `loadActiveProvider` forces a trivial grammar (`root ::= "PLURNK-RAILS-LIVE"`) and confirms the backend returned exactly that — a live end-to-end proof the rails engaged. Anything else **fails hard at boot**: the openai provider only transports the grammar when its probe detects llama-server (grammarStyle `llamacpp`), and any probe hiccup silently falls back to `none` — unconstrained generation with no signal, the whole grammar contract dark, model rambles that read as reasoning failure. The Provider interface exposes no capability to introspect this, so the contract is *verified* rather than trusted; a legible boot refusal beats silent garbage. No-op when GBNF is unset/`0` (unconstrained is then a deliberate mode). {§grammar-enforcement-verified-at-boot}
|
|
258
|
+
|
|
257
259
|
```
|
|
258
260
|
PLURNK_MODEL_gemma=openai/macher.gguf
|
|
259
261
|
PLURNK_MODEL_opus=openrouter/anthropic/claude-opus-latest
|
|
@@ -545,10 +547,12 @@ OPEN/FOLD operate on the **log** (`log:///`) — the model's context-curation su
|
|
|
545
547
|
|
|
546
548
|
### §model-entry The model's own emission, mirrored back
|
|
547
549
|
|
|
548
|
-
A `model` log row is the model's **verbatim prior emission**, mirrored back so it can finally SEE its own behavior — and reason through its own syntax errors (the parser reports by line; the row renders line-numbered like all content). Actionless, like an `op='error'` row (§telemetry): no target, no op executed; `tx` is empty and the emission lives in `rx.content`, typed `text/vnd.plurnk`. **
|
|
550
|
+
A `model` log row is the model's **verbatim prior emission**, mirrored back so it can finally SEE its own behavior — and reason through its own syntax errors (the parser reports by line; the row renders line-numbered like all content). Actionless, like an `op='error'` row (§telemetry): no target, no op executed; `tx` is empty and the emission lives in `rx.content`, typed `text/vnd.plurnk`. **Always born FOLDED** (budget-neutral) — the retired born-OPEN-on-error auto-trigger was conditional helpfulness that bred its own hazards (a giant erred emission mirrored open re-injects itself into the next packet: cost, contamination, pressure feedback). An error's reported `line:col` resolves the way everything else does: the model that cares `READ`s the folded row at the lines it wants — and can introspect ANY prior emission of its own the same way. OPEN/FOLD/KILL-able like any log row — the model curates its own history, and log-KILL clears the `writableBy` gate for the model (the DB-storage curation lever plurnk.md teaches; Log's handler surface — kill only — keeps every other mutating op at 501). {§model-entry-log-curation} The engine writes one at the end of each turn that produced output; a struck/empty turn mirrors nothing.
|
|
549
551
|
|
|
550
552
|
The run's **first** model row is exceptional: a born-OPEN turn-0 **exemplar** — a minimal worked example (`PLAN` → environment `FIND`s → `SEND[102]`) the model always opens on, so the grammar can stay thin (the example teaches the syntax, not a heavy grammar). {§model-entry}
|
|
551
553
|
|
|
554
|
+
**OPEN and FOLD are meta-operations — render directives, not actions.** They change how the world *displays*, never what it *is* (scrolling, not editing) — so a **successful** OPEN/FOLD leaves **no log row**: the next packet's render IS the receipt (the target shows collapsed/expanded), and the emission itself survives verbatim in the `model` mirror for introspection. A curation receipt that itself rented log space made curation self-defeating in the small — fold a 200-token row, gain a coordinate line, *pay a permanent FOLD row* — and the grinder's mechanical folds were already rowless: one rule now covers both curators, and FOLD is genuinely free. A **failed** OPEN/FOLD (bad target, bad range) keeps its ordinary op row with its status — errors are signals. The idle-turn gate reads the *emitted statements*, so a pure-curation turn is work, never idleness. {§fold-open-meta-operations}
|
|
555
|
+
|
|
552
556
|
### §copy COPY (engine-orchestrated)
|
|
553
557
|
|
|
554
558
|
AST: `{ op: "COPY", target (source), body (destination), signal: tags | null, lineMarker? }`.
|
|
@@ -603,10 +607,8 @@ The engine's failure terminals — **500** (strike threshold) and **508** (cycle
|
|
|
603
607
|
**Three engine error states verify the claim.** None is a status code the model learns; all are engine machinery (§engine-rails), pushed to the model as a steering hint on the next packet and **never** as the strike itself (the model sees errors that happened, never the engine's accounting — the gamification policy, §engine-rails). Each strikes (`turnErrors`) and lets the loop continue so the model can correct; a model that ignores the hint and keeps offending spins out to the engine's 500, seeing only the repeated hint, never the count. (All live at `Engine.runLoop`'s turn close.)
|
|
604
608
|
|
|
605
609
|
- **Idle turn** {§send-idle-turn} — a continuing turn (102) whose ops are only PLAN/SEND — no work op. The model continued with nothing to do. The steer, verbatim: *"If the turn's work is complete, terminate with 200. If awaiting a stream or run trigger, terminate with 202 to hibernate."*
|
|
606
|
-
- **Premature terminate** {§send-premature-terminate} — a `SEND[200]` is refused **409** at dispatch
|
|
607
|
-
|
|
608
|
-
- **A READ submitted this turn** — the model emitted a `READ` and a terminal `SEND[200]` in the *same* turn, terminating on a result it cannot have seen: an op's result folds back on the NEXT turn (the correct shape is `READ` → `SEND[102]` → receive → `SEND[200]`). Without this gate the model ends mid-sentence on the value it expected to inline (the jsonpath/xpath extraction dead-end). Detected from the turn's own ops (the pre-dispatch snapshot), so it never depends on the READ's success. Steer: *"Attempted termination with submitted READ operation(s). SEND[102]; the results arrive next turn."* — prescriptive like the live-thing steer; the terse conflict alone left the verify-then-done model re-emitting to strike-out (the edit-todo digest).
|
|
609
|
-
- **Groundless hibernate** {§send-groundless-hibernate} — the [202] arm of submitted-READ: the model emitted a `READ` and a `SEND[202]` in the *same* turn, with **no wake edge** — no live thing held (the same snapshot the [200] gate reads) and nothing wake-capable opened this turn. The READ's result folds back on the NEXT turn, but a park with no wake edge never *has* a next turn — the model is sleeping on its own unanswered question, an eternal park only an operator inject could revive (the config-lookup dead-park: `PLAN → READ → SEND[202]`, the model's dodge once the [200] arm steers it off `READ → SEND[200]`). Refused **409** with the same record-faithful shape (the row keeps its `[202]` attempt, the loop stays a continue, the steer strikes). Wake-edge-capable ops ground the park from the emission itself, spawn-then-hibernate style: `EXEC` (stream conclusion / `<T,P>` poll, §exec-poll), `COPY(run://…)` (child-conclusion wake, §run-lifecycle-child-wake), directed `SEND(run://…)` (irc), an `http` `READ` (a web fetch streams). A **bare park holding nothing is legal** — the voice door: a sibling irc or operator inject wakes it (§actor-boundary-passive-wake), and the daemon surfaces the idle park as `loop/quiesced` (§run-lifecycle-quiesced) rather than the engine refusing it. Steer: *"Attempted [202] hibernation with submitted READ operation(s). SEND[102]; nothing here would wake you."*
|
|
610
|
+
- **Premature terminate** {§send-premature-terminate} — a `SEND[200]` while **the run still holds a live thing** is refused **409** at dispatch; the SEND row's `[200]` attempt and body are recorded **faithfully** (never rewritten, never erased), stamped `status_rx=409` (Conflict), the loop stays a continue (a `log_entries.status_rx` may be 409; a *loop* status may not, so the loop simply never goes terminal), and it **strikes** (couples to the grinder, §grinder-strike-coupling — a model that won't stop premature-200ing escalates out via the rails to 500/508, never a false 200). A live thing is an open stream/spawn (§subscriptions, §run-lifecycle-total-reap) **or a non-terminal child run** (children and streams are the same kind of live thing a run holds, §run-lifecycle). A child is "live" by its **latest loop** — the SAME definition the `## Plurnk Service Child Runs` orientation renders (§child-orientation), so the gate and the section the model reads NEVER disagree: a refusal is always backed by a child the model can SEE and `KILL` (an inherited/historical loop a fork carries is not the latest, so a concluded child never refuses against an empty orientation — the fanout dead-end). Steer: *"Attempted [200] termination despite active streams or worker runs. You may either hibernate [202] to wait or KILL them before terminating."* This gate is **runtime-only** — the live thing is a spawned child's status / an open stream the parser can never see. The grammar-shape terminals (a `SEND` after a same-turn `READ` — terminating on a result that folds back next turn) are rejected in the **parser**, not here (plurnk-grammar#51); the engine no longer re-derives them.
|
|
611
|
+
- **Groundless hibernate** {§send-groundless-hibernate} — the model emitted a `READ` and a `SEND[202]` in the *same* turn, with **no wake edge** — no live thing held (the pre-dispatch snapshot) and nothing wake-capable opened this turn. The READ's result folds back on the NEXT turn, but a park with no wake edge never *has* a next turn — the model is sleeping on its own unanswered question, an eternal park only an operator inject could revive (the config-lookup dead-park: `PLAN → READ → SEND[202]`). Refused **409**, record-faithful (the row keeps its `[202]` attempt, the loop stays a continue, the steer strikes). This stays engine-side because the wake-edge test is **runtime** state, not a shape the parser can judge: wake-edge-capable ops ground the park from the emission itself, spawn-then-hibernate style — `EXEC` (stream conclusion / `<T,P>` poll, §exec-poll), `COPY(run://…)` (child-conclusion wake, §run-lifecycle-child-wake), directed `SEND(run://…)` (irc), an `http` `READ` (a web fetch streams). A **bare park holding nothing is legal** — the voice door: a sibling irc or operator inject wakes it (§actor-boundary-passive-wake), and the daemon surfaces the idle park as `loop/quiesced` (§run-lifecycle-quiesced) rather than the engine refusing it. Steer: *"Attempted [202] hibernation with submitted READ operation(s). SEND[102]; nothing here would wake you."*
|
|
610
612
|
|
|
611
613
|
### §exec EXEC
|
|
612
614
|
|
|
@@ -614,7 +616,7 @@ AST: `{ op: "EXEC", target (cwd), body: string | null (command), signal: string
|
|
|
614
616
|
|
|
615
617
|
Engine routes unconditionally to `exec` scheme (path slot is `cwd`, not a URI). The runtime slot (`signal`) selects an executor, resolved against the boot-time `ExecutorRegistry` — siblings discovered and probed at startup, availability cached, default `sh`. Unknown or unavailable runtime → 501 carrying the probe `detail`. {§exec-registry-resolves}
|
|
616
618
|
|
|
617
|
-
**Timeout and poll — `<T,P>` on the `<L>` slot (grammar 0.74.20).** EXEC repurposes the line-marker slot as `<timeout, poll>` in **seconds** (consistent with the `seconds=` stream-age render). `T` (mark[0]) caps the spawn's lifetime: at `T>0` the service aborts it (a bounded reap — polite signal then SIGKILL after `
|
|
619
|
+
**Timeout and poll — `<T,P>` on the `<L>` slot (grammar 0.74.20).** EXEC repurposes the line-marker slot as `<timeout, poll>` in **seconds** (consistent with the `seconds=` stream-age render). `T` (mark[0]) caps the spawn's lifetime: at `T>0` the service aborts it (a bounded reap — polite signal then SIGKILL after `PLURNK_SERVICE_EXEC_KILL_GRACE_MS`) and stamps the stream **504**, distinct from a deliberate kill (499) or a clean exit (200). `-1` / absent → unbounded (loop-life bounded), the background-stream behavior. **`0` → turn-scoped**: the stream is reaped at the run's *next pre-turn* (via the registry abort, before the turn's own spawns), so it never survives into the subsequent turn; its terminal output surfaces born-OPEN like any close (§exec-stream). {§exec-timeout} `P` (mark[1]) is the hibernation **poll cadence**, stored on the subscription: while the loop is *parked* at `SEND[202]`, the daemon arms a per-run timer for the tightest open poll cadence and resumes the slept loop every P seconds (floored by `PLURNK_SERVICE_EXEC_WAIT_MS` so it can't tick faster than a turn settles) to inspect progress (the same 202→100 resume a stream conclusion uses, §run-lifecycle). It does **nothing while the loop is active** — an active loop already gets the ambient folded stream deltas (§exec-stream), so the poll-wake matters only across hibernation. A 202 with no polled stream gets no timer (it sleeps until a conclusion wakes it). {§exec-poll}
|
|
618
620
|
|
|
619
621
|
**Effect-gating.** Each executor declares an `effect` (`pure` | `read` | `host`); the service maps it to policy (`EffectPolicy`). A `host` runtime (subprocess; file-backed sqlite) mutates the host → **propose** (lifecycle §proposal): the run waits for a human gate, then spawns and writes stdout/stderr to channels of a `<runtime>:///<loop>/<turn>/<seq>` entry (the runtime tag is the URI scheme, §exec/#240; the coordinate matches the op's log-row coordinate, e.g. `sh:///1/1/2`), returning `102 Processing` immediately. Channel state transitions (`active` → `closed`/`errored`) drive what the model sees at subsequent turn boundaries (§channel-state). {§exec-host-proposes}
|
|
620
622
|
|
|
@@ -634,7 +636,7 @@ A side-effecting op does not execute on dispatch — it **proposes**. The scheme
|
|
|
634
636
|
- **`loop.resolve`** (§methods) — a client's accept / reject / cancel.
|
|
635
637
|
- **Server-YOLO** (§dual-yolo) — an in-tree listener resolves `accept` in-process, same tick, no wire roundtrip.
|
|
636
638
|
- **noProposals** — an in-tree listener resolves `reject` (outcome `no_review_channel`).
|
|
637
|
-
- **Timeout** — `
|
|
639
|
+
- **Timeout** — `PLURNK_SERVICE_PROPOSAL_TIMEOUT_MS` (§operator-config) elapses with no resolution → the engine synthesizes `cancel` (outcome `timeout`), server-side, needing no client. {§proposal-timeout-cancels}
|
|
638
640
|
|
|
639
641
|
**The decision drives a one-way state transition** on `log_entries.state` (resolution is idempotent — `WHERE state='proposed'`, so a second resolution 404s):
|
|
640
642
|
|
|
@@ -805,43 +807,43 @@ Model selection: separate alias cascade in `ProviderRegistry` (§provider-instan
|
|
|
805
807
|
|
|
806
808
|
| Var | Default | Status | Purpose |
|
|
807
809
|
|--------------------------------------|--------------------|------------|---------------------------------------------------------------|
|
|
808
|
-
| `
|
|
810
|
+
| `PLURNK_SERVICE_DB_PATH` | `./plurnk.db` | enforced | SQLite file path. |
|
|
809
811
|
| `PLURNK_HOST` | `127.0.0.1` | enforced | Bind address for the daemon WebSocket. Local-only by default. |
|
|
810
812
|
| `PLURNK_PORT` | `3044` | enforced | TCP port for the daemon WebSocket. |
|
|
811
|
-
| `
|
|
812
|
-
| `
|
|
813
|
-
| `
|
|
814
|
-
| `
|
|
815
|
-
| `
|
|
816
|
-
| `
|
|
817
|
-
| `
|
|
818
|
-
| `
|
|
819
|
-
| `
|
|
820
|
-
| `
|
|
813
|
+
| `PLURNK_SERVICE_MAX_TURNS` | `-1` | enforced | Operator turn **ceiling** — `-1` = no cap; a positive value caps a per-call `loop.run({maxTurns})`. |
|
|
814
|
+
| `PLURNK_SERVICE_MAX_COMMANDS` | `99` | enforced | Per-emission op cap. Overflow ops drop silently; one `max_commands_exceeded` telemetry entry surfaces on the next packet. |
|
|
815
|
+
| `PLURNK_SERVICE_RPC_TIMEOUT` | `30000` | enforced | ms deadline for non-`longRunning` RPC handlers; expiry answers `-32007 Timeout` (§errors) and the abandoned handler's late outcome is logged, never re-answered. `longRunning` registrations (proposal-pausing ops, external installs) are exempt. {§operator-config-rpc-timeout} |
|
|
816
|
+
| `PLURNK_SERVICE_LOOP_TIMEOUT` | `86400000` | enforced | ms wall-clock budget for a single `loop.run`: expiry aborts the loop signal mid-flight (a stuck `generate` included) and the loop terminates `504 loop_timeout` — a legible engine terminal, kin to the exec `<T>` reap's 504 (§exec-timeout). {§operator-config-loop-timeout} |
|
|
817
|
+
| `PLURNK_SERVICE_MAX_STRIKES` | `3` | enforced | Strike threshold + sudden-death lead time (§engine-rails). |
|
|
818
|
+
| `PLURNK_SERVICE_MIN_CYCLES` | `3` | enforced | Min repetitions before cycle detection fires (§engine-rails). |
|
|
819
|
+
| `PLURNK_SERVICE_MAX_CYCLE_PERIOD` | `4` | enforced | Max period length cycle detection examines (§engine-rails). |
|
|
820
|
+
| `PLURNK_SERVICE_MD_<ALIAS>` | (unset) | enforced | Operator reference doc: materializes `<path>` as `plurnk:///<ALIAS>.md`, auto-READ into every model run's turn 0 (§actor-boundary). `~` expands to home. |
|
|
821
|
+
| `PLURNK_SERVICE_FILES_ITEMS` | `-1` | enforced | Turn-0 catalog preview, one `FIND(scheme:///**)` per scheme. Memory/scratch/docs always full; the first-`N` cap applies **only** to the `file` list. `-1` = all full; positive `N` = file list first-N (memory still full); `0` / unset = off (§actor-boundary-catalog-preview). |
|
|
822
|
+
| `PLURNK_SERVICE_PROPOSAL_TIMEOUT_MS` | `300000` | enforced | ms wait for a proposed entry (status=202) to be resolved before timing out. |
|
|
821
823
|
| `PLURNK_PROVIDERS_THINKING` + `_CAPACITY` | `on` / `4096` | enforced | The activation/capacity split (providers 0.31; a numeric budget silently flipping template flags was secret flag-setting). `off | adaptive | on`; capacity (tokens) REQUIRED when on, shipped equal to the partition's REASONING reserve (template-pinned). A think-trained model MUST think — off reroutes its thought into the grammar's legal free zone as prose. One setting is right everywhere: providers clamp thinking to none on in-band (response_format) grammar calls themselves, so the channel-topology parallax needs no per-backend override. F7 coupling: llama-server honors only the box's `--reasoning-budget` launch flag (must equal capacity; boot-warned). |
|
|
822
824
|
| `PLURNK_PROVIDERS_FETCH_TIMEOUT` | `600000` | enforced | Service-wide ms ceiling on any outbound request (providers, future http schemes). Module-specific overrides are allowed below the ceiling. |
|
|
823
|
-
| `
|
|
824
|
-
| `
|
|
825
|
+
| `PLURNK_SERVICE_DEBUG` | `0` | reserved | Schema-validation toggle. Not yet enforced. |
|
|
826
|
+
| `PLURNK_SERVICE_LOG_LEVEL` | `info` | reserved | Stdout banner verbosity. Not yet enforced. |
|
|
825
827
|
|
|
826
828
|
**enforced** = engine reads and acts on the value. **reserved** = shipped in `.env.example` (forward-spec) but no-op until wired.
|
|
827
829
|
|
|
828
830
|
**Two override semantics — ceiling vs default.** Which kind a var is determines what "override" means across the cascade:
|
|
829
|
-
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-session constraint, not a per-call RPC arg. `
|
|
830
|
-
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `loop.run({alias})` overrides it), `
|
|
831
|
+
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-session constraint, not a per-call RPC arg. `PLURNK_SERVICE_GIT_ALLOWED` (`=0` flatly denies git service-wide, §membership), `PLURNK_SERVICE_MAX_COMMANDS`, `PLURNK_SERVICE_MAX_STRIKES`, `PLURNK_PROVIDERS_FETCH_TIMEOUT` (module overrides allowed only *below* it), and `PLURNK_SERVICE_MAX_TURNS` (`-1` ships it off; a positive value caps the per-call request). The sandbox/cost guarantee: the operator caps it; no client widens it.
|
|
832
|
+
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `loop.run({alias})` overrides it), `PLURNK_SERVICE_REQUIREMENTS` (the per-call requirements default), and the config-time vars (`HOST` / `PORT` / `DB_PATH`).
|
|
831
833
|
|
|
832
|
-
**The shipped `.env.example` is itself under test** (no active `
|
|
834
|
+
**The shipped `.env.example` is itself under test** (no active `PLURNK_SERVICE_MD_*` doc alias — the policy is a SECTION, a doc default double-injects it; no active `PLURNK_MODEL`; an actively-resolving `PLURNK_PROVIDERS_GBNF`; the policy renders in exactly one packet section): every other tier runs the test cascade, so shipped-default regressions are invisible to it by construction. {§operator-config-shipped-defaults} Its companion **flag-parity** check binds code and template both ways: every `PLURNK_SERVICE_*` the service reads has a `.env.example` line (a floor, a `--flag`, a legend entry) and every declared `PLURNK_SERVICE_*` is read — so a half-landed rename (a missed file, a script-glob gap) fails a test instead of a user's boot, and a dead knob can't ship. {§operator-config-flag-parity}
|
|
833
835
|
|
|
834
|
-
Enforcement is per-use-site — no central most-restrictive pass; each ceiling is checked where it bites. `
|
|
836
|
+
Enforcement is per-use-site — no central most-restrictive pass; each ceiling is checked where it bites. `PLURNK_SERVICE_MAX_TURNS` ships **off** (`-1` = no cap; the loop ends via SEND, budget, strikes, or cycle detection) and, when an operator sets a positive value, the per-call request is `min()`-capped against it. {§operator-config-max-turns-ceiling}
|
|
835
837
|
|
|
836
838
|
**Client open-context (per session).** `session.create({settings})` carries per-session overrides, persisted on `sessions.settings` and composed against env at each knob's read-site. Two families, kept distinct so neither semantic leaks into the other; operator-arcane knobs stay env-only — this is the narrow client surface.
|
|
837
839
|
|
|
838
840
|
*Defaults — explicit-wins (the client replaces/merges freely):*
|
|
839
|
-
- `settings.filesItems` (number) **replaces** `
|
|
840
|
-
- `settings.mdDocs` (`[{alias, content}]`) **unions** with the server's `
|
|
841
|
+
- `settings.filesItems` (number) **replaces** `PLURNK_SERVICE_FILES_ITEMS` for the session: a one-shot opens clean (`0`, no preview), a workspace full (`-1`), or with the file list capped (`N`, memory still full). A single scalar — the client value wins outright. {§operator-config-session-files-items}
|
|
842
|
+
- `settings.mdDocs` (`[{alias, content}]`) **unions** with the server's `PLURNK_SERVICE_MD_*` docs, keyed by alias — a client adds its own repo docs atop the operator's systemwide policy doc. On alias collision the client wins (a deliberate shadow), but by default the policy doc rides into every session. The client sends content (it owns the file), not a path. {§operator-config-session-md-docs}
|
|
841
843
|
|
|
842
844
|
*Ceilings — most-restrictive-wins (the client may only narrow, never widen):*
|
|
843
|
-
- `settings.maxCommands` (number) **min()s** the `
|
|
844
|
-
- `settings.git` (`false`) **denies** git for the session (`
|
|
845
|
+
- `settings.maxCommands` (number) **min()s** the `PLURNK_SERVICE_MAX_COMMANDS` per-emission cap for the session — a client tightens the runaway-op guard, never raises it past the operator's. {§operator-config-session-max-commands} The cap bounds *actions* only: PLAN (reasoning) and a terminal `SEND` (signal ≥ 200, the conclusion) are never counted and always dispatch — so `0` is a valid floor (the tightest), admitting a plan and a conclusion with zero actions. {§operator-config-session-max-commands-floor}
|
|
846
|
+
- `settings.git` (`false`) **denies** git for the session (`PLURNK_SERVICE_GIT_ALLOWED` AND session) — the client opts its session out of git membership + telemetry; it can never re-enable git past the operator's service-wide lockout. {§operator-config-session-git}
|
|
845
847
|
|
|
846
848
|
Feature-flag bools use `process.env.X === "1"` exactly — never `=== "true"`.
|
|
847
849
|
|
|
@@ -878,7 +880,7 @@ registry.registerMethod("loop.run", {
|
|
|
878
880
|
description: "Run a model-driven loop with a prompt.",
|
|
879
881
|
params: {
|
|
880
882
|
prompt: "string — the user prompt for the loop",
|
|
881
|
-
maxTurns: "number? — defaults to
|
|
883
|
+
maxTurns: "number? — defaults to PLURNK_SERVICE_MAX_TURNS",
|
|
882
884
|
alias: "string? — overrides the boot-time PLURNK_MODEL",
|
|
883
885
|
},
|
|
884
886
|
requiresInit: true,
|
|
@@ -889,7 +891,7 @@ registry.registerMethod("loop.run", {
|
|
|
889
891
|
- `description`: one-liner surfaced by `discover`.
|
|
890
892
|
- `params`: `"type — meaning"` per param; `?` suffix = optional. Self-documenting, not enforced.
|
|
891
893
|
- `requiresInit`: rejects until a session is attached.
|
|
892
|
-
- `longRunning`: exempt from `
|
|
894
|
+
- `longRunning`: exempt from `PLURNK_SERVICE_RPC_TIMEOUT`. {§method-registration-register}
|
|
893
895
|
|
|
894
896
|
### §discovery Discovery
|
|
895
897
|
|
|
@@ -921,7 +923,7 @@ registry.registerMethod("loop.run", {
|
|
|
921
923
|
|
|
922
924
|
`capabilities` lists registered plug-ins by `(kind, name)`. Cold clients call `discover` first. No hardcoded method names or capability lists in any client. {§discovery-discover}
|
|
923
925
|
|
|
924
|
-
`versions` rides the same round-trip so a client shows update status without per-invocation registry IO: `{ service: { installed, latest? }, client: { latest? } }`. `service.installed` is the daemon's own `package.json` version (honest self-report); `latest` is a **cached, best-effort** npm-registry poll (TTL `
|
|
926
|
+
`versions` rides the same round-trip so a client shows update status without per-invocation registry IO: `{ service: { installed, latest? }, client: { latest? } }`. `service.installed` is the daemon's own `package.json` version (honest self-report); `latest` is a **cached, best-effort** npm-registry poll (TTL `PLURNK_SERVICE_VERSION_POLL_TTL`) for the service (`@plurnk/plurnk-service`) and client (`@plurnk/plurnk`) packages — offline or registry-down omits `latest`, and the poll never blocks or fails `discover`. The client owns reading its own installed version and the semver compare. {§discovery-versions}
|
|
925
927
|
|
|
926
928
|
### §methods Core method set
|
|
927
929
|
|
|
@@ -1010,7 +1012,7 @@ Server-initiated events on the same WebSocket.
|
|
|
1010
1012
|
| `log/entry` | `{ entry: LogEntry }` | Every `log_entries` write. {§notifications-log-entry-notify} |
|
|
1011
1013
|
| `loop/terminated` | `{ loopId, finalStatus, hitMaxTurns }` | Loop reaches terminal status. |
|
|
1012
1014
|
| `loop/quiesced` | `{ loopId, runId, status: 202 }` | A loop parked at `SEND[202]` reached subtree-quiescence (no open stream, no non-terminal child) — idle/complete-for-now but **reawakable**, distinct from `loop/terminated` (§run-lifecycle-quiesced). |
|
|
1013
|
-
| `loop/proposal` | `{ logEntryId, sessionId, runId, loopId, turnId, op, target, body, attrs, flags }` | Dispatch pauses on status=202. Carries `flags` so server-YOLO clients can suppress review UI. Client responds with `loop.resolve` (or `
|
|
1015
|
+
| `loop/proposal` | `{ logEntryId, sessionId, runId, loopId, turnId, op, target, body, attrs, flags }` | Dispatch pauses on status=202. Carries `flags` so server-YOLO clients can suppress review UI. Client responds with `loop.resolve` (or `PLURNK_SERVICE_PROPOSAL_TIMEOUT_MS` fires). |
|
|
1014
1016
|
| `session/created` | `{ id, name, projectRoot }` | Any client creates a session. |
|
|
1015
1017
|
| `stream/event` | `{ entryId, channel, state, contentLength }` | Channel content grows or state transitions. {§notifications-stream-event-on-channel-change} |
|
|
1016
1018
|
| `stream/concluded` | `{ entryId, target, subscriptionId, scheme, closeStatus, summary, wakeAction, wakeLoopId? }` | A streaming subscription closed (subprocess finished / errored / cancelled). `wakeAction` says how the conclusion reached the run: `resumed-loop` (a slept `202` loop resumed in place, §run-lifecycle-wake-liveness), `no-op-active-loop` (folded into a live loop's next turn), `skipped-aborted`/`skipped-cancelled`/`skipped-no-provider`, or `no-loop` (nothing to resume). `summary` rides the notification for client display; it is no longer fed to the model as a prompt. {§notifications-stream-concluded} |
|
|
@@ -1114,7 +1116,10 @@ Each entry: question, answer, rationale, migration path.
|
|
|
1114
1116
|
- **Per-turn weight.** A markdown table groups render-weight by turn — the `loop/turn` coordinate prefix — listed chronologically (oldest first). The turn is the grinder's rollback unit, and the rail folds the **newest** turn first (§grinder); the model sees which turns are fat and can FOLD ahead of the rail. {§tokenomics-turn-totals}
|
|
1115
1117
|
- **Heaviest entries.** A second table lists the five heaviest log entries by render-weight, each by its `log:///<coord>/<op>` handle — the FOLD targets behind the turn weight. The handle carries the turn, so the two tables interlock. {§tokenomics-largest-entries}
|
|
1116
1118
|
- **Context-window percent.** The headline carries usage as a percent of the ceiling — `usage Y (P%)` — a fullness gauge beside the absolutes. Reads the ceiling already in hand; no extra provider call. {§tokenomics-context-percent}
|
|
1117
|
-
- **The window is a partition, never a fraction.** `effectiveWindow = min(
|
|
1119
|
+
- **The window is a partition, never a fraction.** `effectiveWindow = min(PLURNK_SERVICE_CTX, provider.contextSize)` (CTX alone when the provider reports no window — the operator's policy stands in for unknown physics) splits **without remainder**: `promptBudget = effectiveWindow − REASONING − ASSISTANT − SAFETY` is what the service may send, `max_tokens = REASONING + ASSISTANT` is the generation envelope passed on **every** `generate({maxTokens})` — no decode is unbounded — and `SAFETY` covers chat-template overhead no content counter sees. The prompt ceiling is **derived, never set**: a settable ceiling lets policy contradict physics, and a fractional one budgets the prompt against the window while *forgetting the response lives there too* (at 0.9 × 49152, any emission past ~4.9k overflowed gemma with a perfectly honest ruler). Reserves exceeding the window (`promptBudget ≤ 0`) are a configuration contradiction and fail hard. When native thinking is on, llama-server ignores per-request numeric budgets — the serving box's `--reasoning-budget` launch flag must equal `PLURNK_SERVICE_REASONING`, and the service warns at boot when it cannot verify that coupling. **Shipped-defaults invariant: any ≥77Ki window partitions to exactly 65536 prompt tokens** (78848 − 4096 − 8192 − 1024). {§tokenomics-window-partition}
|
|
1120
|
+
- **Derivation is off the hot path; search is never degraded by it.** The per-turn derivation pump and the session warm run on a background chain (serialized, drained at daemon stop, failures logged) — **a turn never waits on an embedding** (a 2-CPU container CPU-embedding a 335-entry ingest starved every loop ~28min). Full fidelity survives the move on both fusion halves: the **keyword half indexes at the write** (plain string→FTS, no handler invoked — a cold session's first query narrows over everything ever written) {§semantic-fts-at-write}, and a `~` query **derives its own FTS-narrowed candidate slice inline** at dispatch (bounded, cap-telemetered) — ranking only ever scores the narrowed set, so warming exactly that slice is bit-identical to a fully-warm corpus, from turn 1, on any hardware {§semantic-cold-query-full-fidelity}. {§derivation-off-hot-path}
|
|
1121
|
+
- **Binary truth beats the label; no entry dominates the corpus.** A tracked member whose HEAD bytes contain NUL is materialized as a binary marker (empty body, `application/octet-stream`, READ-415) **regardless of what extension-based detection claims** — the markdown default for unmapped extensions once shipped a 3.3MB `.wasm` blob into the semantic corpus as prose, three copies, ~10M tokens (#320) {§membership-binary-sniff}. And the per-entry chunk cap is a **latency stage, never a coverage bound**: the inline (dispatch-time) slice embeds head-first up to its budget so a cold `~` answers in bounded seconds — and a capped pass does NOT stamp the deep hash, so the background pump completes the entry to full depth (a 300-page book is entirely searchable at steady state; rank cannot be dominated regardless — `semantic_rank` takes one best chunk per entry). A flat cap would silently foreclose legitimate large texts: head-only vectors under a whole-file keyword narrow return head-biased spans, permanently. {§semantic-entry-chunk-cap}
|
|
1122
|
+
- **A turn is never blank; the provider never adjudicates.** A completed exchange ALWAYS returns from the provider — the model's bytes flow in `assistant` no matter what, with any grammar-conformance verdict riding `response.telemetry` as an **observation** (providers 0.32; the provider transports and observes — the engine's own parse is the judge). Every emission takes the one pipeline: complete statements dispatch, malformed text mints position-carrying parse-error rows the model reads next turn, nothing-parsed is the ordinary no-ops 422, and the record keeps the emission verbatim with its real usage billed. A `ProviderError` reaching the engine means NO completed exchange exists (auth, network beyond retries, rate limit) — an infrastructure failure, and the loop dies 500 carrying the cause, first occurrence: tolerating an infrastructure violation is how a bug accretes concentric layers of policy instead of a fix (the retired empty-turn fallback laundered provider adjudications into model-behavior 422s, and forensics chased the wrong suspect for days). {§turn-never-blank}
|
|
1118
1123
|
- **The ceiling calibrates to usage.** `countTokens` may be a heuristic ruler (the openai family defaults to chars/4; escaped-JSON log rows run ~2.7 real chars/token — honest arithmetic on that ruler shipped a 65k-real packet into a 49k window, #311). Every response carries ground truth: `usage.prompt` counts the whole wire request. The engine keeps each loop's observed real/measured ratio (monotone max — the ceiling only tightens) and the effective ceiling is `ceiling / ratio`, so past a loop's first response a real context overflow is unreachable; turn 1 rides the floor-sized packet's natural headroom. A provider failure that still escapes lands as a loop-terminal **500 carrying the cause** on both the row and the `loop/terminated` broadcast — never a contentless 500 over a still-102 row. {§tokenomics-ceiling-calibrates-to-usage}
|
|
1119
1124
|
- **Curation pressure gates on occupancy.** The budget section's Turns/Heaviest tables — a standing FOLD-target list — render only at **50%+ occupancy** (assembled total / ceiling); below that the headline's numbers stand alone. A high-headroom model reads the tables as a todo and burns turns on token hygiene at 3–25% occupancy (#308, the bench grok run); a null ceiling can't calibrate, so the full readout stays. The requirements footer carries only the overflow-RECOVERY must, never ambient optimize-your-context pressure. {§tokenomics-pressure-gates-on-occupancy}
|
|
1120
1125
|
- **Depth re-counted at render.** The manifest re-tokenizes each entry's `tokens` through the live provider at build — never the write-time snapshot — so a model change between loops can't stale the catalog. Every token figure in the packet is render-fresh, manifest and budget alike; nothing trusts a cross-loop cached total.
|
|
@@ -1164,11 +1169,11 @@ The version travels *with the proposal*, never re-read from the entry at accept:
|
|
|
1164
1169
|
|
|
1165
1170
|
The CAS is the **hard backstop**, at the moment of writing, on every accept path. It composes with — and is distinct from — the YOLO-only `staleClobberRisk` guard (§dual-yolo-stale-clobber-reject): that guard refuses to *auto-accept* an edit whose target already diverged earlier this turn (the read→propose window, server-YOLO path only); the CAS refuses to *write* against a snapshot disk has left (the propose→write window, every path). Together they bracket the full read→write span.
|
|
1166
1171
|
|
|
1167
|
-
**Permission flags.** {§membership-git-flags} `
|
|
1172
|
+
**Permission flags.** {§membership-git-flags} `PLURNK_SERVICE_GIT_ALLOWED` is the hard ceiling: `=0` denies all git membership service-wide, un-re-enableable — the sandbox/benchmark lockout. `PLURNK_SERVICE_GIT_AUTO` is the default declaration: `=1` (default) declares an implicit `repo` at `project_root` (no-op if it isn't a git tree); `=0` declares nothing — service/clients `repo`-declare explicitly. `ALLOWED` gates `AUTO`.
|
|
1168
1173
|
|
|
1169
1174
|
**Rationale.** Session is the right scope unit; membership *is* the curation, outsourced and tiered: git bounds it by tracking, the client supersedes by overlay, the model curates its own render by READ/FOLD — the engine curates nothing. The forest falls out of "session = world": one workspace can be many repos, so membership is their union, declared not guessed (the scan and its security are the client's). Exhaustiveness is a property of *coverage*, not *work*: every member is checked every turn so no drift hides, but unchanged members cost only a detect — the full-repo cost is git's to bound (what it tracks) and the client's to bound (`hide`), never the engine's to pay re-reading what hasn't moved.
|
|
1170
1175
|
|
|
1171
|
-
**Migration path.** `session_constraints.effect` gains `repo`; the three renames (`add`→`pick`, `ignore`→`hide`, `read-only`→`view`) are wire-surface changes on `session.constrain`. Forest resolution iterates declared repos (was one `ls-files` at root). The change-detect adds a per-member stored signal — mtime+size or content hash, and *that choice is the EMI reliability bound* — gating the existing materialize. `
|
|
1176
|
+
**Migration path.** `session_constraints.effect` gains `repo`; the three renames (`add`→`pick`, `ignore`→`hide`, `read-only`→`view`) are wire-surface changes on `session.constrain`. Forest resolution iterates declared repos (was one `ls-files` at root). The change-detect adds a per-member stored signal — mtime+size or content hash, and *that choice is the EMI reliability bound* — gating the existing materialize. `PLURNK_SERVICE_GIT_ALLOWED` (the hard ceiling) and `PLURNK_SERVICE_GIT_AUTO` (the default declaration) are the git flags (§membership-git-flags). Tenancy / cross-session shared workspaces still require a `workspaces` table lifting constraints off `session_constraints`.
|
|
1172
1177
|
|
|
1173
1178
|
### §grinder Budget enforcement: the grinder
|
|
1174
1179
|
|
|
@@ -1258,7 +1263,7 @@ type Packet = {
|
|
|
1258
1263
|
|
|
1259
1264
|
The wire projection (`PacketWire.renderSlot`) groups sections by slot into the system + user ChatMessages; the digest re-renders the same stored sections byte-for-byte.
|
|
1260
1265
|
|
|
1261
|
-
**Prompt as a first-class entry.** Each loop's prompt is written on loop start as a plurnk-origin `EDIT` against `plurnk:///prompt/<loop_id>/<N>` (indexable, body channel, text/markdown). At render time the **Active User Prompts** section materializes **every** prompt the current loop holds, oldest first — typically one, but an active loop admits injected prompts, all shown in order. The section is the OPPOSITE of the errors section: bare HEREDOC bodies, no meta/link line — the heredoc fence (each prompt's own `plurnk://prompt/<loop>/<N>` address) IS the link. A prompt over `
|
|
1266
|
+
**Prompt as a first-class entry.** Each loop's prompt is written on loop start as a plurnk-origin `EDIT` against `plurnk:///prompt/<loop_id>/<N>` (indexable, body channel, text/markdown). At render time the **Active User Prompts** section materializes **every** prompt the current loop holds, oldest first — typically one, but an active loop admits injected prompts, all shown in order. The section is the OPPOSITE of the errors section: bare HEREDOC bodies, no meta/link line — the heredoc fence (each prompt's own `plurnk://prompt/<loop>/<N>` address) IS the link. A prompt over `PLURNK_SERVICE_PROMPT_PREVIEW_CHARS` renders a `[ Prompt exceeds preview limit. Full content: <addr> ]` pointer instead of its body (the model OPENs/READs the entry to see it whole — never lost). The entry itself stays READ/FOLD-able like any other. The foisted `EDIT`'s **log row is folded by default** (`expanded=0`): the prompt body already lives in the Active User Prompts section, so the log keeps the action for forensics while collapsing the duplicate body, re-OPENable like any fold (§open-fold). {§prompt-fold}
|
|
1262
1267
|
|
|
1263
1268
|
**The entry catalog.** The catalog is the **complete, unranked directory** of what a session holds, served by `FIND(scheme:///**)` — one per-scheme array, queried on demand, not a single materialized entry (there is no `plurnk:///manifest.json`; the per-scheme arrays replaced it). Built in the schemes layer (`_entry-manifest.catalogRowsFor`); a per-turn derivation pump (`maintainDerivations`) refreshes the deep channels the rows report. A scheme's array is **every entry it holds, in no relevance order**, each `{ path, seconds?, tags?, channels: { <uri>: { mimetype, tokens, lines } } }` — every channel keyed by the URI the model READs (the default channel by the bare path, a non-default by `path#channel`), so it reaches a channel without guessing. `tags` is present only when the entry carries `entry_tags` — its own categorization, surfaced so the model can `FIND` by tag. The model ranks and filters the catalog itself by querying it (task-aware); the catalog never ranks for it — the instant it did, it would be an index again. `tokens` is the provider's live count recounted at render, `lines` the content extent from `Mimetypes.process().totalLines`. The catalog never lists itself. {§packet-catalog}
|
|
1264
1269
|
|
|
@@ -1307,25 +1312,25 @@ Strike accounting, cycle detection, sudden-death thresholds, and no-ops bookkeep
|
|
|
1307
1312
|
|
|
1308
1313
|
**Turn-lifecycle liveness.** The provider `generate()` call is the one long, opaque window in a turn — submit → first committed op is provider latency plus a full first-turn generation (tens of seconds on a local model); a static client screen there is indistinguishable from a hang. The engine brackets `generate()` with two `telemetry/event` NOTICES (`source: "engine:turn"`, `level: "info"`): `turn_awaiting_model` the instant it calls the provider, `turn_generated` when the call resolves and op-parsing begins — a legible "thinking… → working…" heartbeat, NOT model token-content (that stays out of the Log, a paradigm break). Both are suppressed on an aborted loop and broadcast to the session like any notice (§telemetry-telemetry-event-notify). Optional intra-generation ticks (a moving counter during the long wait) are a later provider-contract enhancement (an `onProgress` on `generate()`, the `embedBatch` shape); the two-beat bracket needs no provider change. {§turn-lifecycle}
|
|
1309
1314
|
|
|
1310
|
-
**Content-offset position.** An emission-level error carries a `position: { type: "content-offset", line, column }` into the model's own emission — a parse-error LOG ROW (op='error', §model-entry) and a content-offset NOTICE (e.g. a provider's `grammar_unenforced`) both report the line, not the bytes. The model resolves it against its own emission: the
|
|
1315
|
+
**Content-offset position.** An emission-level error carries a `position: { type: "content-offset", line, column }` into the model's own emission — a parse-error LOG ROW (op='error', §model-entry) and a content-offset NOTICE (e.g. a provider's `grammar_unenforced`) both report the line, not the bytes. The model resolves it against its own emission: the `model` mirror row (§model-entry, always folded) holds the line-numbered emission, and the model `READ`s the folded row at the cited lines — surgical, budget-bounded, no auto-opening. No snippet is embedded — that would duplicate an emission the model can already introspect. {§telemetry-content-offset-pointer}
|
|
1311
1316
|
|
|
1312
1317
|
### §tools user.tools — the capability sheet
|
|
1313
1318
|
|
|
1314
1319
|
The tools capability lines render **titleless**, directly under the `definition` (plurnk.md) section — the examples flow on from plurnk.md with no separate header — and **above** `## Plurnk Service Requirements`, so the model sees what it can *do* before the rules it must follow. Each enabled capability contributes one line via `PacketBuilder.#collectTools`; the section is omitted when nothing is enabled. {§tools-capability-sheet}
|
|
1315
1320
|
|
|
1316
|
-
**Contributors: the wired executor tags.** Each available executor tag *with an example* contributes ONE line — its canonical usage — via the shared `teachingLine` (identical shape to the scheme directory, §schemes); its doc is materialized at `plurnk://docs/<tag>.md` and discovered via the turn-1 `FIND(plurnk://docs/**)` foist, not linked inline (#270). A tag with no example contributes nothing; `
|
|
1321
|
+
**Contributors: the wired executor tags.** Each available executor tag *with an example* contributes ONE line — its canonical usage — via the shared `teachingLine` (identical shape to the scheme directory, §schemes); its doc is materialized at `plurnk://docs/<tag>.md` and discovered via the turn-1 `FIND(plurnk://docs/**)` foist, not linked inline (#270). A tag with no example contributes nothing; `PLURNK_SERVICE_DOCS_EXCLUDE` drops a named tag's line + doc. The boot `ExecutorRegistry` probes availability per tag, retiring the model's blind `<<EXEC[sh]…`.
|
|
1317
1322
|
|
|
1318
1323
|
### §schemes user.schemes — the scheme directory
|
|
1319
1324
|
|
|
1320
|
-
A `## Plurnk Service Schemes` section renders in the system slot **after the definition (plurnk.md — grammar + imperatives) and the tools sheet** — a terse directory of the scheme families available this session, so the model knows what URI schemes exist before it acts. Each scheme that ships a `manifest.example` contributes ONE line — its canonical usage (no scheme prefix; the example self-documents). The doc is NOT linked inline (#270) — it is materialized at `plurnk://docs/<scheme>.md` and discovered via the turn-1 `FIND(plurnk://docs/**)` foist, keeping the raw packet free of doc links. The in-tree core schemes author their depth in `docs/<name>.md` (loaded at boot, shipped with the package); daughter schemes ship `manifest.documentation`. The verbose semantics live in that pull doc (materialized like any entry, READ on demand), not the hot path — terse pushes, depth pulls, via the same `teachingLine` as the tools sheet (§tools). A scheme with no example (provisional) is omitted; `
|
|
1325
|
+
A `## Plurnk Service Schemes` section renders in the system slot **after the definition (plurnk.md — grammar + imperatives) and the tools sheet** — a terse directory of the scheme families available this session, so the model knows what URI schemes exist before it acts. Each scheme that ships a `manifest.example` contributes ONE line — its canonical usage (no scheme prefix; the example self-documents). The doc is NOT linked inline (#270) — it is materialized at `plurnk://docs/<scheme>.md` and discovered via the turn-1 `FIND(plurnk://docs/**)` foist, keeping the raw packet free of doc links. The in-tree core schemes author their depth in `docs/<name>.md` (loaded at boot, shipped with the package); daughter schemes ship `manifest.documentation`. The verbose semantics live in that pull doc (materialized like any entry, READ on demand), not the hot path — terse pushes, depth pulls, via the same `teachingLine` as the tools sheet (§tools). A scheme with no example (provisional) is omitted; `PLURNK_SERVICE_DOCS_EXCLUDE` drops a named scheme's line + doc. {§schemes-directory}
|
|
1321
1326
|
|
|
1322
1327
|
### §inject system.inject — the operator injection
|
|
1323
1328
|
|
|
1324
|
-
When `
|
|
1329
|
+
When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as a `## Plurnk Operator Notes` section in the system slot **right after the teaching** (definition → tools → schemes → inject), ahead of the policy sections and budget — part of the cached prefix. Read per-turn so the operator's edits take effect live; a set-but-unreadable path fails the turn hard (a deliberate setting with a broken path is a misconfig, surfaced not hidden). `~/` expands to home. It's the operator-side complement to the plugin section hook — a pressure valve so reshaping the packet edits operator content, never the core. Unset → no section. {§packet-inject}
|
|
1325
1330
|
|
|
1326
1331
|
### §policy system.policy — the client's policy injection
|
|
1327
1332
|
|
|
1328
|
-
Two sections ride the system slot **below the operator notes, above budget**: `## Plurnk Service Policy` from `
|
|
1333
|
+
Two sections ride the system slot **below the operator notes, above budget**: `## Plurnk Service Policy` from `PLURNK_SERVICE_POLICY` (default `~/.plurnk/AGENTS.md`) and `## Project Policy` from `PLURNK_SERVICE_PROJECT` (default `<projectRoot>/AGENTS.md`, resolved relative to the session root). AGENTS.md is **policy** — the client's authoritative rules promoted into the privileged zone — NOT a curatable, foldable, READ-able entry; the model cannot FOLD it away. A default-absent path is silent (the section is omitted); an explicit override (env set) that fails to read fails the turn hard — a deliberate setting with a broken path is a misconfig, surfaced not hidden. Read per-turn so edits take effect live. Reference/scratch docs are NOT policy — they ride `PLURNK_SERVICE_MD_*` (materialized as READ-able entries, §operator-config), which is where the dev-notes AGENTS.md used to hold belong. {§policy-sections}
|
|
1329
1334
|
|
|
1330
1335
|
**The scheme self-doc contract.** `example` is the hot-path one-liner; `documentation` is the deep doc — the exact shape execs already use (`example` + `documentation`). `SchemeRegistry.teach()` renders the directory; `docEntries()` materializes the docs (per loop.run, alongside the operator docs). `documentation` rides a service-side `SchemeManifest` extension until plurnk-schemes#25 lands it in the contract.
|
|
1331
1336
|
|
|
@@ -1333,7 +1338,7 @@ Two sections ride the system slot **below the operator notes, above budget**: `#
|
|
|
1333
1338
|
|
|
1334
1339
|
Rendered at the END of the user packet under `## Plurnk Service Requirements` {§requirements-requirements-render-last} — closest to the assistant turn so the contract the model has to honor is the most recent text it sees. The header is omitted entirely when the requirements string is empty. {§requirements-requirements-omitted-when-empty} Contains rules the grammar block doesn't cover (canonical example: "Conclude the loop with `<<SEND[200]:answer:SEND`"). The op syntax leads the section. PLAN is mandated unconditionally by plurnk.md §Imperatives (grammar 0.70 requires every turn to lead with `<<PLAN`), so the service injects no separate plan directive here.
|
|
1335
1340
|
|
|
1336
|
-
**Sourcing:** caller supplies the string via `runLoop({ requirements })` / `runTurn({ requirements })`. Plurnk-service exposes `PATHS.defaultRequirements` (resolves `
|
|
1341
|
+
**Sourcing:** caller supplies the string via `runLoop({ requirements })` / `runTurn({ requirements })`. Plurnk-service exposes `PATHS.defaultRequirements` (resolves `PLURNK_SERVICE_REQUIREMENTS` env → in-package `requirements.md`). No DB cascade — same string every turn.
|
|
1337
1342
|
|
|
1338
1343
|
**Rationale:** the user's prompt is natural language ("Reply with just the number") and routinely conflicts with the grammar's operational contract. Without an explicit requirement block, the model obeys the prompt literally and never reaches for SEND. Requirements are the contract that wins those conflicts.
|
|
1339
1344
|
|
package/dist/Paths.js
CHANGED
|
@@ -21,17 +21,17 @@ export default class Paths {
|
|
|
21
21
|
// the end of the user packet — names rules the model has to honor that
|
|
22
22
|
// the grammar block doesn't cover (e.g. "loop concludes with SEND[200]").
|
|
23
23
|
static defaultRequirements = Paths.#resolveDefaultRequirements();
|
|
24
|
-
// Resolve the default requirements file: `
|
|
24
|
+
// Resolve the default requirements file: `PLURNK_SERVICE_REQUIREMENTS` env (absolute
|
|
25
25
|
// or relative-to-package-root) overrides the in-package `requirements.md`.
|
|
26
26
|
static #resolveDefaultRequirements() {
|
|
27
|
-
const env = process.env.
|
|
27
|
+
const env = process.env.PLURNK_SERVICE_REQUIREMENTS;
|
|
28
28
|
if (typeof env === "string" && env.length > 0) {
|
|
29
29
|
return resolve(Paths.#PACKAGE_ROOT, env);
|
|
30
30
|
}
|
|
31
31
|
return resolve(Paths.#PACKAGE_ROOT, "requirements.md");
|
|
32
32
|
}
|
|
33
33
|
// Operator reference docs auto-READ into every model run at turn 0.
|
|
34
|
-
// `
|
|
34
|
+
// `PLURNK_SERVICE_MD_<ALIAS>=<path>` materializes <path>'s markdown as a
|
|
35
35
|
// `plurnk:///<ALIAS>.md` entry the model READs — an idiomatic, userland way
|
|
36
36
|
// to inject standing context (an ordinary entry + READ op, not a bespoke
|
|
37
37
|
// packet section). `~` expands to home; relative paths resolve
|
|
@@ -39,9 +39,9 @@ export default class Paths {
|
|
|
39
39
|
static docs() {
|
|
40
40
|
const out = [];
|
|
41
41
|
for (const [key, value] of Object.entries(process.env)) {
|
|
42
|
-
if (!key.startsWith("
|
|
42
|
+
if (!key.startsWith("PLURNK_SERVICE_MD_") || typeof value !== "string" || value.length === 0)
|
|
43
43
|
continue;
|
|
44
|
-
const alias = key.slice("
|
|
44
|
+
const alias = key.slice("PLURNK_SERVICE_MD_".length);
|
|
45
45
|
if (alias.length === 0)
|
|
46
46
|
continue;
|
|
47
47
|
const expanded = value.startsWith("~/") ? resolve(homedir(), value.slice(2)) : value === "~" ? homedir() : value;
|
package/dist/Paths.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Paths.js","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,gDAAgD;AAChD,+EAA+E;AAC/E,6EAA6E;AAC7E,oCAAoC;AACpC,EAAE;AACF,8EAA8E;AAC9E,mDAAmD;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,OAAO,OAAO,KAAK;IACtB,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC;IAE1G,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,CAAC,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtE,+EAA+E;IAC/E,qFAAqF;IACrF,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,mBAAmB,GAAG,KAAK,CAAC,2BAA2B,EAAE,CAAC;IAEjE,
|
|
1
|
+
{"version":3,"file":"Paths.js","sourceRoot":"","sources":["../src/Paths.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,gDAAgD;AAChD,+EAA+E;AAC/E,6EAA6E;AAC7E,oCAAoC;AACpC,EAAE;AACF,8EAA8E;AAC9E,mDAAmD;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,OAAO,OAAO,KAAK;IACtB,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9E,MAAM,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,qCAAqC,CAAC,CAAC,CAAC,CAAC;IAE1G,MAAM,CAAC,UAAU,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;IAC/D,MAAM,CAAC,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IACtE,+EAA+E;IAC/E,qFAAqF;IACrF,uEAAuE;IACvE,uEAAuE;IACvE,0EAA0E;IAC1E,MAAM,CAAC,mBAAmB,GAAG,KAAK,CAAC,2BAA2B,EAAE,CAAC;IAEjE,qFAAqF;IACrF,2EAA2E;IAC3E,MAAM,CAAC,2BAA2B;QAC9B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC;QACpD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;QACD,OAAO,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;IAC3D,CAAC;IAED,oEAAoE;IACpE,yEAAyE;IACzE,4EAA4E;IAC5E,yEAAyE;IACzE,+DAA+D;IAC/D,2EAA2E;IAC3E,MAAM,CAAC,IAAI;QACP,MAAM,GAAG,GAA+C,EAAE,CAAC;QAC3D,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACrD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,oBAAoB,CAAC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACvG,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC;YACrD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;gBAAE,SAAS;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;YACjH,GAAG,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,GAAG,KAAK,KAAK,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,aAAa,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QACzF,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ChannelWrite.d.ts","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG5C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;AAMtE,MAAM,WAAW,gBAAgB;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;AAQvF,MAAM,WAAW,cAAc;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAC;AAU9D,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IAMf,KAAK,CAAC,EAAE,SAAS,CAAC;CACrB,KAAK,OAAO,CAAC;IAAE,MAAM,EAAE,oBAAoB,GAAG,mBAAmB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAOtF,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;AAQzD,MAAM,WAAW,qBAAqB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,qBAAqB,KAAK,IAAI,CAAC;AAW/F,MAAM,CAAC,OAAO,OAAO,YAAY;;WAqBhB,eAAe,CACxB,EAAE,EAAE,EAAE,EACN,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAC7L,OAAO,CAAC,IAAI,CAAC;WAeH,eAAe,CACxB,EAAE,EAAE,EAAE,EACN,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,YAAY,CAAC;QAAC,MAAM,CAAC,EAAE,iBAAiB,CAAC;QAAC,UAAU,CAAC,EAAE,gBAAgB,CAAA;KAAE,GACtK,OAAO,CAAC,IAAI,CAAC;WA2BH,gBAAgB,CACzB,EAAE,EAAE,EAAE,EACN,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,OAAO,CAAA;KAAE,GACnL,OAAO,CAAC,MAAM,CAAC;WAML,iBAAiB,CAC1B,EAAE,EAAE,EAAE,EACN,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GACvE,OAAO,CAAC,IAAI,CAAC;WAOH,kBAAkB,CAC3B,EAAE,EAAE,EAAE,EACN,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GACjE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;WAKZ,sBAAsB,CAC/B,EAAE,EAAE,EAAE,EACN,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;WAUpD,2BAA2B,CACpC,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;WAOpC,qCAAqC,CAC9C,EAAE,EAAE,EAAE,EACN,KAAK,EAAE,MAAM,GACd,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAGpD"}
|
|
@@ -48,6 +48,23 @@ export default class ChannelWrite {
|
|
|
48
48
|
const result = await ChannelWrite.#stateStmt(db).run({ state, entry_id: entryId, channel });
|
|
49
49
|
if (result.changes === 0)
|
|
50
50
|
return;
|
|
51
|
+
// §semantic-fts-at-write — a stream's accumulated BODY content becomes keyword-searchable
|
|
52
|
+
// the moment the channel settles (closed/errored), not a pump-pass later. Body only: the
|
|
53
|
+
// FTS row is per-ENTRY (rowid = entry id) and the fusion's keyword half is the body's.
|
|
54
|
+
if ((state === "closed" || state === "errored") && channel === "body") {
|
|
55
|
+
const row = await db.channel_meta.get({ entry_id: entryId, channel });
|
|
56
|
+
if (row !== undefined) {
|
|
57
|
+
const body = await db.read_channel_content.get({ entry_id: entryId, channel });
|
|
58
|
+
if (body !== undefined) {
|
|
59
|
+
await db.fts_delete.run({ entry_id: entryId });
|
|
60
|
+
// §membership-binary-sniff — the same NUL guard as member ingest: a streamed
|
|
61
|
+
// binary (an http READ of an image) must not enter the keyword index.
|
|
62
|
+
const head = body.content.slice(0, 8192);
|
|
63
|
+
if (body.content.length > 0 && !head.includes("\u0000"))
|
|
64
|
+
await db.fts_insert.run({ entry_id: entryId, content: body.content });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
51
68
|
if (notify === undefined)
|
|
52
69
|
return;
|
|
53
70
|
const meta = await ChannelWrite.#channelMeta(db).get({ entry_id: entryId, channel });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChannelWrite.js","sourceRoot":"","sources":["../../src/core/ChannelWrite.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,3 +77,8 @@ WHERE e.scope = 'session' AND e.session_id = $session_id
|
|
|
77
77
|
AND s.closed_at IS NOT NULL
|
|
78
78
|
ORDER BY s.closed_at DESC
|
|
79
79
|
LIMIT 1;
|
|
80
|
+
|
|
81
|
+
-- PREP: read_channel_content
|
|
82
|
+
-- §semantic-fts-at-write — a settled stream's accumulated body, read once for the
|
|
83
|
+
-- write-time FTS index.
|
|
84
|
+
SELECT content FROM entry_channels WHERE entry_id = $entry_id AND name = $channel;
|
|
@@ -7,7 +7,7 @@ import type TelemetryChannel from "./TelemetryChannel.ts";
|
|
|
7
7
|
import type ProposalLifecycle from "./ProposalLifecycle.ts";
|
|
8
8
|
import type { WriterTier } from "./scheme-types.ts";
|
|
9
9
|
import type { StreamEventNotify, WakeRunNotify, InjectRunNotify, CancelRunNotify } from "./ChannelWrite.ts";
|
|
10
|
-
export type PrematureReason = "live-thing" | "
|
|
10
|
+
export type PrematureReason = "live-thing" | "groundless-hibernate";
|
|
11
11
|
export type DispatchContext = {
|
|
12
12
|
statement: PlurnkStatement;
|
|
13
13
|
sessionId: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/Dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAmD,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAC;AAO5D,OAAO,KAAK,EAAkB,UAAU,EAAkC,MAAM,mBAAmB,CAAC;AAEpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAgB5G,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,
|
|
1
|
+
{"version":3,"file":"Dispatcher.d.ts","sourceRoot":"","sources":["../../src/core/Dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAmD,MAAM,wBAAwB,CAAC;AAC/G,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,EAAE,EAAc,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,gBAAgB,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAC;AAO5D,OAAO,KAAK,EAAkB,UAAU,EAAkC,MAAM,mBAAmB,CAAC;AAEpG,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAgB5G,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,sBAAsB,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAC1B,SAAS,EAAE,eAAe,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAM1C,gBAAgB,CAAC,EAAE,eAAe,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAAE,CAAC;AAgBxF,MAAM,CAAC,OAAO,OAAO,UAAU;;gBAgBf,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE;QACnJ,EAAE,EAAE,EAAE,CAAC;QACP,OAAO,EAAE,cAAc,CAAC;QACxB,SAAS,EAAE,SAAS,CAAC;QACrB,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;QACnC,SAAS,EAAE,gBAAgB,CAAC;QAC5B,SAAS,EAAE,iBAAiB,CAAC;QAC7B,SAAS,EAAE,MAAM,gBAAgB,GAAG,SAAS,CAAC;QAC9C,UAAU,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,WAAW,GAAG,SAAS,CAAC;QACxD,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;QACtC,aAAa,CAAC,EAAE,aAAa,CAAC;QAC9B,SAAS,CAAC,EAAE,eAAe,CAAC;QAC5B,SAAS,CAAC,EAAE,eAAe,CAAC;KAC/B;IAeK,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC;IA2H3D,IAAI,CAAC,OAAO,EAAE;QAChB,SAAS,EAAE,eAAe,CAAC;QAC3B,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QACjD,MAAM,CAAC,EAAE,UAAU,CAAC;KACvB,GAAG,OAAO,CAAC,cAAc,CAAC;IA+VrB,eAAe,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAgB,EAAE,EAAE;QAC3F,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,CAAC;KAC3H,GAAG,OAAO,CAAC,MAAM,CAAC;CA6RtB"}
|
package/dist/core/Dispatcher.js
CHANGED
|
@@ -103,6 +103,15 @@ class Dispatcher {
|
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
// §fold-open-meta-operations — OPEN/FOLD are render directives, not actions: they change
|
|
107
|
+
// how the world DISPLAYS, never what it is. A successful one leaves NO log row — the next
|
|
108
|
+
// packet's render IS the receipt (the row shows collapsed/expanded), and a curation
|
|
109
|
+
// receipt that itself rents log space made curation self-defeating in the small (the
|
|
110
|
+
// grinder's own mechanical folds were already rowless — one rule for both curators).
|
|
111
|
+
// Failures keep the ordinary op row with their status: errors are signals.
|
|
112
|
+
if ((statement.op === "OPEN" || statement.op === "FOLD") && result.status < 400) {
|
|
113
|
+
return { ...result, rowsWritten: 0 };
|
|
114
|
+
}
|
|
106
115
|
const logEntryId = await this.#writeLog({ statement, result, runId, loopId, turnId, sequence, origin });
|
|
107
116
|
onDispatch?.(logEntryId);
|
|
108
117
|
// Proposal lifecycle (SPEC.md §engine-rails + §methods loop.resolve; §proposal-202-pauses). When a
|
|
@@ -649,14 +658,12 @@ class Dispatcher {
|
|
|
649
658
|
// Premature terminate (§send-premature-terminate): a terminal SEND[200] is REFUSED 409 — the row
|
|
650
659
|
// keeps the [200] emission + body (faithful, never erased), the loop never goes terminal. The
|
|
651
660
|
// decision is the runTurn PRE-DISPATCH snapshot (threaded), so a same-turn fire-and-forget spawn
|
|
652
|
-
// isn't miscounted.
|
|
653
|
-
//
|
|
661
|
+
// isn't miscounted. Runtime-only: a live thing the run holds (a spawned child's status / an open
|
|
662
|
+
// stream), which the parser can never see. Grammar-shape terminals (a SEND after a same-turn
|
|
663
|
+
// READ) are the parser's job to reject (grammar#51), not the engine's.
|
|
654
664
|
if (status === 200 && prematureRefusal === "live-thing") {
|
|
655
665
|
return { status: 409, error: "Attempted [200] termination despite active streams or worker runs. You may either hibernate [202] to wait or KILL them before terminating." };
|
|
656
666
|
}
|
|
657
|
-
if (status === 200 && prematureRefusal === "submitted-read") {
|
|
658
|
-
return { status: 409, error: "Attempted termination with submitted READ operation(s). SEND[102]; the results arrive next turn." };
|
|
659
|
-
}
|
|
660
667
|
// Groundless hibernation (§send-groundless-hibernate): a SEND[202] alongside a same-turn READ,
|
|
661
668
|
// with no wake edge — the READ's result folds back on a next turn this park would never reach,
|
|
662
669
|
// so the model is sleeping on its own unanswered question. Refused 409 on the record, same
|