@plurnk/plurnk-service 1.5.0 → 1.6.1
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.defaults +22 -27
- package/INSTALL.md +22 -17
- package/README.md +1 -1
- package/SPEC.md +505 -362
- package/dist/build-info.json +1 -1
- package/dist/content/edit-collision.d.ts +5 -0
- package/dist/content/edit-collision.d.ts.map +1 -0
- package/dist/content/edit-collision.js +14 -0
- package/dist/content/edit-collision.js.map +1 -0
- package/dist/content/edit-receipt.d.ts +3 -2
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +58 -13
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/index.d.ts +4 -1
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +3 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-anchors.d.ts +41 -0
- package/dist/content/line-anchors.d.ts.map +1 -0
- package/dist/content/line-anchors.js +191 -0
- package/dist/content/line-anchors.js.map +1 -0
- package/dist/content/read-projector.d.ts +7 -1
- package/dist/content/read-projector.d.ts.map +1 -1
- package/dist/content/read-projector.js +46 -10
- package/dist/content/read-projector.js.map +1 -1
- package/dist/content/read-resolve.js +3 -3
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +9 -2
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/ChannelWrite.js +1 -1
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +3 -3
- package/dist/core/CoreSchemeServices.d.ts +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +15 -5
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +220 -978
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/DurableStatement.d.ts.map +1 -1
- package/dist/core/DurableStatement.js +2 -0
- package/dist/core/DurableStatement.js.map +1 -1
- package/dist/core/Engine.d.ts +18 -43
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +90 -1554
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +192 -112
- package/dist/core/ExecutableTools.d.ts +10 -0
- package/dist/core/ExecutableTools.d.ts.map +1 -0
- package/dist/core/ExecutableTools.js +41 -0
- package/dist/core/ExecutableTools.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +3 -3
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +2 -2
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/LogBody.d.ts +3 -0
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +21 -5
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts +14 -0
- package/dist/core/LogEntryProjection.d.ts.map +1 -0
- package/dist/core/LogEntryProjection.js +38 -0
- package/dist/core/LogEntryProjection.js.map +1 -0
- package/dist/core/ModelCall.d.ts +27 -0
- package/dist/core/ModelCall.d.ts.map +1 -0
- package/dist/core/ModelCall.js +127 -0
- package/dist/core/ModelCall.js.map +1 -0
- package/dist/core/PacketBuilder.d.ts +14 -25
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +170 -244
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts +1 -1
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +15 -3
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +2 -2
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +17 -9
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +25 -8
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/ResourceMutations.d.ts +73 -0
- package/dist/core/ResourceMutations.d.ts.map +1 -0
- package/dist/core/ResourceMutations.js +1142 -0
- package/dist/core/ResourceMutations.js.map +1 -0
- package/dist/core/SchemeRegistry.js +10 -10
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StoredPacket.d.ts +2 -2
- package/dist/core/StoredPacket.js +7 -7
- package/dist/core/StrikeRail.d.ts +0 -1
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +6 -4
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +72 -0
- package/dist/core/TurnRunner.d.ts.map +1 -0
- package/dist/core/TurnRunner.js +1730 -0
- package/dist/core/TurnRunner.js.map +1 -0
- package/dist/core/caps/CapsResolve.js +1 -1
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +4 -4
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +2 -1
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +8 -5
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbProjectionCaps.d.ts +1 -0
- package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +29 -0
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +5 -2
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +6 -4
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/content-hash.js +2 -2
- package/dist/core/content-hash.js.map +1 -1
- package/dist/core/content-weight.d.ts +2 -0
- package/dist/core/content-weight.d.ts.map +1 -0
- package/dist/core/content-weight.js +4 -0
- package/dist/core/content-weight.js.map +1 -0
- package/dist/core/content_weight.d.ts +4 -0
- package/dist/core/content_weight.d.ts.map +1 -0
- package/dist/core/content_weight.js +6 -0
- package/dist/core/content_weight.js.map +1 -0
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +23 -7
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +38 -17
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +1 -2
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.js +1 -1
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/packet-wire.d.ts +6 -3
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +257 -99
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/provider-accounting.d.ts +24 -0
- package/dist/core/provider-accounting.d.ts.map +1 -0
- package/dist/core/provider-accounting.js +76 -0
- package/dist/core/provider-accounting.js.map +1 -0
- package/dist/core/scheme-types.d.ts +1 -1
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/search-gate.d.ts +2 -2
- package/dist/core/search-gate.d.ts.map +1 -1
- package/dist/core/search-gate.js +8 -8
- package/dist/core/search-gate.js.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +221 -110
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +40 -18
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +136 -110
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +1 -1
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +5 -4
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +25 -23
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +11 -0
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +172 -107
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +48 -32
- package/dist/schemes/Prompt.d.ts +3 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +4 -1
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts +4 -4
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -0
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts +4 -4
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +11 -7
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +0 -1
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +19 -13
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +4 -16
- package/dist/schemes/_entry-find.d.ts +17 -16
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +58 -52
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +2 -21
- package/dist/schemes/_entry-graph.sql +1 -1
- package/dist/schemes/_entry-manifest.d.ts +5 -17
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +32 -28
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +5 -4
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +89 -49
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +30 -4
- package/dist/schemes/_entry-send.js +4 -4
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +10 -2
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/schemes/exec-abort.js +2 -2
- package/dist/schemes/exec-abort.js.map +1 -1
- package/dist/server/Daemon.d.ts +7 -42
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +139 -900
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +117 -0
- package/dist/server/DrainSupervisor.d.ts.map +1 -0
- package/dist/server/DrainSupervisor.js +802 -0
- package/dist/server/DrainSupervisor.js.map +1 -0
- package/dist/server/drain.sql +2 -2
- package/dist/server/envelope.d.ts +0 -2
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +2 -2
- package/dist/server/lifecycle-recovery.sql +28 -15
- package/dist/server/logEntry.d.ts +2 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +14 -2
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +5 -1
- package/dist/server/seam-entry-read.sql +2 -2
- package/dist/service.js +1 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +622 -216
- package/package.json +39 -34
- package/dist/core/BudgetOverflow.d.ts +0 -20
- package/dist/core/BudgetOverflow.d.ts.map +0 -1
- package/dist/core/BudgetOverflow.js +0 -49
- package/dist/core/BudgetOverflow.js.map +0 -1
- package/dist/core/caps/DbTagCaps.d.ts +0 -10
- package/dist/core/caps/DbTagCaps.d.ts.map +0 -1
- package/dist/core/caps/DbTagCaps.js +0 -46
- package/dist/core/caps/DbTagCaps.js.map +0 -1
- package/dist/core/ruler_count.d.ts +0 -4
- package/dist/core/ruler_count.d.ts.map +0 -1
- package/dist/core/ruler_count.js +0 -7
- package/dist/core/ruler_count.js.map +0 -1
- package/dist/core/token-ruler.d.ts +0 -2
- package/dist/core/token-ruler.d.ts.map +0 -1
- package/dist/core/token-ruler.js +0 -5
- package/dist/core/token-ruler.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -26,8 +26,9 @@ flowchart LR
|
|
|
26
26
|
| **agent** | PLURNK | The plurnk runtime. Acts in-workspace as the reserved `plurnk` worker ({§actor-boundary} self-hosting), never a privileged singleton owning its own entries ({§entry-owner}, {§machine-processes}). |
|
|
27
27
|
| **workspace** | Core | Durable user-named shared world. Persists across workers and process restarts. Identity: `workspaces.id` + unique `workspaces.name`. |
|
|
28
28
|
| **worker** | Core | Durable actor and private history over one workspace. Owns its loops and log rows, may carry a `parent_worker_id`, and has one process-local cancellation scope while active. |
|
|
29
|
-
| **loop** | Core | Queued-to-terminal unit of model or client work within a worker. Status ∈ {100 pending · 102 running · 200 done · 202 waiting (blocked on a live obligation, {§send}) · 413
|
|
29
|
+
| **loop** | Core | Queued-to-terminal unit of model or client work within a worker. Status ∈ {100 pending · 102 running · 200 done · 202 waiting (blocked on a live obligation, {§send}) · 413 provider input-capacity failure · 429 turn-ceiling · 499 cancelled · 500 failed · 504 wall-clock timeout ({§operator-config-loop-timeout}) · 508 runaway}. Many loops may belong to one worker. |
|
|
30
30
|
| **turn** | Core | One engine scheduling unit (or one client-op scheduling unit). A model turn sends one assembled prompt through one or more emission attempts and admits at most one response. Many turns may belong to one loop. Identity: `(loop_id, sequence)`. |
|
|
31
|
+
| **model call** | Core/provider | One logical `provider.generate` invocation. Emission attempts and BARE inferences share this durable accounting owner; provider retries remain cardinal physical requests beneath it. Identity: `(turn_id, sequence)`. |
|
|
31
32
|
| **op** | Model/core | One DSL operation the model emits, parsed into a `PlurnkStatement`. One admitted turn produces zero or more ops. |
|
|
32
33
|
| **statement** | Model/core | A parsed op: the `PlurnkStatement` AST from `@plurnk/plurnk-contracts`. |
|
|
33
34
|
| **action** | Core | One executed op. Execution normally produces a `log_entries` row at `log:///<L>/<T>/<S>/<op>`; an engine rail may instead record an actionless `op='error'` row ({§operation-results}). Actionless artifacts carry no fabricated operation. |
|
|
@@ -35,14 +36,14 @@ flowchart LR
|
|
|
35
36
|
| **AG-UI Run** | AG-UI protocol | A client request/stream envelope identified by the client's `runId`. A message or resume AG-UI Run binds to one core loop; a management-action AG-UI Run may complete without creating a core loop. |
|
|
36
37
|
| **AG-UI thread** | AG-UI protocol | Conversation identity. Within an explicitly selected workspace, `threadId` resolves to one conversation worker. |
|
|
37
38
|
| **`--run`** | Client compatibility | A compatibility-sensitive client spelling, not an internal entity. |
|
|
38
|
-
| **session** | Retired/unqualified | Not a PLURNK lifecycle noun. Use the actual core noun; a third-party standard may use only its explicitly qualified protocol term.
|
|
39
|
+
| **session** | Retired/unqualified | Not a PLURNK lifecycle noun. Use the actual core noun; a third-party standard may use only its explicitly qualified protocol term. <!-- lexicon-allow: this row defines the retired noun --> |
|
|
39
40
|
|
|
40
41
|
### §storage-terms Storage terms
|
|
41
42
|
|
|
42
43
|
| Term | Meaning |
|
|
43
44
|
|---|---|
|
|
44
|
-
| **entry** | The unit of canonical state. Identity: `(workspace, owner, scheme, pathname)` ({§entry-identity-no-null}). Holds one or more `channels` of content plus
|
|
45
|
-
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, `
|
|
45
|
+
| **entry** | The unit of canonical state. Identity: `(workspace, owner, scheme, pathname)` ({§entry-identity-no-null}). Holds one or more `channels` of content plus private `attributes`. |
|
|
46
|
+
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, curation `weight`, and lifecycle `state`. |
|
|
46
47
|
| **scope** | A scheme-manifest declaration ignored by core; registrations are discovered at boot and are not persisted. Entry sharing and privacy are owner-based; #80 owns retiring this residual axis. |
|
|
47
48
|
| **scheme** | An addressed capability family + handler. Built-ins include `worker`, `prompt`, `log`, and bare/file paths; discovered schemes and executor-runtime tags extend that set. Internal `exec` routes the EXEC op but is not an addressable model namespace. Consumption surface {§scheme-surface}; author contract: [plurnk-schemes](../plurnk-schemes/SPEC.md). |
|
|
48
49
|
| **mimetype** | A channel's content type. Drives the handler that produces the structural projections (`symbols`, `deepJson`, `deepXml`). Consumption surface {§mimetype-surface}; author contract: [plurnk-mimetypes](../plurnk-mimetypes/SPEC.md). |
|
|
@@ -76,8 +77,7 @@ These are the complete strike sources:
|
|
|
76
77
|
| Strike source | Exact trigger | Model-visible occurrence |
|
|
77
78
|
|---------------------|------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
|
|
78
79
|
| Hard result | An admitted non-`EXEC` operation or bounded parse-error status is `>= 400`, except the soft set `404`, `409`, `416`, `501`. | The originating failure row. |
|
|
79
|
-
|
|
|
80
|
-
| Terminal steering | An idle `SEND[102]` or a final disposition refused at 409 sets the turn's steering ruling ({§send}). | The idle rail row or refused SEND row. |
|
|
80
|
+
| Terminal steering | An idle SEND with signal `102` or a final disposition refused at 409 sets the turn's steering ruling ({§send}). | The idle rail row or refused SEND row. |
|
|
81
81
|
| Cycle | The configured consecutive fingerprint pattern repeats. | None; cycle detection itself is private engine accounting. |
|
|
82
82
|
|
|
83
83
|
`EXEC` results remain exact model-visible evidence but are always soft: an
|
|
@@ -98,6 +98,7 @@ shown. The current streak may ride first-party provider metadata
|
|
|
98
98
|
| **verdict** | The end-of-turn ruling computed inline in `Engine.runLoop` from the strike rail and independent loop terminals. No filter chain. |
|
|
99
99
|
| **strike** | One admitted turn matching at least one source above. |
|
|
100
100
|
| **emission attempt** | One completed provider exchange beneath an engine turn. ANTLR admits it when it has a trustworthy PLAN...SEND frame and no boundary-destroying tail. A hard error bounded to an interior statement becomes a failed operation inside the admitted turn; a rejected attempt is forensic evidence, not another turn or an engine strike. |
|
|
101
|
+
| **BARE inference** | One body-only child-provider model call whose response becomes an ordinary BARE log result. It has no worker, packet, tools, output grammar, or persistent child state ({§bare-inference}). |
|
|
101
102
|
| **cycle** | A repeated turn fingerprint across consecutive turns. Detection strikes silently under the rule above. |
|
|
102
103
|
| §mode-ask-read-only **mode** | `"ask" \| "act"`. Per-loop. Ask = read-only: the dispatch gate refuses every side-effecting op (a filesystem write — EDIT/COPY-dest/MOVE/KILL on the `file` scheme — or any EXEC invocation); reads of the workspace stay open. `act` = full surface. Ask never changes the world. |
|
|
103
104
|
| **flag** | Per-loop value: `mode`, `noWeb`, and `noInteraction` shape scheme authority ({§manifest-flag-affinity}); `auto` and `noProposals` select proposal settlement. |
|
|
@@ -191,7 +192,18 @@ Composed daemon internals + admin CLI. Four plug points:
|
|
|
191
192
|
- **Mimetypes** ({§mimetype}) — content interpretation. Render-time handlers consume channel content; framework owns the dispatch.
|
|
192
193
|
- **Executors** ({§exec} / {§bundled-set}) — EXEC runtime dispatch for subprocess, search, data, and pure-computation runtimes.
|
|
193
194
|
|
|
194
|
-
|
|
195
|
+
Core's internal owners compose without becoming new package or public seams:
|
|
196
|
+
|
|
197
|
+
| Owner | Machine |
|
|
198
|
+
|-------|---------|
|
|
199
|
+
| `Daemon` | Process/module lifecycle, dependency composition, provider policy, notifications, and the external client façade. |
|
|
200
|
+
| `DrainSupervisor` | One worker's queue consumer, drain identity, wake obligations, cancellation scope, poll/park timers, and terminal cleanup. |
|
|
201
|
+
| `Engine` | Loop lifecycle and the public turn, dispatch, derivation, and proposal façades. |
|
|
202
|
+
| `TurnRunner` | One durable model turn from materialization through provider admission, operation scheduling, and settlement. |
|
|
203
|
+
| `Dispatcher` | Operation admission/routing, scheme execution, proposal waiting, curation, and durable log writes. |
|
|
204
|
+
| `ResourceMutations` | EDIT/COPY/MOVE selection, anchor preconditions, cross-scheme effects, and mutation settlement. |
|
|
205
|
+
|
|
206
|
+
Capability-specific behavior remains with the owning plug point.
|
|
195
207
|
|
|
196
208
|
The contracts package (`@plurnk/plurnk-contracts`) owns the parser and AST contract. Schemes receive parsed statement fragments via dispatch.
|
|
197
209
|
|
|
@@ -231,7 +243,7 @@ preserving the originating failure.
|
|
|
231
243
|
flowchart LR
|
|
232
244
|
actor["Worker A"] -->|"shared file or shared entry op"| state["Shared project files<br/>and shared workspace entries"]
|
|
233
245
|
state -->|"folded attributed delta<br/>environment door"| log["Worker B log"]
|
|
234
|
-
actor -->|"SEND
|
|
246
|
+
actor -->|"SEND to worker B<br/>voice door"| log
|
|
235
247
|
client["User / client"] -->|"loop.inject<br/>voice door"| log
|
|
236
248
|
```
|
|
237
249
|
|
|
@@ -253,7 +265,7 @@ file or ancestry-authorized entry through ordinary dispatch ({§worker-read-scop
|
|
|
253
265
|
| Door | Carries | Wake behavior |
|
|
254
266
|
| ----------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
255
267
|
| Environment | A change to a shared project file or shared worker entry, as a folded attributed delta. | Ambient state never wakes an idle worker ({§env-delta}). |
|
|
256
|
-
| Voice | A directed `loop.inject` or
|
|
268
|
+
| Voice | A directed `loop.inject` or `## SEND0 (worker://name)` message. | An active worker folds it into its next turn; an idle one wakes. |
|
|
257
269
|
|
|
258
270
|
§actor-boundary-no-mutex **Wild west by default; explicit branch batches are the exception.** Ordinary workers share workspace state without locks. Coordination is cooperative and softly fenced (the {§membership} `read-only` overlay, a workspace policy, bounds every worker's writable surface uniformly — {§machine-processes}); a conflict *surfaces* as a delta rather than being prevented. A branch-tagged WORK/FORK opts the whole workspace into the bounded, exclusive Git transaction in {§worker-branch-batch}. It is not a general entry mutex or a hidden per-worker filesystem.
|
|
259
271
|
|
|
@@ -290,7 +302,9 @@ the `plurnk` worker's log; the model sees the shared entry through its own READ.
|
|
|
290
302
|
Client-provided workspace documents union with the operator set at the same
|
|
291
303
|
entry surface.
|
|
292
304
|
|
|
293
|
-
§actor-boundary-catalog-preview **Catalog preview.** `PLURNK_SERVICE_FILES_ITEMS` foists turn-0 FINDs into the worker's first turn, so a worker opens with a navigable map instead of blank. An enabled preview
|
|
305
|
+
§actor-boundary-catalog-preview **Catalog preview.** `PLURNK_SERVICE_FILES_ITEMS` foists turn-0 FINDs into the worker's first turn, so a worker opens with a navigable map instead of blank. An enabled preview executes exactly four orienting surveys in order: project files (`## FIND0 [+init] (*)`), workspace commons (`## FIND0 [+init] (worker:///*)`), the worker's own space (`## FIND0 [+init] (worker://~/*)`), and kernel docs (`## FIND0 [+init,+docs] (worker://plurnk/docs/**) <1,-1>`). Their log classifications make the opening survey one `init` set while retaining `docs` on its documentation row ({§log-item-tags}). A shallow result renders direct entries normally and every deeper first-segment directory as an actionable `dir/**` summary with its recursive `items` and `tokens`. Ordinary surveys use FIND's markerless first-16 page, whose range metadata reports the requested and returned page against the complete result total; only the small curated kernel-doc surface explicitly selects all. The opening exemplar therefore demonstrates both `*` and `**` without normalizing an all-results override. Every survey executes even when empty because zero results are useful orientation. A positive `N` explicitly caps only the file map's rendered rows, using the map's actual direct-entry-plus-directory count; `-1` enables the ordinary markerless page; unset / `0` disables previews. `log://` is absent because the current worker's log already renders in present mode.
|
|
306
|
+
|
|
307
|
+
§worker-initialization-entry **Worker initialization is not model output.** A worker's first loop begins with one born-OPEN actionless row at `log:///1/1/1`: `origin="plurnk"`, `op` null, and `attrs.kind="initialization"`. Its `text/vnd.plurnk` body dynamically mirrors the turn-zero PLAN, the orienting operations actually dispatched, and terminal `SEND0 [102]`. The PLAN states `* Initialization complete.` and `* Next: address the prompt.`; SEND hands off with `Next, address the prompt.`
|
|
294
308
|
|
|
295
309
|
### §machine-processes The machine and its processes: workspace, worker, fork
|
|
296
310
|
|
|
@@ -326,8 +340,8 @@ terminal history.**
|
|
|
326
340
|
| Project files ({§machine-processes-one-filesystem}) | Workspace | Shared live; a fork does not create another checkout. |
|
|
327
341
|
| Shared worker entries (`worker:///...`) | Workspace commons | Shared live. |
|
|
328
342
|
| Membership overlay ({§machine-processes-one-overlay}) | Workspace | Shared unchanged; divergent membership requires another workspace. |
|
|
329
|
-
| Log items ({§machine-processes-fork-copies-the-log}) | Worker | Rows, event identities, tags, fold state, and the matching observation cursor are copied as terminal history.
|
|
330
|
-
| §machine-processes-fork-cost **
|
|
343
|
+
| Log items ({§machine-processes-fork-copies-the-log}) | Worker | Rows, event identities, curation effects, tags, fold state, and the matching observation cursor are copied as terminal history. |
|
|
344
|
+
| §machine-processes-fork-cost **Provider evidence and accounting** | Worker | Turns and their model-facing log history are copied, but `model_calls`, emission-admission rows, and physical provider requests are not: one issued call or request has one owning worker. Parent and fork accounting therefore includes only work issued in that branch, while workspace accounting never double-counts copied history. |
|
|
331
345
|
| Private worker entries (`worker://~/...`) | Worker | Deep-copied with ownership remapped; parent and child then diverge. |
|
|
332
346
|
| Active loops, turns, and cancellation | Worker | Never copied as live work; inherited structure is terminal history, then a new loop starts. |
|
|
333
347
|
|
|
@@ -344,6 +358,14 @@ therefore carries only shared project-file and shared-entry changes
|
|
|
344
358
|
|
|
345
359
|
§machine-processes-worker-origin **A worker carries its actor.** Each worker records its `origin` — `model` (a conversation), `client` (a client-interface actor), or `plurnk` (the runtime's self-hosting worker) — set once at creation and inherited by a fork. `listWorkers` returns it, so a client interface identifies actor class without parsing the name, which is set at instantiation and immutable (a worker is permanent history, {§machine-processes-worker-is-its-log}).
|
|
346
360
|
|
|
361
|
+
§worker-provider-identity **A worker owns a durable provider identity distinct
|
|
362
|
+
from its database id.** Creation mints a globally unique, opaque 128-bit value;
|
|
363
|
+
forks mint their own value. Core supplies it as the provider `workerId` for every
|
|
364
|
+
emission and supplies the lineage root's value as `primaryWorkerId`
|
|
365
|
+
({§provider-cache-identity}). Database ids remain the internal relational and
|
|
366
|
+
client coordinate. BARE calls use isolated per-call provider identities rather
|
|
367
|
+
than either worker value.
|
|
368
|
+
|
|
347
369
|
§worker-primary **The primary worker is the lineage root.** The PRIMARY worker of a turn's lineage is the no-parent root reached by walking `parent_worker_id` up; a no-parent worker is its own primary. Core supplies it on the first-party metadata channel alongside `Worker-Id` (same gate, computed per turn), stamped on EVERY turn including the primary's own (where it equals `Worker-Id`) — absent-with-a-Worker-Id is a contract violation, never a silent "assume primary." An unresolvable root (a corrupt/cyclic parent chain the `parent != id` CHECK forbids) fails hard. Providers emits it as `Plurnk-Worker-Primary`; a consumer routes primary-vs-spawned by equality (`Worker-Primary == Worker-Id` ⇒ the primary; `!=` ⇒ any-depth spawn, no depth math) and groups the worker tree by the shared root.
|
|
348
370
|
|
|
349
371
|
§machine-processes-fork-shares-the-world **A fork copies worker-owned history
|
|
@@ -402,9 +424,9 @@ literal `workers.name` value.
|
|
|
402
424
|
| `READ` | existing literal name | Collect the named worker's deliverable. |
|
|
403
425
|
| `KILL` | existing literal name, `~` | Terminate the named worker or caller. |
|
|
404
426
|
|
|
405
|
-
- §worker-scheme-spawn **Spawn** —
|
|
406
|
-
- §worker-scheme-irc **irc** —
|
|
407
|
-
- §worker-scheme-fork **Fork** —
|
|
427
|
+
- §worker-scheme-spawn **Spawn** — `## WORK0 (worker://<name>)` with a task body creates a new worker sister (empty log) and starts it with that task on its first loop. WORK/FORK are the worker-creation verbs: EDIT is file/entry only, so EDIT on the bare worker entity is a **400** steering to WORK/FORK — the entity is not an entry. A name is **frozen per worker** but **reclaimable across time** ({§machine-processes-worker-origin}): a name held only by a *terminated* sister is free to reuse — a fresh spawn takes a new row and `worker_resolve_by_name` resolves the newest, the corpse keeping its name in permanent history. A name a *live* sister still holds is a conflict — **409 `worker '<name>' is already running`**, legible at the spawn gate, never a raw store-level uniqueness error.
|
|
428
|
+
- §worker-scheme-irc **irc** — `## SEND0 (worker://<name>)` with a message body delivers it to an existing sister, the **voice door** ({§actor-boundary-two-doors}): an active sister folds it into its next turn, an idle one wakes ({§actor-boundary-passive-wake}). `## SEND0 (worker://~)` targets the caller; a literal name with no worker in the workspace is 404.
|
|
429
|
+
- §worker-scheme-fork **Fork** — `## FORK0 (worker://<name>)` with a task body branches the
|
|
408
430
|
current worker into a **named** sister: its log is deep-copied
|
|
409
431
|
({§machine-processes-fork-copies-the-log}), which continues with `task`; the
|
|
410
432
|
world is shared, never copied ({§machine-processes-fork-shares-the-world}).
|
|
@@ -419,7 +441,7 @@ literal `workers.name` value.
|
|
|
419
441
|
parent's private entries — its own space deep-copied with the owner
|
|
420
442
|
remapped (source → branch) — so the branch opens with the parent's notes and
|
|
421
443
|
diverges on its own edits: *fork = everything-in-common-but-name*.
|
|
422
|
-
- **Git branch batch** —
|
|
444
|
+
- **Git branch batch** — `## WORK0 [feature/x] (worker://<name>)` and `## FORK0 [feature/x] (worker://<name>)`, each with a task body, retain their worker meanings while placing the child in the serialized Git transaction defined by {§worker-branch-batch}. The signal is one branch ref, not tags; an untagged WORK/FORK keeps the ordinary concurrent shared-world behavior.
|
|
423
445
|
- §worker-delegation-inherits-flags **Delegation inherits authority.** The live loop a spawn, fork, or irc-raised fresh loop starts with carries the **delegating loop's flags** — an auto parent delegates auto workers. Flags are a property of the delegation, not of a client binding: a child loop that fell back to defaults could propose side effects into a resolver-less headless review queue. An irc that *resumes* a parked loop leaves that loop's own flags untouched — inheritance applies only where a fresh loop is born.
|
|
424
446
|
- §worker-lifecycle-wake-requeue-not-terminal **A wake re-queue is not a terminal.** A conclusion-wake resumes a 202-blocked loop by re-queueing it (202 → 100); when that lands while the loop's own live drain is between turns, the drain **re-claims and continues** (atomic 100 → 102; the injected prompt is already the next turn). The internal re-queue is never reported as an outward terminal.
|
|
425
447
|
|
|
@@ -453,7 +475,7 @@ sequenceDiagram
|
|
|
453
475
|
participant B as Branch batch
|
|
454
476
|
participant C1 as Child branch 1
|
|
455
477
|
participant C2 as Child branch 2
|
|
456
|
-
P->>B: WORK
|
|
478
|
+
P->>B: WORK branch-1, FORK branch-2
|
|
457
479
|
P->>G: queue exclusive before releasing shared turn
|
|
458
480
|
G-->>B: all earlier turns drained
|
|
459
481
|
B->>B: snapshot clean project repository; create both refs from frozen base
|
|
@@ -469,18 +491,18 @@ sequenceDiagram
|
|
|
469
491
|
|
|
470
492
|
§worker-branch-batch-preflight **Preflight is total.** `GitMembership.projectRepository` resolves the repository containing `project_root`; absence rejects the tagged op. Earlier turns and finite derivation work drain at the exclusive boundary; a pre-existing open stream subscription is not a finite checkout operation and therefore rejects preflight rather than being silently cancelled or waited forever. Before any child starts, every branch passes `git check-ref-format --branch`, the project repository has no staged, unstaged, or nonignored untracked changes, every requested branch is absent, and the original symbolic ref/detached commit is recorded. All branch refs are then created from that frozen commit. Failure rolls back only refs created by this preflight, fails the queued children, restores the parent, and releases the workspace. Existing branches are never adopted or overwritten.
|
|
471
493
|
|
|
472
|
-
§worker-branch-batch-return **A child returns commits, never a dirty checkout.** SEND
|
|
494
|
+
§worker-branch-batch-return **A child returns commits, never a dirty checkout.** SEND signals `200`, `499`, and an already-drained `202` are refused with 409 while the project repository is off the assigned branch or dirty. The model commits or deliberately discards its changes and concludes again. On terminal, the batch records the full result commit and whether it differs from the frozen base, restores the exact original ref and commit, and only then advances. A clean child failure is a completed batch item and does not suppress later siblings; an ambiguous or dirty host failure becomes `recovery_required` and retains exclusivity because restoring would destroy or misattribute work.
|
|
473
495
|
|
|
474
496
|
The active direct child's `Git Status` names its assigned branch and states the commit-and-clean return condition. No ordinary worker receives ambient commit or authorship policy; commit identity remains host-owned and outside model teaching. {§packet-git-status}
|
|
475
497
|
|
|
476
|
-
§worker-branch-batch-receipt **The parent reconciles; the engine does not merge.** The ordinary child deliverable remains the model's exact SEND result. Its pushed termination delta and pull-side
|
|
498
|
+
§worker-branch-batch-receipt **The parent reconciles; the engine does not merge.** The ordinary child deliverable remains the model's exact SEND result. Its pushed termination delta and pull-side `## READ0 (worker://child)` append a bounded branch receipt to the presented body without changing that result: branch, item outcome, and the abbreviated result commit (`PLURNK_SERVICE_BRANCH_RECEIPT_REVISION_CHARS`; the database retains the full id). Branch refs remain after the batch. The parent chooses inspection, cherry-pick, merge, rejection, or deletion with ordinary Git tools.
|
|
477
499
|
|
|
478
500
|
§worker-branch-batch-recovery **Recovery follows durable ownership.** `branch_batches`, their ordered items, the project-repository snapshot, and result tips are schema state, not process memory. Generic boot recovery never starts their queued loops. A crash before sealing fails the unstarted batch. A queued partial preflight is rolled back only when every created ref still equals its frozen base, then retried. A running child is never replayed: its loop settles under the ordinary owner-loss rule; if the checkout is clean and either on the assigned branch or the exact original position, the committed tip is retained, the original restored, that item marked interrupted, and queued siblings continue. Any mismatch becomes `recovery_required` and keeps the workspace stopped for operator correction.
|
|
479
501
|
|
|
480
502
|
The remaining worker surfaces are:
|
|
481
503
|
|
|
482
|
-
- **Entries (storage)** — entry addressing rides the authority carving above ({§worker-authority-carving}): `worker:///` the commons, `worker://~/` the own space, a name an ancestry-gated read ({§worker-read-scope}), writes own-space-and-commons only ({§worker-write-scoping}). An entry-path `KILL` deletes under the same write-scoping (200; 404 absent; 403 named) — distinct from the path-
|
|
483
|
-
- §worker-scheme-terminate **Terminate** —
|
|
504
|
+
- **Entries (storage)** — entry addressing rides the authority carving above ({§worker-authority-carving}): `worker:///` the commons, `worker://~/` the own space, a name an ancestry-gated read ({§worker-read-scope}), writes own-space-and-commons only ({§worker-write-scoping}). An entry-path `KILL` deletes under the same write-scoping (200; 404 absent; 403 named) — distinct from the path-absent `## KILL0 (worker://<name>)` which terminates the worker ({§worker-scheme-terminate}); the discriminator is the entry path, never the op. A worker's own space is catalogued in its perspective alone (`## FIND0 [+init] (worker://~/*)`, foisted at turn 0 even when empty); isolation is the owner column, structural.
|
|
505
|
+
- §worker-scheme-terminate **Terminate** — `## KILL0 (worker://<name>)` aborts a named worker and `## KILL0 (worker://~)` aborts the caller: every unresolved loop in that worker's subtree closes 499 and every subscription in the subtree tears down; a literal name with no worker is 404. Cancellation is structured: descendants cannot detach implicitly. The override to the fire-and-forget default is not a parent-power — whoever holds the address may end it; a worker left alone simply ends at its own SEND signal `200`.
|
|
484
506
|
- §worker-scheme-cap **Cap** — `PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE` ceilings the *concurrent* active workers per workspace (a worker 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 workspaces that live for months.
|
|
485
507
|
- §worker-scheme-collect **Collect** — a worker's loop reaching a terminal status
|
|
486
508
|
surfaces to its sisters as an ambient delta ({§env-delta}): a `SEND` from
|
|
@@ -491,9 +513,9 @@ The remaining worker surfaces are:
|
|
|
491
513
|
non-2xx result surfaces folded; a failure retains its exact status and Problem. Every death-path is stamped uniformly,
|
|
492
514
|
so no termination is silent; collection is the shared world moving, never a
|
|
493
515
|
verb. The **pull** side mirrors the push: a path-absent
|
|
494
|
-
|
|
516
|
+
`## READ0 (worker://<name>)` collects that same result on demand for a
|
|
495
517
|
concluded worker; a worker **still running** has not delivered, so the READ
|
|
496
|
-
returns **425** (Too Early) and the turn's bare `
|
|
518
|
+
returns **425** (Too Early) and the turn's bare SEND signal `102` **becomes a
|
|
497
519
|
parked loop (202) on the join** ({§join-blocking-collect}) until the worker
|
|
498
520
|
delivers — the engine holds the join, the model never drives a park. A
|
|
499
521
|
missing name is 404. The model therefore reads the worker itself for its
|
|
@@ -511,7 +533,7 @@ The remaining worker surfaces are:
|
|
|
511
533
|
|
|
512
534
|
### §worker-loop-lifecycle Worker and loop lifecycle: drain, reap, and passive wake
|
|
513
535
|
|
|
514
|
-
- §join-blocking-collect **A `READ` on a running child is a blocking join, not a poll.** A path-absent
|
|
536
|
+
- §join-blocking-collect **A `READ` on a running child is a blocking join, not a poll.** A path-absent `## READ0 (worker://<running-child>)` returns **425** (Too Early) and records a live obligation on the loop. The turn's bare SEND signal `102` is converted into an indefinite parked loop (202) instead of asking the model to poll or drive the scheduler. When the child reaches any terminal status, the same loop resumes with the result in its log. Children are bounded by their own turn and strike limits, terminal failure also wakes the parent, and the owed-wake path covers completion before the parent parks. Any `SEND` clears the per-turn arm; SEND signal `200` with a live child remains a premature-termination error. A `<seconds>` timeout-poll is the explicit polling alternative.
|
|
515
537
|
|
|
516
538
|
A worker is a **log plus a cancellation scope** — one `AbortController` per worker, reused while live and replaced only once aborted, so a cancel ends the worker as a unit and a later `runLoop` request is never born cancelled. A worker's queued loops are advanced by a **drain**: a single per-worker drain that claims loops atomically (status 100→102) and runs each under the worker'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 worker holds open. Cancellation and conclusion are defined against these structures, never wall-clock timing.
|
|
517
539
|
|
|
@@ -527,18 +549,17 @@ stateDiagram-v2
|
|
|
527
549
|
Terminal --> [*]
|
|
528
550
|
```
|
|
529
551
|
|
|
530
|
-
§worker-lifecycle-state-machine The lifecycle store admits only the guarded transitions shown above: `100 → 102`, `102 → 202`, `202 → 100`, and any unresolved state (`100`, `102`, `202`) to a terminal status. Terminal state is immutable.
|
|
552
|
+
§worker-lifecycle-state-machine The lifecycle store admits only the guarded transitions shown above: `100 → 102`, `102 → 202`, `202 → 100`, and any unresolved state (`100`, `102`, `202`) to a terminal status. Terminal state is immutable. `DrainSupervisor` owns claim, wake, and cancellation; the dispatcher owns model-requested park/conclusion; the daemon owns boot-recovery orchestration; and the engine owns policy terminals. A racing transition that loses observes the durable winner; it does not overwrite it or report the requested state as fact.
|
|
531
553
|
|
|
532
554
|
§stream-catalog-lifecycle Streams are independently durable subscriptions owned by a worker. Payload and
|
|
533
555
|
lifecycle are orthogonal: zero bytes is a valid payload for both success and
|
|
534
556
|
failure, while the closed subscription and its status are the terminal fact.
|
|
535
|
-
Every stream entry exposes that durable state
|
|
536
|
-
`stream: { state, ... }
|
|
557
|
+
Every stream entry exposes that durable state on its catalog group's default
|
|
558
|
+
channel (`[0].stream: { state, ... }`): active streams carry `seconds`; terminal streams carry
|
|
537
559
|
their exact `status` and derive `closed` (status below 400), `killed` (499), or
|
|
538
560
|
`failed` (other failure status). An entry with no subscription has no `stream`
|
|
539
561
|
member. This is historical state, not merely a live-process hint.
|
|
540
562
|
|
|
541
|
-
|
|
542
563
|
```mermaid
|
|
543
564
|
stateDiagram-v2
|
|
544
565
|
[*] --> Open: executor registers subscription
|
|
@@ -552,7 +573,7 @@ stateDiagram-v2
|
|
|
552
573
|
Observed --> [*]
|
|
553
574
|
```
|
|
554
575
|
|
|
555
|
-
| §worker-lifecycle-subscription-matrix Subscription state at `
|
|
576
|
+
| §worker-lifecycle-subscription-matrix Subscription state at SEND signal `202` | Terminal observation already in a packet | Result |
|
|
556
577
|
|-------------------------------------------------------------------------|---:|---|
|
|
557
578
|
| open | no | park; polling or closure may wake it |
|
|
558
579
|
| closed, any status, empty or non-empty | no | continue directly to the observation turn |
|
|
@@ -579,9 +600,9 @@ sequenceDiagram
|
|
|
579
600
|
participant C as Child loop
|
|
580
601
|
participant S as Child stream
|
|
581
602
|
P->>C: WORK or FORK
|
|
582
|
-
P->>P: SEND
|
|
603
|
+
P->>P: SEND 202 parks on live child
|
|
583
604
|
C->>S: EXEC opens subscription
|
|
584
|
-
C->>C: SEND
|
|
605
|
+
C->>C: SEND 202 parks on live stream
|
|
585
606
|
loop backoff, fixed cadence, or explicit arrival
|
|
586
607
|
S-->>C: optional progress observation
|
|
587
608
|
C->>C: continue or park
|
|
@@ -602,9 +623,9 @@ sequenceDiagram
|
|
|
602
623
|
| cancelled or failed terminal | no | same wake/delivery path as success; outcome remains non-2xx |
|
|
603
624
|
|
|
604
625
|
A stream's close status and a loop's terminal status are separate layers. A
|
|
605
|
-
stream may close 4xx/5xx and wake its worker to recover. Model
|
|
606
|
-
|
|
607
|
-
`
|
|
626
|
+
stream may close 4xx/5xx and wake its worker to recover. Model SEND signals
|
|
627
|
+
`4xx/5xx` report a failed action and continue; signal `200` concludes successfully and
|
|
628
|
+
signal `499` explicitly abandons the worker. Only a concluded loop crosses the
|
|
608
629
|
parent edge as the child's result.
|
|
609
630
|
|
|
610
631
|
§worker-lifecycle-terminal-result **Terminal truth is a result, not a lifecycle code.** `loops.terminal_result`
|
|
@@ -620,7 +641,6 @@ it is the parked lifecycle state, not a terminal. No product surface may infer o
|
|
|
620
641
|
reconstruct a result from that projection. Active rows have no terminal result;
|
|
621
642
|
terminal rows must have one, and database triggers enforce both directions.
|
|
622
643
|
|
|
623
|
-
|
|
624
644
|
```mermaid
|
|
625
645
|
flowchart TD
|
|
626
646
|
W[Worker cancellation] --> L[Terminalize unresolved loops]
|
|
@@ -640,16 +660,16 @@ observe their terminal results. No effect is replayed across an unknown
|
|
|
640
660
|
boundary.
|
|
641
661
|
|
|
642
662
|
- §worker-lifecycle-single-drain **One drain advances a worker.** At most one drain is registered for a worker at any instant: a `runLoop` request or wake on a worker 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 the worker under one per-worker lock, so the teardown's re-claim cannot race a concurrent start into a double-drain. Fresh-loop sequence allocation and insertion are one mutation under that same lock; concurrent accepted prompts remain distinct ordered queue items.
|
|
643
|
-
- §worker-lifecycle-total-reap **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's `
|
|
663
|
+
- §worker-lifecycle-total-reap **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's SEND signal `499` terminalize every unresolved loop in the cancelled worker subtree and iterate each worker's durable open-subscription rows, invoking each exact callable owner from the process-local live registry. The durable rows answer *what is held*; the live registry answers *how this process tears it down*; the abort signal is a fast-path optimization. There is no implicit detachment. Before shutdown awaits drains, it cancels every process-local proposal waiter through {§proposal-cancel-aborts} with outcome `daemon_stopping`, so a stopped-world dispatch cannot hold teardown open. 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 bounded: the executor sends a polite signal then SIGKILL after a consumer-set grace (`PLURNK_SERVICE_EXEC_KILL_GRACE_MS`). A model `## KILL0 [code]` on one live stream instead delivers exactly that signal once (bare KILL uses the executor's SIGHUP default; `## KILL0 [9]` uses SIGKILL).
|
|
644
664
|
- §worker-lifecycle-exec-epoch-bound **A stream's kill binds to the scope it captured at spawn.** A stream captures the worker'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.
|
|
645
665
|
- §worker-lifecycle-no-resurrection **A cancelled worker is not resurrected by its own torn-down work.** A stream conclusion delivered to a cancelled, idle worker 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 `runLoop` request resumes the worker.
|
|
646
|
-
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** When a backgrounded stream concludes,
|
|
647
|
-
- §worker-lifecycle-child-wake **A child worker concluding wakes a parent blocked on it — the topology join.** `worker://` spawn/fork records `parent_worker_id` ({§lifecycle-terms}). When a worker's drain exits having **concluded** — no `202`-blocked loop, no open stream —
|
|
666
|
+
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** When a backgrounded stream concludes, `DrainSupervisor` routes it through the same inject seam as any loop source ({§actor-boundary-passive-wake}): an active worker folds the conclusion into its next turn; a worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. The result is never lost: a blocked loop sleeps rather than ending, and the stream's status-transition is the arrival ({§actor-boundary-passive-wake}) that wakes it; on resume it reads the concluded stream's own state, not a synthetic prompt.
|
|
667
|
+
- §worker-lifecycle-child-wake **A child worker concluding wakes a parent blocked on it — the topology join.** `worker://` spawn/fork records `parent_worker_id` ({§lifecycle-terms}). When a worker's drain exits having **concluded** — no `202`-blocked loop, no open stream — `DrainSupervisor.#onDrainExit` resumes its parent **in place** through the shared `#wakeParkedWorker`, the same 202→100 resume a stream conclusion uses. So a parent that spawns work and blocks with SEND signal `202` is woken the moment its child finishes; on resume it reads the child's deliverable from the {§worker-scheme-collect} delta in its own log — a control edge, **never an injected prompt**. The wake recurses upward via the parent's own drain-exit. A child still running — or itself blocked at 202 — is not *concluded*, so it does not wake the parent (it's still a live thing the subtree holds). This is the structured-concurrency join: streams and child workers are the same kind of "live thing a worker holds," driving premature-terminate ({§send-premature-terminate}), the wake edge, and the collect delta identically. A worker conclusion is a **bounded, un-loseable** wake: if the conclusion fires while the parent is mid-turn (before its block commits), `#wakeParkedWorker` finds it not-yet-slept and records an **owed wake**, which the drain honors when the parent blocks — so a wait awaiting workers **always returns**, never dead-blocks on a conclude-before-block race. (Only a live exec stream, unbounded absent a timeout, may legitimately hold a wait open.)
|
|
648
668
|
- §worker-optimistic-settlement **Asynchronous settlement receives one bounded worker-local opportunity before model dispatch.** An initiating turn lets only the streams it started settle before its terminal SEND; separately, a stream or direct-child conclusion persists and publishes immediately but holds the parked worker's single `202→100` requeue while another stream or direct child remains live. Both use `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`, shipped at five seconds; zero disables the opportunity. The wake hold ends as soon as no sibling obligation remains, never extends its original deadline, and coalesces every conclusion that lands within it into one requeue. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. A conclusion that lands after provider dispatch begins retains its next wake, while poll, park-deadline, prompt, and operator wakes never open this hold. Only packet/provider dispatch waits: terminal state, client events, cancellation, and child execution do not. One redaction-safe span records elapsed time, quiescence versus deadline, and conclusion count without entering the packet.
|
|
649
|
-
- §worker-lifecycle-idle-is-concluded **An idle worker concludes; it does not park.** A loop is idle only when it has neither live obligations nor completed results awaiting their first packet. A live child or stream blocks a `
|
|
669
|
+
- §worker-lifecycle-idle-is-concluded **An idle worker concludes; it does not park.** A loop is idle only when it has neither live obligations nor completed results awaiting their first packet. A live child or stream blocks a SEND signal `202` join; a completed stream, child result, or same-turn retrieval continues directly to the next packet where it is observed. Only after those sets are drained does signal `202` resolve like signal `200`. There is no held-open idle loop and no `loop/quiesced` soft signal. A concluded worker is durable working history and an addressed arrival reawakens it as a new loop.
|
|
650
670
|
- §worker-lifecycle-no-lost-loop **A loop is never stranded by a drain's exit.** A drain relinquishes its registry slot only after a lock-held re-claim confirms the queue is empty; a loop enqueued during that teardown is either re-claimed by the exiting drain or claimed by a fresh drain that a later inject starts. The relinquish and the start are serialized, so neither the lost-loop hang nor a transient double-drain can occur.
|
|
651
671
|
- §worker-lifecycle-durable-disposition **Durable disposition wins cancellation races.** At a turn boundary, the engine reads the loop's durable status before interpreting a process-local abort. A committed `202` park survives a later daemon-shutdown signal; only a loop still durably running at `102` can be terminalized by that cancellation.
|
|
652
|
-
- §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation survives only while a live child obligation remains; after reconciliation, an unblocked park requeues `202→100` and resumes in place. Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
|
|
672
|
+
- §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every pending physical provider request beneath that loop first settles as an error with absent usage and explicitly unknown cost, then its logical model call closes; recovery never fabricates zero evidence. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation survives only while a live child obligation remains; after reconciliation, an unblocked park requeues `202→100` and resumes in place. Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
|
|
653
673
|
|
|
654
674
|
---
|
|
655
675
|
|
|
@@ -661,11 +681,11 @@ Author-facing contract: [`@plurnk/plurnk-providers`](../plurnk-providers/SPEC.md
|
|
|
661
681
|
|
|
662
682
|
Three current entry points:
|
|
663
683
|
|
|
664
|
-
- §provider-surface-generate `provider.generate(args)` — once per
|
|
665
|
-
- §provider-surface-
|
|
666
|
-
- §provider-surface-
|
|
684
|
+
- §provider-surface-generate `provider.generate(args)` — once per logical model call. An emission attempt supplies the complete packet messages, worker/turn coordinates, generation envelope, optional local grammar, first-party metadata, and `callKind: "emission"`. A BARE inference supplies only one user message containing its body plus non-prompt call identity and accounting metadata, including `callKind: "bare"` ({§bare-inference} {§provider-call-kind}). Both receive a durable physical-request observer; provider-owned retry and failover may issue several ordered requests beneath either call. A successful `ProviderResponse` reaches its call-specific consumer; a `ProviderError.attempt` remains failed response evidence under {§provider-interrupted-attempt}. Core persists normalized response evidence separately from physical accounting and relays encrypted reasoning only from an admitted emission ({§encrypted-reasoning-carrier}).
|
|
685
|
+
- §provider-surface-capacity `provider.assessRequestCapacity(messages, maxOutputTokens?, signal?)` — provider-owned intersection of request-shaped token evidence and every known physical input limit. It admits, rejects only a proven exact overflow, or defers ambiguity to upstream ({§tokenomics-context-envelope-admission}). `generate` performs this assessment for its exact request and preserves the evidence on success and capacity failure.
|
|
686
|
+
- §provider-surface-prompt-measurement `provider.countPromptTokens(messages, signal)` — the cancellable complete-request measurement primitive used by provider capacity assessment, with `exact`, `upper_bound`, `estimate`, or `unavailable` provenance. Core never substitutes this physical fact for its curation ruler.
|
|
667
687
|
|
|
668
|
-
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow`
|
|
688
|
+
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow`, `maxInputTokens`, and `maxOutputTokens` carry known model limits; `outputBudget` is the total generation envelope, optional `reasoningBudget` is its strict subset, and `inputCapacity` is the stable intersection of known input constraints ({§tokenomics}). Unknown facts remain `null`. `model` identifies persisted turn/provider evidence. Local GBNF boot verification also consumes `constrainsOutput` ({§grammar-enforcement-verified-at-boot}).
|
|
669
689
|
|
|
670
690
|
§meta-passthrough **Metadata passthrough (provider → client).** `generate` may return an open `meta: Record<string, unknown>` bag. The service stores it unenforced per turn (`turns.meta`, `json_valid` only — no schema) and forwards the latest turn's blob in `loop/terminated.usage` ({§notifications}). The service never reads a field within it. Providers own their metadata shapes; monetary values carry an explicit amount and currency rather than an implied unit. Absent → `{}`. The mirror direction (client → provider, the self-identified `client` id) rides `generate({client})` ({§attribution}).
|
|
671
691
|
|
|
@@ -674,18 +694,20 @@ Three current entry points:
|
|
|
674
694
|
- `messages` is a complete prompt (the section list, pre-assembled into the system + user messages). Provider does not reorder.
|
|
675
695
|
- §provider-guarantees-signal-wired `signal` is wired to the worker's AbortController.
|
|
676
696
|
- §provider-guarantees-serial-attempts Emission attempts for one engine turn are serial. They reuse the exact messages, coordinates, generation limits, and strike state; two attempts for that turn never overlap.
|
|
697
|
+
- BARE calls admitted by one turn launch as one parallel batch; each call retains independent observer and failure state, and the engine awaits the complete batch before committing results in authored order ({§bare-inference}).
|
|
698
|
+
- §provider-guarantees-request-observer Immediately before each physical provider I/O, the provider opens its provider/model identity through `observeRequest` and settles the returned handle exactly once as response or error. Core durably records that occurrence before I/O and rejects a returned response or `ProviderError` whose ordered accounting differs from the observed records. Persistence failure is an internal contract failure, never optional telemetry.
|
|
677
699
|
- §provider-guarantees-assistantraw-opaque `assistantRaw` is opaque to the engine (forensics-only).
|
|
678
|
-
-
|
|
700
|
+
- Capacity assessment receives the exact `PacketWire` messages supplied to `generate`, its effective total-output tightening, and the loop cancellation signal. It may perform provider I/O. A curation-weight comparison never authorizes or rejects physical I/O.
|
|
679
701
|
|
|
680
702
|
### §emission-admission Provider emission admission
|
|
681
703
|
|
|
682
704
|
A completed provider exchange is an **emission attempt**, not necessarily an engine turn. The provider transports and observes the model's bytes; ANTLR is the admission authority only after provider completion. Admission asks whether the exchange has a trustworthy frame: its first parsed operation is PLAN, its last parsed operation is a terminal SEND, every hard parse error is bounded between those anchors, and no `unparsedTail` exists. Missing anchors, an error outside the frame, or a boundary-destroying tail rejects the entire exchange regardless of `finishReason`; no recovered prefix dispatches. Parser warnings remain admissible. `finish=length` is forensic evidence of likely truncation, not an independent rejection rule. A provider-declared resource interruption never reaches admission, even when its partial bytes form a complete-looking frame ({§provider-interrupted-attempt}).
|
|
683
705
|
|
|
684
|
-
Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable
|
|
706
|
+
Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable logical `model_calls` row and its emission-specific `turn_attempts` admission row. A call that ends without response evidence leaves that admission row unclassified (`accepted IS NULL`) and does not consume the emission-attempt ceiling. Beneath the model call, every provider observer invocation opens one cardinal `provider_requests` occurrence immediately before physical I/O and settles it as response or error. Adapter retries and capacity failover append requests in issue order; a response-less failure therefore remains an accounted occurrence rather than disappearing. Normalized response evidence is durable before parser classification and does not duplicate the separately owned accounting. The accepted exchange alone completes `turns.packet`; every physical request remains in turn and loop accounting, while the context gauge reads the latest settled emission request on the latest turn. Digest exposes rejected response evidence as `packetNNN.attemptNNN.rejected.*` and every physical request in its machine-readable ledger.
|
|
685
707
|
|
|
686
708
|
The first exhaustion in a consecutive sequence closes that unadmitted turn as a continue and opens exactly one ordinary recovery turn. Its packet projects the latest rejected response OPEN from a durably FOLDED model item under {§model-entry-log-curation} and carries one transient `invalid_emission` Notice whose complete message is: `Your previous response contained an unrecoverable syntax error. No operations were performed. Try again.` No parser diagnostic, attempt count, or rail state becomes model-facing. The recovery turn has its own honestly stored packet and its configured private same-packet attempts. The packet-local projection never changes the row's curation state, so no later packet repeats the malformed body unless the model explicitly OPENs it. Admission clears the recovery state; exhausting the informed turn terminates instead of opening another.
|
|
687
709
|
|
|
688
|
-
An admitted frame may contain bounded malformed statements. Parsed operations still dispatch; each malformed statement becomes one durable model-origin `error` row with the parser's exact diagnostic under {§parse-diagnostics} and status 400. These failures are committed before the terminal disposition, participate in the ordinary strike rail, and prevent SEND
|
|
710
|
+
An admitted frame may contain bounded malformed statements. Parsed operations still dispatch; each malformed statement becomes one durable model-origin `error` row with the parser's exact diagnostic under {§parse-diagnostics} and status 400. These failures are committed before the terminal disposition, participate in the ordinary strike rail, and prevent SEND signal `200` or an already-drained signal `202` from concluding before the model sees them in the next packet. This is operation recovery, not provider resampling.
|
|
689
711
|
The Problem recovery states that only the failed operation needs correction
|
|
690
712
|
because its parsed siblings were retained; the parser-owned detail states the
|
|
691
713
|
specific syntax rule.
|
|
@@ -697,26 +719,28 @@ terminates the loop at 500 without spending an engine strike.
|
|
|
697
719
|
§turn-never-blank An admitted turn whose operation fails — during parsing or
|
|
698
720
|
dispatch — is categorically different: its failed operation row enters
|
|
699
721
|
model-visible history and the next engine turn may recover. A `ProviderError`
|
|
700
|
-
means no
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
722
|
+
means no exchange was admitted (auth, exhausted transport retries, rate limit,
|
|
723
|
+
or provider-declared interruption). Core settles and retains every physical
|
|
724
|
+
request's known or unknown {§provider-request-accounting}; when the error carries
|
|
725
|
+
interrupted response evidence, Core stores it unaccepted without duplicating its
|
|
726
|
+
accounting. The failed turn still stores the exact request and never fabricates
|
|
727
|
+
an assistant or a zero-valued observation.
|
|
704
728
|
|
|
705
729
|
### §attribution Plugin-authored attribution folksonomy
|
|
706
730
|
|
|
707
731
|
A plugin may declare opaque attribution tags statically or at runtime under the
|
|
708
732
|
shared contract {§plugin-attribution}:
|
|
709
733
|
|
|
710
|
-
```
|
|
734
|
+
```jsonc
|
|
711
735
|
{ "plurnk": { "attribution": "@acme/widgets" } } // always-on string or string[]
|
|
712
736
|
```
|
|
713
737
|
|
|
714
738
|
| Stage | Contract |
|
|
715
739
|
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
716
|
-
| Collection | Immediately before each
|
|
740
|
+
| Collection | Immediately before each emission attempt, Core pulls the admitted scheme, executor, loaded mimetype-handler, and selected provider sources. A BARE call pulls only its selected provider source because it admits no other plugin capability. |
|
|
717
741
|
| Composition | Core flattens, deduplicates, and sorts the tags. The resulting non-empty array rides `generate({ attributions })`; an empty set omits that provider field. |
|
|
718
742
|
| Meaning | Core neither verifies nor infers contribution. Tags are plugin-authored folksonomy for telemetry, optimization, attribution, or downstream rules. The `@plurnk/` reservation is the only namespace policy ({§plugin-attribution}). |
|
|
719
|
-
| Request evidence | The stored request packet carries the exact set most recently forwarded for that turn. Every pre-I/O `
|
|
743
|
+
| Request evidence | The stored request packet carries the exact set most recently forwarded for that turn. Every pre-I/O `model_calls` row carries that call's exact set, including response-less failures. |
|
|
720
744
|
| Derived reporting | Turn, loop, digest, and client views project the recorded sets. `loop/terminated.attributions` is their deduplicated sorted union and remains separate from provider usage and charge evidence. |
|
|
721
745
|
|
|
722
746
|
Runtime hooks are synchronous and receive only the attempt coordinates. A hook
|
|
@@ -741,16 +765,20 @@ plugin discovery is the last protocol-extension seam.
|
|
|
741
765
|
The ANTLR grammar always defines and validates the PLURNK language. Separately,
|
|
742
766
|
an operator may configure `PLURNK_PROVIDERS_GBNF_<alias>` for a local
|
|
743
767
|
llama-server. The provider must advertise GBNF transport and satisfy a forcing
|
|
744
|
-
probe whose
|
|
745
|
-
`PLURNK-RAILS-LIVE` sentinel, or boot
|
|
768
|
+
probe whose sampled sentence and pre-projection response match the selected
|
|
769
|
+
rail's template boundary followed by the `PLURNK-RAILS-LIVE` sentinel, or boot
|
|
770
|
+
fails. The setting is resolved
|
|
746
771
|
per alias and is unset by default. Configuring it on a cloud or endpoint-managed
|
|
747
772
|
provider is an error, not a request for best-effort filtering.
|
|
773
|
+
Alias-scoped `PLURNK_PROVIDERS_GBNF_DEBUG` deliberately withholds transport, so
|
|
774
|
+
boot skips the enforcement probe while real requests retain the configured rail
|
|
775
|
+
for local syntax validation and the engine's withheld-rail verdict.
|
|
748
776
|
Runtime injection uses the provider's registered alias, falling back only to
|
|
749
777
|
the process's active alias. Suffixed rail settings with neither identity fail
|
|
750
778
|
instead of guessing. A configured package variant or explicit path that cannot
|
|
751
779
|
be loaded also fails; it never silently becomes unconstrained.
|
|
752
780
|
|
|
753
|
-
§gbnf-requires-reasoning
|
|
781
|
+
§gbnf-requires-reasoning Both shipped PLURNK rails require reasoning. The same alias-scoped configuration
|
|
754
782
|
must resolve reasoning to `adaptive` or `on`; `off` with GBNF is rejected before
|
|
755
783
|
the probe or any model generation. Reasoning-off remains valid when no GBNF rail
|
|
756
784
|
is configured.
|
|
@@ -758,7 +786,8 @@ is configured.
|
|
|
758
786
|
§rail-truth-engine-verdict **Local constraint truth is independently observed.**
|
|
759
787
|
For a configured local GBNF, the provider returns the pre-projection sentence as
|
|
760
788
|
`grammarEvidence` under `plurnk-providers` {§gbnf-response-observation}. The engine
|
|
761
|
-
requires that evidence, independently validates `grammarEvidence.input
|
|
789
|
+
requires that evidence, independently validates `grammarEvidence.input` with the
|
|
790
|
+
artifact's declared response root, and
|
|
762
791
|
stamps `railsAttached: "client"` when transported or `"withheld"` in debug mode
|
|
763
792
|
plus `railsVerdict`; it never validates projected
|
|
764
793
|
`assistant.content` as though the required reasoning enclosure were still
|
|
@@ -768,7 +797,7 @@ inside the projected reasoning prefix has no false content pointer. With no
|
|
|
768
797
|
local GBNF, core adds no rail state and makes no claim about endpoint-owned
|
|
769
798
|
settings.
|
|
770
799
|
|
|
771
|
-
```
|
|
800
|
+
```dotenv
|
|
772
801
|
PLURNK_MODEL_gemma=openai/macher.gguf
|
|
773
802
|
PLURNK_MODEL_opus=openrouter/anthropic/claude-opus-latest
|
|
774
803
|
PLURNK_MODEL=gemma
|
|
@@ -859,7 +888,7 @@ Every fact names the canonical key, never the host root or an echo of the
|
|
|
859
888
|
model's spelling. These classes let a caller distinguish a wrong address, an
|
|
860
889
|
invalid range, read-only authority, and occupied hidden state without guessing.
|
|
861
890
|
|
|
862
|
-
§fs-world-state **The world-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting world. `WorldState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, orphan-freedom
|
|
891
|
+
§fs-world-state **The world-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting world. `WorldState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, channel orphan-freedom, the closed admission set (a file row's grantor is git or a client act — or the create-accepted transient NULL the next reconcile stamps), and sig-coherence. It runs as a lifecycle-test epilogue and at every soak turn boundary, where the delta half applies: an idle turn grows the entries table by ZERO. A violation names its law and its row.
|
|
863
892
|
|
|
864
893
|
### §scheme-manifest Manifest
|
|
865
894
|
|
|
@@ -890,9 +919,11 @@ Registration precedes loop affinity:
|
|
|
890
919
|
| Registered but inactive under flag | The flag gate returns `403 scheme-unavailable`. |
|
|
891
920
|
| Registered and active | Dispatch continues to the operation owner. |
|
|
892
921
|
|
|
893
|
-
- §op-mode-phases **A continuing turn executes in MODE phases.** A model turn describes intended effects and requested observations; it is not an imperative program whose later statements can consume invisible same-turn results. The engine therefore performs four stable phases: **Mutate** (`EDIT`, `COPY`, `MOVE`, `KILL`, `FOLD`), **Observe** (`FIND`, `READ`, `OPEN`), **Do** (all remaining non-terminal actions, including `EXEC`, `WORK`, `FORK`, and directed `SEND`), then **End** (the terminal `SEND`). `PLAN` remains the turn anchor and is recorded before those phases. Authored order is preserved within each phase. A result still lands in the next packet; phasing makes that result describe settled state instead of an accidental intermediate state.
|
|
922
|
+
- §op-mode-phases **A continuing turn executes in MODE phases.** A model turn describes intended effects and requested observations; it is not an imperative program whose later statements can consume invisible same-turn results. The engine therefore performs four stable phases: **Mutate** (`EDIT`, `COPY`, `MOVE`, `KILL`, `FOLD`), **Observe** (`FIND`, `READ`, `OPEN`, `BARE`), **Do** (all remaining non-terminal actions, including `EXEC`, `WORK`, `FORK`, and directed `SEND`), then **End** (the terminal `SEND`). `PLAN` remains the turn anchor and is recorded before those phases. Authored order is preserved within each phase. A result still lands in the next packet; phasing makes that result describe settled state instead of an accidental intermediate state.
|
|
894
923
|
|
|
895
|
-
|
|
924
|
+
§bare-inference **BARE is isolated, synchronous retrieval over the durable child-provider policy.** Its body is the complete prompt and becomes the sole user message; Core supplies no PLURNK system packet, log context, tools, GBNF, parser, target, worker, or persistent child state. The selected provider is exactly the loop's WORK/FORK child provider, falling back to the parent provider when the durable policy is inherit. All BARE statements in one admitted turn receive logical model-call identities in authored order and launch concurrently under the loop cancellation signal. Core awaits the batch, isolates a provider failure to that operation, then records results and notifications in authored order regardless of completion order. Accounting or persistence failure is internal and fails hard. Each response is unseen retrieval work: the canonical disposition is `SEND[102]`, and same-turn `SEND[200]` is refused until the next packet presents it.
|
|
925
|
+
|
|
926
|
+
- §op-synchronous **Decisive operations settle before the next scheduled operation.** The dispatcher `await`s every decisive operation. Work remains in flight only when the operation's contract deliberately creates concurrency: `FORK`, `WORK`, stream-producing `EXEC`, and a streaming `READ` after its scheme-specific acquisition boundary. Such a READ first establishes its durable subscription, returns `102`, and then retains only its `StreamSubscription`; a later scheduled operation may address that live owner. MODE changes scheduling, not completion semantics. This is why a same-turn KILL followed by SEND signal `200` concludes ({§send-premature-terminate}): KILL synchronously flips the worker's live loops terminal (`engine_terminate_worker_live_loops`) before the End phase judges the pending set, while the physical scope reap rides `cancelWorker` asynchronously and invisibly.
|
|
896
927
|
|
|
897
928
|
- §edit-batch **Same-resource EDITs are one mutation.** Every EDIT targeting the same canonical resource and channel in one turn applies to the resource's one pre-turn snapshot. The scheme validates the complete batch before writing, applies disjoint replacements from the highest original coordinate downward, and commits one resulting revision atomically; reversing the statements cannot change that revision. A failing statement rejects that resource batch without a partial write; independent resource batches remain independent. Whole-resource replacement or creation cannot coexist with another EDIT in the same batch, selected regions may not overlap, and a zero-length insertion may occur at most once at each boundary. Prepend (`<0>`), append (`<-1>`), and exact equal-endpoint insertions compose with non-overlapping replacements. Proposal-gated schemes expose one proposal for the resource batch and accept all or none. The public scheme contract is batch-shaped: a scheme must never emulate this guarantee by applying individual EDITs sequentially.
|
|
898
929
|
|
|
@@ -926,14 +957,14 @@ Details state `destinationWritten: true` and identify the destination.
|
|
|
926
957
|
|
|
927
958
|
Directed SEND (non-null path) routes to scheme's `send`. Status = intent:
|
|
928
959
|
|
|
929
|
-
-
|
|
930
|
-
-
|
|
960
|
+
- `## SEND0 [200] (path)` — write body into resource (WS message, exec stdin).
|
|
961
|
+
- `## SEND0 [499] (path)` — cancel active subscription ({§stream}).
|
|
931
962
|
|
|
932
|
-
- §log-uniform-query **Log speaks the universal query contract** —
|
|
963
|
+
- §log-uniform-query **Log speaks the universal query contract** — `## FIND0 (log://…)` works like every scheme's FIND. Candidates are worker rows scoped by the coordinate hierarchy ({§log-coordinate-hierarchy}) and projected exactly as READ shows them. Content dialects use `Matcher.matchCandidates`; `~semantic` and `@graph` use the same persistent derivation artifacts and candidate rankers as entries. Broad results are one-channel catalog groups whose `[0].path` is `log:///loop/turn/seq/OP`; exact matcher results are flat locations ({§find-result-projection}). A FIND signal classifies the FIND result row and never changes this candidate set ({§log-item-tags}). Log remains the core event ledger rather than duplicating rows into `entries`; its core-private storage adapter supplies one complete channel representation to the same READ projector. That adapter is not a plugin seam and grants no protocol scheme an alternate READ path.
|
|
933
964
|
- §find-source-agnostic **The content matcher is source-agnostic** — `Matcher.matchCandidates(body, candidates, mimetypes)` applies a content matcher (regex/jsonpath/xpath/glob) to candidates from ANY source, keyed by the caller's own identity (a pathname for entries, a `loop/turn/seq` coordinate for log). The matcher never cares what table the content came from, so FIND works uniformly across schemes by construction: `EntryFind` and `Log.find` run the one shared primitive rather than re-implementing it per scheme. Log stays its own event stream, but its rows are candidates the shared matcher covers like any entry's content.
|
|
934
965
|
- §matcher-selection-signal **Matching carries navigation evidence** - a matcher is a boolean resource predicate. Internally, each selected resource carries `matches: MatchEvidence[]`, where `MatchEvidence` is `{locator?,region?}`. `locator` preserves a structural address without overloading the resource row's `path`; `region` is a complete four-coordinate `TextRegion` only when the finding maps honestly into the exact text the model can READ. Exact duplicate evidence deduplicates. Relation findings map their indexed source spans through the same readable text coordinate index. FIND alone decides whether that grouped selection projects as resource rows or flat locations ({§find-result-projection}); the engine never fabricates a region or guesses which surgical READ the model wants.
|
|
935
966
|
|
|
936
|
-
|
|
967
|
+
`## SEND0 [410] (path[#fragment])` also deletes the target entry/channel — an implemented side-effect, NOT taught to the model and with no live/demo surface. The model-facing delete idiom is KILL ({§move}).
|
|
937
968
|
|
|
938
969
|
§send-dispatch-entry-schemes-501-on-non-410 Other status codes return 501 from entry-bearing schemes by default.
|
|
939
970
|
|
|
@@ -971,7 +1002,7 @@ Engine → scheme guarantees:
|
|
|
971
1002
|
- §universal-read-composition **Exact READ has one composition.** Core resolves
|
|
972
1003
|
canonical identity and owner once, gives a data scheme its optional
|
|
973
1004
|
`prepareRepresentation({ target, pathname })` opportunity, reads the complete
|
|
974
|
-
canonical channels
|
|
1005
|
+
canonical channels, selects the authored channel, applies binary and
|
|
975
1006
|
text-coordinate rules, and finally composes that channel's durable producer
|
|
976
1007
|
result. Preparation receives neither fragment nor `lineMarker`; finite work
|
|
977
1008
|
returns `200`, while only a retained live representation may return `102`
|
|
@@ -992,9 +1023,9 @@ Engine → scheme guarantees:
|
|
|
992
1023
|
- `ctx.signal` is wired to the worker's AbortController ({§provider-guarantees-signal-wired}).
|
|
993
1024
|
- §scheme-surface-exception-500 Scheme exceptions are contract violations. Core records their complete cause in daemon diagnostics, closes the action with a generic core-owned 500 Problem, and surfaces that durable row in the next turn's `errors` section ({§operation-results}). Implementation exception text is not repurposed as a model recovery instruction.
|
|
994
1025
|
|
|
995
|
-
**
|
|
996
|
-
populates `entry_channels.
|
|
997
|
-
({§tokenomics-
|
|
1026
|
+
**Curation-weight participation.** Core's shared `_entry-crud.ts` write helper
|
|
1027
|
+
populates `entry_channels.weight` at write time through `ctx.weigh`
|
|
1028
|
+
({§tokenomics-weight-stored-at-write}). Scheme handlers reach that path through
|
|
998
1029
|
the public `ctx.entries` capability. Raw database writes are outside the scheme
|
|
999
1030
|
API and receive no implicit token accounting.
|
|
1000
1031
|
|
|
@@ -1085,10 +1116,10 @@ discovery ({§mimetype-discovery}).
|
|
|
1085
1116
|
|
|
1086
1117
|
**Token accounting.** The daemon injects no tokenizer into `Mimetypes`; content
|
|
1087
1118
|
projection is independent of packet budgeting. Core uses the stable
|
|
1088
|
-
model-independent ruler for stored/catalog weights and the model-facing budget
|
|
1119
|
+
model-independent ruler for stored/catalog weights and the model-facing curation budget
|
|
1089
1120
|
({§tokenomics-agnostic-ruler}). The provider's request-shaped measurement is
|
|
1090
|
-
confined to provider-physical
|
|
1091
|
-
({§tokenomics-
|
|
1121
|
+
confined to provider-owned physical capacity assessment
|
|
1122
|
+
({§tokenomics-context-envelope-admission}).
|
|
1092
1123
|
|
|
1093
1124
|
§persistent-search-index **Persistent search index.** `SearchIndex.maintain` is the pre-model engine pass. Each searchable resource supplies an address and the exact readable body its READ exposes. Entries supply their default body; `LogBody` resolves each log row's canonical full body from its durable tx/rx envelope. Acquisition schemes project remote source material before storing that body; search never introduces a second hidden text projection. The readable body, mimetype, resolved text/binary classification, mimetype projection identity, embedder configuration, and applicable search exclusion form a content hash. Complete artifacts own FTS, vectors, symbol definitions, and references; resource rows hold only the attachment hash. Binary, empty, and excluded derivations do not invoke handler projections and therefore use one fixed no-projection identity.
|
|
1094
1125
|
|
|
@@ -1159,7 +1190,7 @@ A published default channel renders under the entry's ordinary fragmentless addr
|
|
|
1159
1190
|
|
|
1160
1191
|
### §no-visibility Entries carry no visibility
|
|
1161
1192
|
|
|
1162
|
-
Every entry is uniformly listed in the catalog (
|
|
1193
|
+
Every entry is uniformly listed in the catalog (`## FIND0 (scheme:///**)`, {§packet}) and READable — entries have no per-worker open/folded state. Context curation is the model's, on the **log** (via OPEN/FOLD, {§open-fold}), never on entries.
|
|
1163
1194
|
|
|
1164
1195
|
### §channel-mimetype Mimetype is a (scheme, channel) property — never a default
|
|
1165
1196
|
|
|
@@ -1193,14 +1224,7 @@ Op implications:
|
|
|
1193
1224
|
|
|
1194
1225
|
Client-interface target parameters carry fragments inline (`{ target: "sh:///1/1/2#stderr" }`).
|
|
1195
1226
|
|
|
1196
|
-
**Wire rendering: default channel is path-only.**
|
|
1197
|
-
|
|
1198
|
-
```
|
|
1199
|
-
<<notes.md:...:notes.md — file scheme (bare)
|
|
1200
|
-
<<sh:///1/1/2:...:sh:///1/1/2 — exec output default (stdout)
|
|
1201
|
-
<<sh:///1/1/2#stderr:...:sh:///1/1/2#stderr — non-default
|
|
1202
|
-
<<log:///1/1/0:...:log:///1/1/0 — atomic log row
|
|
1203
|
-
```
|
|
1227
|
+
**Wire rendering: default channel is path-only.** A rendered target omits `#channel` when channel matches `defaultChannel`. Single-channel entries render path-only; multi-channel entries render the default path-only and only non-default carries `#name`.
|
|
1204
1228
|
|
|
1205
1229
|
### §channel-state Channel state — metadata, not gating
|
|
1206
1230
|
|
|
@@ -1221,17 +1245,68 @@ Model uses state to anticipate growth between turns. Clients use state for UI (s
|
|
|
1221
1245
|
|
|
1222
1246
|
Per-op semantics. AST shapes come from `@plurnk/plurnk-contracts`'s `PlurnkStatement`. Engine dispatches by `op`; scheme implements per author contract ({§scheme}).
|
|
1223
1247
|
|
|
1248
|
+
### §line-anchors Text line anchors
|
|
1249
|
+
|
|
1250
|
+
A scheme declaring `textEditScopes: true` with model write authority publishes
|
|
1251
|
+
the contracts-owned {§text-line-anchor-syntax}. For canonical model-facing
|
|
1252
|
+
resource identity `R`, one-based line ordinal `L`, configured non-negative
|
|
1253
|
+
neighbor count `C`, and ordered content array `W` containing that line and up to
|
|
1254
|
+
`C` complete lines on either side (all excluding separators), core hashes the
|
|
1255
|
+
JSON tuple `["plurnk-line-anchor-v1",R,L,C,W]` with SHA-256, interprets the
|
|
1256
|
+
digest as a big-endian integer modulo `62^5`, and encodes five fixed-width
|
|
1257
|
+
characters with alphabet `0-9A-Za-z`. The universal READ projector derives
|
|
1258
|
+
anchors from the complete canonical selected channel before applying the
|
|
1259
|
+
authored text slice; its durable result retains the canonical derivation
|
|
1260
|
+
identity and anchors aligned with returned lines. Packet rendering right-aligns
|
|
1261
|
+
`L` to the decimal width of the complete canonical selected channel's final
|
|
1262
|
+
addressable line and emits `@xxxxx L:<content>` with one or more ASCII spaces
|
|
1263
|
+
before `L`; a source line therefore retains the same prefix across projections
|
|
1264
|
+
of one revision.
|
|
1265
|
+
An explicit default-channel fragment and its fragmentless spelling share that
|
|
1266
|
+
identity; a selected non-default channel retains its canonical `#channel`.
|
|
1267
|
+
|
|
1268
|
+
For READ/LOOK and COPY/MOVE source or destination selection, core resolves every
|
|
1269
|
+
anchor against the addressed current complete content before applying the
|
|
1270
|
+
ordinary numeric text-coordinate contract. Exactly one current match lowers to
|
|
1271
|
+
its numeric line; zero or multiple matches return 409 `line-anchor-collision`,
|
|
1272
|
+
and an anchor in a column position returns 400. COPY/MOVE mutation owners retain
|
|
1273
|
+
the resolved endpoint neighborhoods as compare-and-swap preconditions. There is
|
|
1274
|
+
no revision sidecar or fuzzy relocation. A range authenticates both endpoint
|
|
1275
|
+
neighborhoods, so every line of a range up to `2C + 2` lines is covered; a
|
|
1276
|
+
longer range retains an unauthenticated interior gap. The shipped `C = 2`
|
|
1277
|
+
covers ranges through six lines.
|
|
1278
|
+
|
|
1224
1279
|
### §edit EDIT
|
|
1225
1280
|
|
|
1226
|
-
AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
|
|
1281
|
+
AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker?: TextLineMarker }`.
|
|
1227
1282
|
|
|
1228
1283
|
- Resolves target channel from fragment ({§channel-selection}); unknown channel → 400; undeclared in manifest → engine crash ({§channel-mimetype}).
|
|
1229
1284
|
- §edit-null-clears Writes the body; `body: null` clears it.
|
|
1230
1285
|
- §edit-status-201-200 Returns `{ status: 201, entryId }` for a new entry and
|
|
1231
1286
|
`{ status: 200, entryId }` for a content update.
|
|
1232
|
-
- §edit-noop-304 A write that changes nothing — identical content
|
|
1233
|
-
- §edit-tags-additive Tags from `signal[]` apply additively via `entry_tags` (scheme may vary).
|
|
1287
|
+
- §edit-noop-304 A write that changes nothing — identical content — returns `{ status: 304, entryId }`, mirroring OPEN/FOLD's idempotence ({§open-fold}). The operation's log classification remains independent ({§log-item-tags}).
|
|
1234
1288
|
- §edit-marker-required-on-existing **A markerless EDIT is CREATE-ONLY — there is no easy-clobber path on an existing entry.** A `<L>` marker scopes an EDIT to a range; without one, the body becomes the entry's WHOLE content — legitimate and required for a fresh entry (nothing exists to scope into), but on an EXISTING entry a missing marker is refused **400**, never a silent full replace. A deliberate full rewrite states that intent explicitly: `<1,-1>` resolves through the ordinary marker math to the same whole-content replacement, so the capability is available but cannot be selected by omission.
|
|
1289
|
+
- §edit-line-anchors An anchored EDIT resolves under {§line-anchors} and carries
|
|
1290
|
+
its endpoint checks as a core-private mutation precondition. Otherwise-valid
|
|
1291
|
+
zero/multiple matches and later precondition misses share {§edit-collision};
|
|
1292
|
+
malformed positions and schemes without textual EDIT scopes return 400 before
|
|
1293
|
+
handler invocation, while an upstream current-read failure preserves its
|
|
1294
|
+
status. The model-facing teaching recommends anchors for EDIT because this
|
|
1295
|
+
rejection is deliberate stale-target protection; parser support for anchors
|
|
1296
|
+
on observations does not imply the same recommendation.
|
|
1297
|
+
- §edit-collision Every standard entry EDIT lands by compare-and-swap against
|
|
1298
|
+
the exact channel content used to calculate it, including numeric-only EDITs.
|
|
1299
|
+
A concurrent creator that wins the resource identity or channel, an anchor
|
|
1300
|
+
that no longer identifies exactly one line, a selected endpoint neighborhood
|
|
1301
|
+
that changes before mutation, or a representation that changes in the final
|
|
1302
|
+
check/write gap returns the same neutral **409 `edit-collision`** and preserves
|
|
1303
|
+
the winner's content. Its public detail says only that EDIT collided with
|
|
1304
|
+
another change and directs the model to READ and retry; it does not assign
|
|
1305
|
+
fault or reveal which detection layer won. Concurrent correct workers are an
|
|
1306
|
+
ordinary cause. Core resolves anchors, scheme handlers receive only numeric
|
|
1307
|
+
coordinates, the shared entry mutation owner rechecks selected endpoint
|
|
1308
|
+
neighborhoods against its exact snapshot, and atomic identity/channel claims
|
|
1309
|
+
and storage predicates close the remaining races.
|
|
1235
1310
|
|
|
1236
1311
|
A `file:///` member EDIT diverges from this immediate-write contract: it diffs against the entry snapshot (the body channel, never a fresh disk read) and **proposes** (202) a disk write that lands via a compare-and-swap on accept. See {§membership-edit-write-cas} and the proposal lifecycle {§proposal}. The marker-required rule above applies identically here — an existing file is never markerlessly replaced.
|
|
1237
1312
|
|
|
@@ -1239,25 +1314,44 @@ A `file:///` member EDIT diverges from this immediate-write contract: it diffs a
|
|
|
1239
1314
|
|
|
1240
1315
|
AST: `{ op: "READ", target, body: null, signal: tags | null, lineMarker? }`.
|
|
1241
1316
|
|
|
1317
|
+
Matcher-bearing or path-glob READ syntax has already normalized to canonical
|
|
1318
|
+
FIND before dispatch under {§read-find-normalization}; core has no second READ
|
|
1319
|
+
selection or fan-out path.
|
|
1320
|
+
|
|
1242
1321
|
- §read-read-content Returns channel content and mimetype.
|
|
1243
1322
|
- §read-read-404 Returns 404 when the channel is absent.
|
|
1244
1323
|
- §read-selection-projection READ applies `lineMarker` as text coordinates to one
|
|
1245
1324
|
exact target under {§read-exact-target}. Markerless READ synthesizes
|
|
1246
1325
|
`<1,16>`; `<1,-1>` explicitly selects all text. Successful positional reads
|
|
1247
1326
|
carry the compact requested/returned extent and available total
|
|
1248
|
-
({§range-extent}).
|
|
1327
|
+
({§range-extent}). Anchors resolve under {§line-anchors} before selection. An
|
|
1328
|
+
invalid text region is 416.
|
|
1329
|
+
|
|
1330
|
+
§log-item-tags **Tags classify durable log items.** Under {§log-tag-signal}, FIND, READ, EDIT, COPY,
|
|
1331
|
+
and MOVE strip any leading `+` from every signal term and add the resulting tag
|
|
1332
|
+
to the one log row created for that operation. The row receives its complete
|
|
1333
|
+
deduplicated set even when the operation fails or has no body; the signal never
|
|
1334
|
+
filters candidates, changes a resource, or persists on an entry. OPEN and FOLD
|
|
1335
|
+
use every unsigned signal term as an ALL-tags filter over existing log rows;
|
|
1336
|
+
their optional target and matcher intersect that set. They then strip and add
|
|
1337
|
+
each `+tag` and strip and remove each `-tag` on the exact selected rows while
|
|
1338
|
+
applying the requested visibility. Signed terms never select: a curation
|
|
1339
|
+
operation requires a target, matcher, or unsigned tag. Add and remove terms for
|
|
1340
|
+
the same tag conflict. Successful visibility and classification changes land as
|
|
1341
|
+
one curation event whose exact per-row deltas are durable. Engine policy may
|
|
1342
|
+
apply its separately specified diagnostic classifications, such as `overflow`.
|
|
1343
|
+
Every classification lives once in `log_tags`, is erased with its row, and is
|
|
1344
|
+
copied with log history on fork.
|
|
1249
1345
|
|
|
1250
1346
|
### §open-fold OPEN / FOLD
|
|
1251
1347
|
|
|
1252
1348
|
AST: `{ op: "OPEN"|"FOLD", target, body: MatcherBody | null, signal: tags | null, lineMarker: null }`.
|
|
1253
1349
|
|
|
1254
|
-
OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surface ({§packet}). FOLD collapses a log row to its path; OPEN restores its ordinary packet projection, including any preview bound ({§body-projection}). The canonical full body remains available through READ of the log URI with `<1,-1>`. Non-destructive: rows and bodies persist. On any valid row, applying its current state again or targeting a bodyless row is a successful visibility no-op;
|
|
1350
|
+
OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surface ({§packet}). FOLD collapses a log row to its path; OPEN restores its ordinary packet projection, including any preview bound ({§body-projection}). Both select by target, matcher, and the symmetric ALL-tags filter, then apply the tag changes defined by {§log-item-tags}. The canonical full body remains available through READ of the log URI with `<1,-1>`. Non-destructive: rows and bodies persist. On any valid row, applying its current state again or targeting a bodyless row is a successful visibility no-op; classification changes still land. Malformed targets and nonexistent exact coordinates fail at their addressing boundary. Entries carry no visibility ({§no-visibility}), so OPEN/FOLD against an entry scheme returns 501.
|
|
1255
1351
|
|
|
1256
1352
|
### §jsonplurnk The Log's wire format
|
|
1257
1353
|
|
|
1258
|
-
The `## Log` section renders as a
|
|
1259
|
-
|
|
1260
|
-
§jsonplurnk-dynamic-fence The opening fence length is **dynamic**: one backtick longer than the longest backtick run in the rendered entries (floor 3). A body can carry arbitrary content — a READ of a doc whose own text opens a column-0 triple-backtick fence — which a fixed opener would let close the block early; a dynamic opener can never be closed by its own body content (CommonMark closes a fence only on a line of at least its own length), independent of the `N:` numbering that incidentally keeps text bodies off column 0.
|
|
1354
|
+
The `## Log` section renders as a fixed three-backtick `jsonplurnk` fence - a JSON array of entry objects, otherwise-valid JSON with **exactly one** deviation: an open, nonempty `body` is a raw multiline string. Its opening JSON quote is followed by a physical newline, every content line begins with a numeric `N:` or anchored `@hash N:` coordinate prefix whose separator is one or more ASCII spaces, and its closing quote appears at column zero before either the object close or a following member. Source quotes, braces, fences, and headings cannot collide with either boundary because source text never occupies column zero after projection; source backticks therefore cannot form a CommonMark closing fence. The fixed opener keeps the packet prefix stable across content changes. The carve-out is localized to `body`, so the strip-parser recognizes `"body":"` followed by a newline, consumes one or more coordinate-prefixed lines, and replaces the raw multiline value with an escaped JSON string while preserving following members to recover strict JSON. The body shape is a strict three-state invariant: `"display":"none","body":""` for no body, `"display":"folded"` with the ordinary projection withheld, and `"display":"open","body":"\n<coordinate>...\n"` with it shown. `path` is the complete model-facing log identity: when a projected operation exists it ends in `/OP`, and no separate `op` field duplicates it. Nonempty `tags` is the row's complete deduplicated, sorted folksonomy; an untagged row omits it. When an OPEN bounded projection differs from the canonical body, it appends `"chunk":"showing <selected> of <complete>"` after `body`; otherwise it omits `chunk`. Complete-line extents use inclusive two-coordinate line regions. A cut inside a line uses four-coordinate, start-inclusive and end-exclusive regions with 1-based Unicode code-point columns. The row's `path` remains the canonical READ target. The block is data only - no prose leads the fence. `tokens` is the model-facing projection of the row's curation weight: the room OPEN adds and FOLD saves. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page has a different weight. These are curation weights, not dollars. The invariants bind regardless of shape ({§packet}): addressability (`path`/`target`/`#channel`/coordinate-prefixed bodies), weighability (per-item `tokens`), honesty (every 4xx/5xx row and the exact body/display state). {§jsonplurnk} {§packet-jsonplurnk-exception}
|
|
1261
1355
|
|
|
1262
1356
|
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
1263
1357
|
|
|
@@ -1282,21 +1376,18 @@ and its internally resolved whole-line region. Exact READ retains only its
|
|
|
1282
1376
|
region. A failed retrieval's Problem owns its range extension rather than
|
|
1283
1377
|
repeating it at top level. Generic `tokens` always weighs the rendered body;
|
|
1284
1378
|
generic body `lines` remains available on READ-shaped materialization notices
|
|
1285
|
-
that have no retrieval extent. FIND content weights follow {§jsonplurnk};
|
|
1286
|
-
|
|
1379
|
+
that have no retrieval extent. FIND content weights follow {§jsonplurnk};
|
|
1380
|
+
ordinary bounded bodies expose their displayed and complete chunk extents there.
|
|
1287
1381
|
|
|
1288
1382
|
### §model-entry The model's own emission, mirrored back
|
|
1289
1383
|
|
|
1290
1384
|
§model-entry-log-curation A model-emission log row is the model's **verbatim emission**, mirrored back so it can inspect and curate its own behavior. It is an actionless artifact: `op` is null, `attrs.kind="model_emission"` identifies the row, no target exists, `tx` is empty, and the emission lives in `rx.content`, typed `text/vnd.plurnk`. Its exact address is the undecorated three-part coordinate `log:///<L>/<T>/<S>` because no operation exists to append. An admitted emission is **always born FOLDED** (budget-neutral), line-numbered like all content, and OPEN/FOLD/KILL-able like any log row. 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). The engine writes one after each admitted model turn. The invalid-emission recovery item under {§emission-admission} mirrors only the latest rejected response, is born FOLDED with `attrs.admission="rejected"`, and is projected OPEN only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
1291
1385
|
|
|
1292
|
-
- §log-coordinate-hierarchy **Log coordinates are a hierarchical prefix; the trailing slash is optional** — a coordinate is `loop/turn/sequence`, and a PARTIAL coordinate selects its descendants: `log:///1` = loop 1's rows, `log:///1/2` = turn 1/2's rows, `log:///1/2/3` = the one row. A full coordinate is always three parts, so a one- or two-part path is unambiguously a prefix — the trailing slash is an optional alias (`log:///1/2` ≡ `log:///1/2/`), uniform with
|
|
1293
|
-
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — OPEN/FOLD/KILL take a concrete coordinate or a path-glob, and a **trailing slash or a partial coordinate means "the contents"** ({§log-coordinate-hierarchy}), like a folder-scoped FIND:
|
|
1294
|
-
- §log-
|
|
1295
|
-
- §log-curation-set-selection **Set selection, never positional curation** — target/glob, optional body matcher, and (for OPEN) tag filters compose into the affected row set. FOLD's tags apply after selection. OPEN and FOLD have no `<L>` marker and never paginate that set; FIND owns result pagination.
|
|
1386
|
+
- §log-coordinate-hierarchy **Log coordinates are a hierarchical prefix; the trailing slash is optional** — a coordinate is `loop/turn/sequence`, and a PARTIAL coordinate selects its descendants: `log:///1` = loop 1's rows, `log:///1/2` = turn 1/2's rows, `log:///1/2/3` = the one row. A full coordinate is always three parts, so a one- or two-part path is unambiguously a prefix — the trailing slash is an optional alias (`log:///1/2` ≡ `log:///1/2/`), uniform with `## READ0 (worker:///docs/)`. A rendered operation row appends its canonical model-facing `/OP`, not a fourth resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive suffix is authoritative and a disagreement resolves 404. Typed entry materialization therefore resolves as `/READ` while retaining its durable `EDIT` event ({§exec-entry-sink}). `log:///1/2/*` still selects the turn's item rows, while `log:///**/READ` deliberately filters the canonical suffix.
|
|
1387
|
+
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — OPEN/FOLD/KILL take a concrete coordinate or a path-glob, and a **trailing slash or a partial coordinate means "the contents"** ({§log-coordinate-hierarchy}), like a folder-scoped FIND: `## FOLD0 (log:///1/2)` folds turn 1/2's rows. OPEN and FOLD may instead take only a tag filter ({§log-item-tags}). A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. A targetless operation without tags or a matcher is 400.
|
|
1388
|
+
- §log-curation-set-selection **Set selection, never positional curation** — target/glob, optional body matcher, and optional ALL-tags filter compose by intersection into the affected row set. OPEN and FOLD have no `<L>` marker and never paginate that set; FIND owns result pagination.
|
|
1296
1389
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
§fold-open-meta-operations **OPEN and FOLD are meta-operations — render directives, not actions.** They change how the world *displays*, never what it *is* (scrolling, not editing). A **successful** OPEN/FOLD **is recorded in the log** but **suppressed from the packet render**: the row exists for forensics — a curation act with NO trace is how a weak model folding its own task frame stayed invisible until a database dig — while the render still costs it nothing, so FOLD stays genuinely free (the original rowless design's concern, met by hide-not-drop). The emission also survives verbatim in its model-emission mirror. A **failed** OPEN/FOLD (bad target or matcher) renders normally with its status — errors are signals. The idle-turn gate reads the *emitted statements*, so a pure-curation turn is work, never idleness.
|
|
1390
|
+
§fold-open-meta-operations **OPEN and FOLD are meta-operations — log-curation directives, not world actions.** They change log visibility and classifications, never the underlying resources. A **successful** OPEN/FOLD **is recorded in the log** but **suppressed from the packet render**: the row exists for forensics — a curation act with NO trace is how a weak model folding its own task frame stayed invisible until a database dig — while the render still costs it nothing, so FOLD stays genuinely free (the original rowless design's concern, met by hide-not-drop). Its exact selected target set, each target's pre-event visibility, and the classifications actually added and removed persist with that event; `matched: N` and the authored selector are not the database's sole effect evidence. The operation row, visibility changes, tag changes, and landed effects commit in one database statement. The emission also survives verbatim in its model-emission mirror. A **failed** OPEN/FOLD (bad target, matcher, or tag signal) renders normally with its status — errors are signals. The idle-turn gate reads the *emitted statements*, so a pure-curation turn is work, never idleness.
|
|
1300
1391
|
|
|
1301
1392
|
§kill-log-receipt-suppressed **A successful KILL of a log item is suppressed from the render too — same principle, different mechanism.** KILL is a real deletion (not a meta-op), but once it has executed against a `log://` target its tombstone is *spent*: the killed row is gone, and a receipt saying "I deleted it" is no forward-actionable context. So a **successful** `KILL` whose target is a **log item** is recorded in the DB (forensics) and suppressed from the packet, while its emission survives in its model-emission mirror. Without suppression, every deleted row would create a replacement receipt, so per-row curation could not shrink the log. The suppression is **scoped to log targets**: a `KILL` of a `worker://` note, an `sh://` stream, or any stored artifact is a **world mutation**, not log housekeeping, and stays visible. A **failed** KILL (bad target, no match ≠ error but a malformed coordinate is) renders like any error.
|
|
1302
1393
|
|
|
@@ -1313,7 +1404,7 @@ secret detection.
|
|
|
1313
1404
|
| Query and authored body | Preserved exactly; they are authored content and URI identity, not structurally identifiable credential slots. |
|
|
1314
1405
|
| Parser failure | Preserves the structural diagnosis and source position without quoting request-metadata contents ({§path-request-metadata}). |
|
|
1315
1406
|
| Client, fork, packet, and digest | Consume the stored projection; none owns a second redaction policy. |
|
|
1316
|
-
|
|
|
1407
|
+
| Model-call evidence and model-emission mirror | `model_calls.response` under {§emission-admission} and the folded row under {§model-entry-log-curation} remain exact forensic evidence and are the explicit exception. |
|
|
1317
1408
|
|
|
1318
1409
|
### §copy COPY (engine-orchestrated)
|
|
1319
1410
|
|
|
@@ -1322,17 +1413,20 @@ body: ResourceSelection (destination), signal: tags | null }`.
|
|
|
1322
1413
|
|
|
1323
1414
|
1. §copy-missing-source-404 Resolve source path, channel, and optional text scope; missing resource or
|
|
1324
1415
|
channel is 404. A binary marker is not a byte channel and returns 415;
|
|
1325
|
-
readable projections are ordinary text sources under
|
|
1416
|
+
readable projections are ordinary text sources under
|
|
1417
|
+
{§membership-source-projection}. Source anchors resolve under
|
|
1418
|
+
{§line-anchors}.
|
|
1326
1419
|
2. Resolve destination path, channel, and optional text scope. Source and
|
|
1327
|
-
destination mimetypes must agree or the result is 415.
|
|
1420
|
+
destination mimetypes must agree or the result is 415. Destination anchors
|
|
1421
|
+
resolve independently under {§line-anchors}.
|
|
1328
1422
|
3. A scoped destination must already exist and is mutated through the
|
|
1329
1423
|
destination scheme's `editBatch`.
|
|
1330
1424
|
4. An unscoped destination writes only its selected channel. Existing other
|
|
1331
1425
|
channels survive.
|
|
1332
1426
|
- §copy-conflict-409 Different content in that channel is 409.
|
|
1333
|
-
- §copy-noop-304 Identical content
|
|
1334
|
-
5.
|
|
1335
|
-
|
|
1427
|
+
- §copy-noop-304 Identical content is 304.
|
|
1428
|
+
5. The signal classifies the COPY log item and never changes either resource
|
|
1429
|
+
({§log-item-tags}).
|
|
1336
1430
|
|
|
1337
1431
|
§copy-cross-scheme-copy The result is 201 for a new entry, 200 for a write, 304 for an exact no-op, or
|
|
1338
1432
|
202 when the owning scheme requires proposal review. Same- and cross-scheme
|
|
@@ -1346,8 +1440,14 @@ body: ResourceSelection (destination), signal: tags | null }`.
|
|
|
1346
1440
|
- §move-relocation-deletes-source MOVE first performs the destination mutation under {§copy}, then removes only
|
|
1347
1441
|
the selected source region or channel. A whole-channel MOVE deletes the
|
|
1348
1442
|
source entry only when that was its final channel.
|
|
1443
|
+
- §move-canonical-whole-source The canonical whole-content source scope
|
|
1444
|
+
`<1,-1>` resolves as a whole-channel selection for MOVE: it removes the
|
|
1445
|
+
selected channel and deletes the source entry when that was its final
|
|
1446
|
+
channel. Every other source scope remains regional even when it currently
|
|
1447
|
+
covers all available text; resource deletion is never inferred from extent.
|
|
1349
1448
|
- A same-channel regional MOVE applies destination insertion and source
|
|
1350
|
-
deletion in one same-snapshot `editBatch`;
|
|
1449
|
+
deletion in one same-snapshot `editBatch`; source and destination anchor
|
|
1450
|
+
preconditions compose against that snapshot, and overlapping regions are 409.
|
|
1351
1451
|
- A cross-resource destination failure leaves the source untouched. A source
|
|
1352
1452
|
failure after destination success is an explicit partial failure with
|
|
1353
1453
|
`destinationWritten: true`. Proposal acceptance/rejection follows the same
|
|
@@ -1372,16 +1472,15 @@ AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal
|
|
|
1372
1472
|
identity-bearing: `https://example.com/page` queries
|
|
1373
1473
|
`(https, /example.com/page)`, never `(https, /page)`.
|
|
1374
1474
|
- §find-glob-filter-on-content `body` matcher operates on entry content (glob/regex/jsonpath/xpath), per `plurnk.md` "Pattern Filtering"; the path-glob lives in the (target), not the body.
|
|
1375
|
-
- §find-semantic-selection Every matcher operates only over the candidate set selected by `(target)
|
|
1376
|
-
- §find-tag-filter-and-semantics `signal` is a tag filter; entries match if they have ALL listed tags.
|
|
1475
|
+
- §find-semantic-selection Every matcher operates only over the candidate set selected by `(target)`; relation matchers do not bypass that selection. Semantic ranking is exhaustive within that candidate set, then applies the ordinary FIND result scope. Markerless semantic FIND therefore uses the same `<1,16>` default as every other matcher. Integers retain FIND's positional contract: `<N>` selects result N and `<N,M>` selects the inclusive range. A leading decimal first applies a minimum cosine-similarity threshold; following integers select positions within that ranked threshold set. Thus `<0.7,10,20>` means threshold 0.7 followed by results 10 through 20, while `<0.7>` applies the threshold and the ordinary first-16 page.
|
|
1377
1476
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
1378
|
-
- §find-result-projection **The authored target shape determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount,
|
|
1477
|
+
- §find-result-projection **The authored target shape determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount, itemsWeightTotal, returnedItemsWeightTotal }`:
|
|
1379
1478
|
|
|
1380
1479
|
| Target | Matcher body | `range.unit` | Result rows |
|
|
1381
1480
|
|---|---|---|---|
|
|
1382
|
-
| exact | absent | `resource` | the one catalog
|
|
1383
|
-
| glob or folder | absent | `resource` | catalog
|
|
1384
|
-
| glob or folder | present | `resource` | matching
|
|
1481
|
+
| exact | absent | `resource` | the one catalog channel group |
|
|
1482
|
+
| glob or folder | absent | `resource` | catalog channel groups |
|
|
1483
|
+
| glob or folder | present | `resource` | matching channel groups with `matchLocationCount` on `[0]` |
|
|
1385
1484
|
| exact | present | `matchLocation` | flat `{ locator?, region? }` locations |
|
|
1386
1485
|
|
|
1387
1486
|
A glob or folder remains resource mode when it resolves to one path. An exact
|
|
@@ -1393,24 +1492,32 @@ AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal
|
|
|
1393
1492
|
|
|
1394
1493
|
Inside `FindResult`, `matchingPathCount` and `matchLocationCount` describe the
|
|
1395
1494
|
complete selection before pagination; the packet curates those facts under
|
|
1396
|
-
{§retrieval-packet-metadata}. `path` is reserved for resource identity;
|
|
1397
|
-
never nest locations, and exact location rows never repeat the
|
|
1398
|
-
A **body-less** FIND is the **catalog**.
|
|
1399
|
-
|
|
1495
|
+
{§retrieval-packet-metadata}. `path` is reserved for resource or channel identity;
|
|
1496
|
+
broad results never nest locations, and exact location rows never repeat the
|
|
1497
|
+
resource path. A **body-less** FIND is the **catalog**. Its outer result array
|
|
1498
|
+
contains one nonempty, flat channel array per resource. Element `[0]` is always
|
|
1499
|
+
the default channel and carries the bare resource path; later elements carry
|
|
1500
|
+
their complete `path#channel` addresses. Each channel is
|
|
1501
|
+
`{ path, mimetype, weight, lines, parseIssues? }`; `parseIssues` is the
|
|
1502
|
+
positive-only advisory projection of `{§mimetype-parse-issues}` for the exact
|
|
1503
|
+
body derivation under `{§scheme-catalog-parse-issues}`. Resource-level `stream` and broad-match
|
|
1504
|
+
`matchLocationCount` live only on `[0]`. A single-channel resource is therefore
|
|
1505
|
+
a one-element array, with no path-owning wrapper or duplicated channel map.
|
|
1400
1506
|
A terminal single-star path scope is a one-level map: direct entries retain
|
|
1401
|
-
that shape, while deeper first-segment directories collapse to
|
|
1402
|
-
`{ path: "dir/**", items,
|
|
1507
|
+
that shape, while deeper first-segment directories collapse to the one-element
|
|
1508
|
+
group `[{ path: "dir/**", items, weight }]`, where the selector and both aggregates
|
|
1403
1509
|
describe the exact recursive subtree. Scope summaries are navigation
|
|
1404
1510
|
metadata, not resources. Markerless FIND returns positions 1–16 in the
|
|
1405
1511
|
selected unit; `<N,M>` selects an inclusive page and `<1,-1>` explicitly
|
|
1406
1512
|
selects all. `range` reports the unit, complete result total, normalized
|
|
1407
|
-
request, and returned positions ({§range-extent}). `
|
|
1408
|
-
`
|
|
1513
|
+
request, and returned positions ({§range-extent}). `itemsWeightTotal` weighs the complete matched set while
|
|
1514
|
+
`returnedItemsWeightTotal` weighs the returned resource page; in exact
|
|
1409
1515
|
location mode both weigh the one selected resource once. Resource order is
|
|
1410
1516
|
rank for `~`semantic and candidate order otherwise; location order is dialect
|
|
1411
1517
|
order and exact duplicates deduplicate. The intended drill-down is broad FIND
|
|
1412
1518
|
to choose paths, exact-target FIND to choose locations, then exact READ.
|
|
1413
|
-
`content` uses the shared generated-JSON projection
|
|
1519
|
+
`content` uses the shared generated-JSON projection and translates only this
|
|
1520
|
+
final model-facing representation from `weight` to `tokens`
|
|
1414
1521
|
({§json-result-rendering}), so universal packet numbering makes result
|
|
1415
1522
|
ordinal N addressable as line N, matching `<N>` pagination without a second
|
|
1416
1523
|
coordinate system. Pagination is the only FIND materialization bound; no
|
|
@@ -1430,9 +1537,9 @@ AST: `{ op: "SEND", target: ParsedPath | null, body: SendBody | null, signal: nu
|
|
|
1430
1537
|
|---|---|---|---|
|
|
1431
1538
|
| **102** continue | next turn | next turn | next turn |
|
|
1432
1539
|
| **200** done | **resolved** — terminal, loop ends | **refused** — Premature-Terminate (KILL to abandon, or wait) | **refused** — forced next turn to see the result |
|
|
1433
|
-
| **202** wait | **resolves like 200
|
|
1540
|
+
| **202** wait | **resolves like 200**, unless this turn successfully FOLDed — an empty wait is satisfied, while FOLD continues into the curated next packet | **block on the join** — the loop sleeps (`<T>`/`<-1>` bound it, `<P>` polls); its work's conclusion **reawakens the same loop**, prompt intact ({§worker-lifecycle-child-wake}, {§worker-lifecycle-wake-liveness}) | resolves next turn (≈ continue) |
|
|
1434
1541
|
|
|
1435
|
-
§wait-obligation-matrix **499** gives up regardless of obligations and cancels the unresolved descendant scope — the model's one self-decided failure ({§state-terms}). The surface is small on purpose. The **one** non-obvious cell is **200 with an obligation in flight** — a contradiction (you claimed done while you owe work), which the engine holds you to via Premature-Terminate below. A child join is bounded by the child's terminal transition; an external stream may carry an explicit `<T,P>` policy.
|
|
1542
|
+
§wait-obligation-matrix **499** gives up regardless of obligations and cancels the unresolved descendant scope — the model's one self-decided failure ({§state-terms}). The surface is small on purpose. The **one** non-obvious cell is **200 with an obligation in flight** — a contradiction (you claimed done while you owe work), which the engine holds you to via Premature-Terminate below. A child join is bounded by the child's terminal transition; an external stream may carry an explicit `<T,P>` policy. A successful same-turn FOLD is synchronous housekeeping, so it does not block an explicit `200`; with `202`, it instead continues as `102` because its context effect is useful only in the curated next packet.
|
|
1436
1543
|
|
|
1437
1544
|
§loop-terminal-authorship **Terminal authorship is explicit when external.**
|
|
1438
1545
|
|
|
@@ -1453,7 +1560,7 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1453
1560
|
| Idle turn | An engine-rail error row with the corrective disposition | One strike |
|
|
1454
1561
|
| Refused disposition | The final SEND's 409 row with its exact Problem Detail | One strike |
|
|
1455
1562
|
|
|
1456
|
-
- §send-idle-turn **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 your work is done, conclude with 200
|
|
1563
|
+
- §send-idle-turn **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 your work is done, conclude with `## SEND0 [200]`. If you're waiting on a child or stream you spawned, use `## SEND0 [202]` to block on it — a 202 with nothing to wait on simply concludes."* A successful same-turn FOLD is the exception: its `202` continues without a strike so the curated packet can support the next reasoning turn.
|
|
1457
1564
|
- §send-premature-terminate **Premature terminate — the pending set.**
|
|
1458
1565
|
Completion is gated by one rule: *nothing pending may be silently
|
|
1459
1566
|
discarded*. Pending work has two states: **live obligations** (open
|
|
@@ -1466,68 +1573,76 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1466
1573
|
retrieval-only refusal. The pending kind changes the corrective message, not
|
|
1467
1574
|
rail accounting. `[499]` deliberately abandons regardless.
|
|
1468
1575
|
- §send-undelivered-child-term **Completion is not delivery.** A result becomes
|
|
1469
|
-
observed only after crossing a packet boundary. `
|
|
1576
|
+
observed only after crossing a packet boundary. SEND signal `202` parks only on
|
|
1470
1577
|
live obligations. If work has completed but is unobserved, it continues
|
|
1471
1578
|
directly to the next packet because the wake edge has already fired; only a
|
|
1472
|
-
genuinely empty set resolves immediately like `[200]`.
|
|
1473
|
-
- §send-300-choices **SEND
|
|
1579
|
+
genuinely empty set with no successful same-turn FOLD resolves immediately like `[200]`.
|
|
1580
|
+
- §send-300-choices **SEND signal `300` is an operator question - a PROPOSAL using the same stop-the-world system as file edits.** Enablement cascades: `PLURNK_QUESTIONS=0` is a servicewide ceiling; otherwise the client affirmatively requests per workspace (`settings.questions: true` at workspace creation), which ALSO injects the required `questions.md` teaching - capability and teaching gate as one ({§teaching-corpus}). An enabled workspace fails doc materialization with the read cause if that source is broken; a disabled workspace does not consume it. Enabled: the `;`-delimited body parses leniently (first segment the question, the rest choices; zero choices = an open question - never malformed), and the ask raises a proposal: dispatch stops the world, `loop/proposal` carries `{question, choices}` in attrs, and the client's accepted proposal body delivers the ANSWER - written into the ask's own model-facing rx (`{"status":200,"body":...}`), read next packet. Reject/timeout resolve through the standard {§proposal} semantics; the turn records a continue either way (never a 300 terminal), and the loop simply proceeds. Loop auto never auto-answers a question - it exists precisely to stop the world for a human, and the workspace opted in. Disabled: refused 409 with a self-decide steer, never a park into the void.
|
|
1474
1581
|
|
|
1475
1582
|
### §exec EXEC
|
|
1476
1583
|
|
|
1477
|
-
AST: `{ op: "EXEC", target (optional
|
|
1584
|
+
AST: `{ op: "EXEC", target (optional runtime-specific target), body: string | null (runtime-specific input), signal: string | null (runtime tag), lineMarker (timeout/poll) }`.
|
|
1478
1585
|
|
|
1479
|
-
§exec-target-routing Engine routes unconditionally to the `exec` scheme
|
|
1480
|
-
|
|
1586
|
+
§exec-target-routing Engine routes unconditionally to the `exec` scheme,
|
|
1587
|
+
resolves the runtime first, and enforces that runtime's required
|
|
1588
|
+
{§executor-invocation} declaration before effect admission. Core owns target
|
|
1589
|
+
realization; neither filesystem type nor body presence may invent a target role
|
|
1590
|
+
the selected runtime did not declare. With no declared directory override,
|
|
1481
1591
|
`cwd` is the workspace's `project_root`, where the File scheme writes — never
|
|
1482
|
-
the daemon's own cwd.
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
|
1489
|
-
|
|
|
1490
|
-
|
|
|
1491
|
-
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1592
|
+
the daemon's own cwd.
|
|
1593
|
+
|
|
1594
|
+
| Declared target kind | Authored target | Canonical effect target | Executor realization |
|
|
1595
|
+
| -------------------- | --------------------------------------- | ----------------------- | --------------------------------------------------------- |
|
|
1596
|
+
| Omitted | Any present target | — | Refuse 400 before admission. |
|
|
1597
|
+
| `literal` | Any target | Complete authored string | Preserve that exact string; perform no stat or scheme read. |
|
|
1598
|
+
| `path` | Local or `file://` path | Local path | Pass the path directly. |
|
|
1599
|
+
| `path` | Non-file address | — | Refuse 400 before admission. |
|
|
1600
|
+
| `resource` | Local or `file://` path | Local path | Pass the path directly. |
|
|
1601
|
+
| `resource` | Non-file data-scheme address | Complete authored address | Resolve one exact READ after acceptance and pass its temporary local file. |
|
|
1602
|
+
|
|
1603
|
+
A local directory becomes `cwd` with an absent executor target only when the
|
|
1604
|
+
runtime declaration explicitly sets `target.directory: "cwd"`; otherwise it
|
|
1605
|
+
remains the target. Core stats only for that declared rule. `ENOENT` remains a
|
|
1606
|
+
target so the runtime reports its own not-found. Any other stat failure stops
|
|
1607
|
+
before effect admission with a core-owned 500 Problem whose bounded diagnostic
|
|
1608
|
+
states the occurrence-specific cause while daemon diagnostics retain the
|
|
1609
|
+
complete error.
|
|
1610
|
+
|
|
1611
|
+
Body and target requirements come from the same runtime declaration. A runtime
|
|
1612
|
+
with no target declaration refuses a target; required body or target fields are
|
|
1613
|
+
enforced independently; every EXEC requires at least one of them; and an
|
|
1614
|
+
`exclusive` declaration refuses an invocation containing both. A target retains
|
|
1615
|
+
its one declared role whether the body is empty or non-empty. Runtime selection,
|
|
1616
|
+
target validation, and body/target relation failures therefore occur before
|
|
1617
|
+
effect classification or proposal creation.
|
|
1618
|
+
|
|
1619
|
+
§exec-source-temporary A non-file `resource` target is materialized into one
|
|
1620
|
+
core-owned temporary file after acceptance. Core reparses the complete authored
|
|
1621
|
+
address and resolves one exact `<1,-1>` READ through
|
|
1622
|
+
{§universal-read-composition}; internal source consumption never borrows the
|
|
1623
|
+
model-facing 16-line preview. The file lives through the executor run and core
|
|
1624
|
+
removes it after the subscription's terminal result has settled. A removal
|
|
1625
|
+
failure is reported to daemon diagnostics with its complete cause; it cannot
|
|
1626
|
+
rewrite the execution result, stream state, or completion wake.
|
|
1627
|
+
|
|
1628
|
+
Loop-flag authority follows the selected runtime's declaration:
|
|
1629
|
+
|
|
1630
|
+
| Target realization | Schemes that must be active |
|
|
1631
|
+
| ------------------------------------------------------- | -------------------------------------- |
|
|
1632
|
+
| Absent, `literal`, local `path`, or local `resource` | `exec` |
|
|
1633
|
+
| Non-file `resource` | `exec` and the addressed source scheme |
|
|
1634
|
+
|
|
1520
1635
|
Worker and runtime-stream authorities, query, fragment, request metadata, and
|
|
1521
|
-
every other
|
|
1522
|
-
failed source READ is preserved as the proposal-application
|
|
1523
|
-
successful READ with no string representation is refused 422;
|
|
1524
|
-
|
|
1525
|
-
command.
|
|
1636
|
+
every other component of a `resource` address retain their owning READ
|
|
1637
|
+
semantics. A failed source READ is preserved as the proposal-application
|
|
1638
|
+
failure. A successful READ with no string representation is refused 422; an
|
|
1639
|
+
empty string remains a present representation and is materialized faithfully.
|
|
1526
1640
|
|
|
1527
|
-
Core calls `effect()` once against
|
|
1528
|
-
stores the resulting fact with the invocation, and reuses it
|
|
1529
|
-
proposal policy, application, stream registration, and
|
|
1530
|
-
policy. The post-acceptance materialization path never
|
|
1641
|
+
Core calls `effect()` once against the canonical target shown above, without
|
|
1642
|
+
body text, stores the resulting fact with the invocation, and reuses it
|
|
1643
|
+
unchanged for proposal policy, application, stream registration, and
|
|
1644
|
+
effect-qualified hold policy. The post-acceptance materialization path never
|
|
1645
|
+
triggers reclassification.
|
|
1531
1646
|
|
|
1532
1647
|
§exec-registry-resolves The runtime slot (`signal`) selects an executor,
|
|
1533
1648
|
resolved against the boot-time `ExecutorRegistry`: siblings are discovered and
|
|
@@ -1538,7 +1653,7 @@ put a complete command in bare `EXEC`; they are never reinterpreted as shell
|
|
|
1538
1653
|
command words. An unavailable runtime is also 501 and carries the probe
|
|
1539
1654
|
`detail`.
|
|
1540
1655
|
|
|
1541
|
-
Per-tool programs such as `go`, `cargo`, `make`, and `npm` do not earn executor tags merely because they are executables; they are complete shell commands
|
|
1656
|
+
Per-tool programs such as `go`, `cargo`, `make`, and `npm` do not earn executor tags merely because they are executables; they are complete shell commands under `## EXEC0` or `## EXEC0 [sh]`. Registered tags exist only for tools that own a distinct body, target, or output contract. {§exec-registry-resolves}
|
|
1542
1657
|
|
|
1543
1658
|
**Timeout and poll — `<T,P>` on the `<L>` slot (grammar 0.74.20).** EXEC
|
|
1544
1659
|
repurposes the line-marker slot as `<timeout, poll>` in **seconds**, the same
|
|
@@ -1554,7 +1669,7 @@ before the turn's own spawns, so it never survives into the subsequent turn;
|
|
|
1554
1669
|
its terminal output surfaces born-OPEN like any close ({§exec-stream}).
|
|
1555
1670
|
|
|
1556
1671
|
§exec-poll `P` (`mark[1]`) is the **poll cadence**, stored on the subscription.
|
|
1557
|
-
While the loop is blocked on a `
|
|
1672
|
+
While the loop is blocked on a SEND signal `202` wait for that stream, the daemon arms
|
|
1558
1673
|
a per-worker timer for the tightest open poll cadence and resumes the blocked
|
|
1559
1674
|
loop every P seconds, floored by `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS` so it cannot tick
|
|
1560
1675
|
faster than the optimistic settlement scale, to inspect progress. It does **nothing while the
|
|
@@ -1603,9 +1718,9 @@ observation is born OPEN; a terminal state with no newly publishable body still
|
|
|
1603
1718
|
produces one conclusion row. Every READ then obeys {§body-projection} and
|
|
1604
1719
|
therefore renders its selected result complete. A stream that closes before a
|
|
1605
1720
|
same-turn wait remains pending until this terminal READ crosses the next packet
|
|
1606
|
-
boundary. The EXEC row separately records the
|
|
1721
|
+
boundary. The EXEC row separately records the authored invocation.
|
|
1607
1722
|
|
|
1608
|
-
|
|
1723
|
+
`## KILL0 (<runtime>:///<loop>/<turn>/<seq>)` cancels an active subprocess via
|
|
1609
1724
|
the subscription registry's stored controller. A terminal stream is immutable:
|
|
1610
1725
|
499 returns 410 (already killed), every other terminal status returns an RFC
|
|
1611
1726
|
9457 409 Problem carrying `terminalStatus`, and an unknown address returns 404.
|
|
@@ -1613,8 +1728,9 @@ The runtime scheme participates in the durable lookup; a completed `sh:///`
|
|
|
1613
1728
|
stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
1614
1729
|
|
|
1615
1730
|
§exec-env-scoped **Scoped environment.** An EXEC subprocess inherits the *project's* environment — its `.env`, the standard shell vars — so the model's commands run as the project expects; but never plurnk's own secrets: the provider API keys and `PLURNK_*` config are stripped before the spawn, so a model-executed command can't `printenv` the engine's keys. The service owns the scoping policy (the denylist); the executor spawns with the env it is handed.
|
|
1616
|
-
|
|
1617
|
-
- §exec-
|
|
1731
|
+
|
|
1732
|
+
- §exec-hold-until-concluded **The turn-hold exception** — for runtimes in `PLURNK_SERVICE_EXEC_HOLD` (a decision-table env, shipped listing the search family), an in-flight stream **pauses the cycle**: the next packet does not assemble until the stream concludes, so the model never burns a turn asking "are we there yet" about a result the engine controls end-to-end. This exception is limited to seconds-bounded runtimes whose final result the engine controls end-to-end. Bounded by `PLURNK_SERVICE_EXEC_HOLD_MS` and **fail-open**: at the cap the standard cycle resumes untouched (waits, wakes, polls). Zero grammar or teaching surface — the model emits EXEC followed by SEND signal `102` as ever; the wake-shaped world simply arrives one packet sooner. It extends selected runtimes beyond the ordinary {§worker-optimistic-settlement} cap before the next packet assembles. A bare entry holds ALL of a runtime's spawns; a `<runtime>:<effect>` suffix (`github:read`) holds only that effect-class — an MCP server is one runtime whose tools split (a `read` `get_issue` is instant; a `host` `run_migration` is a slow mutation), so an operator opts the known-fast read-class in without parking on the mutation. Conservative stays default: an arbitrary third-party server's latency never parks the engine unless a suffix opts a class in.
|
|
1733
|
+
- §exec-entry-sink **The entry() sink** — an executor may *request* entry materialization (execs SPEC §2.6: every sink is a consumer-implemented callback; the executor owns zero substrate). The service implements it in exec dispatch: `entry(path, content: string | null, {tags, mimetype?})` upserts the entry, then journals ONE typed `EDIT` row in the reserved `plurnk` worker's log — the fs-fiction pattern, `source` = the calling worker, `weight` = the canonical resulting span's curation weight, and `attrs.kind="entry_materialized"`. The requested tags classify that journal row under {§log-item-tags}; they never become resource metadata or duplicate into attrs. Durable replay and clients retain that exact creation event. The model packet projects the typed event as a folded system `READ` of the resulting ordinary resource: its relevant truth is readable state now available in the environment, not an agent-authored mutation. **The executor owns no fetcher:** a `content: null` is a *declaration* — the service acquires the page through schemes-http's checked WebFetcher and accepts its model-facing body and available source/evidence channels {§html-materialization}. Generic public HTML follows the same origin-Markdown, configured Tavily, and local-projection routes as exact HTTP acquisition. A failed acquisition, body-production failure, materialization exception, or absent final projection rejects the sink and produces no HTTP entry, but does not invalidate a search runtime's upstream discovery row; materialization exceptions retain their cause in daemon diagnostics. A non-null `content` is the materialize-given-body path (the caller already holds the bytes and states their mimetype) and grants no provider authority. **No page body ever rides a packet**; the announcement is the folded row's path, weight, and log classifications, and the model READs/~queries what it chooses. Parallel `entry()` calls serialize on a per-spawn chain; a rejected call leaves the chain healthy. The spawn tail settles that complete chain before unregistering, so executor idleness and shutdown are barriers over its materialization writes. The narration context (one plurnk-worker turn) is lazy per spawn, not per entry.
|
|
1618
1734
|
|
|
1619
1735
|
### §proposal The proposal lifecycle
|
|
1620
1736
|
|
|
@@ -1630,7 +1746,7 @@ stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
|
1630
1746
|
|
|
1631
1747
|
| decision | state | `status_rx` | default outcome | effect |
|
|
1632
1748
|
|---------------------------------|---|---|---|---|
|
|
1633
|
-
| §proposal-accept-applies accept | `resolved` | 200 | — | runs the scheme's **`applyResolution`** — the real side effect (disk write, exec spawn). A failing apply (≥400) downgrades to reject, carrying the apply's own outcome — e.g. a member EDIT's `
|
|
1749
|
+
| §proposal-accept-applies accept | `resolved` | 200 | — | runs the scheme's **`applyResolution`** — the real side effect (disk write, exec spawn). A failing apply (≥400) downgrades to reject, carrying the apply's own outcome — e.g. a member EDIT's `edit_collision` from its write-back compare-and-swap ({§membership-edit-write-cas}) — or `apply_failed` when it names none. |
|
|
1634
1750
|
| §proposal-reject-fails reject | `failed` | 400 | `rejected` | none — the action did not occur. |
|
|
1635
1751
|
| §proposal-cancel-aborts cancel | `cancelled` | 499 | `loop_aborted` | none — the loop is abandoning. |
|
|
1636
1752
|
|
|
@@ -1678,7 +1794,7 @@ Thus `auto` wins the otherwise nonsensical `auto + noProposals` combination, but
|
|
|
1678
1794
|
|
|
1679
1795
|
### §subscriptions Subscriptions
|
|
1680
1796
|
|
|
1681
|
-
§subscriptions-subscription-registry-routes-cancellation READ on a streaming scheme is a subscription, not a one-shot. The scheme establishes its protocol-specific acquisition boundary, returns `102 Processing`, and stays alive through the `StreamSubscription` returned by `subscriptions.open()`. The service commits that initial operation result normally; later chunk and terminal work cannot rewrite it. Durable terminal truth lives on the subscription and its channels. The service records durable subscription identity and metadata in SQLite and retains the callable `SubscriptionHandle` only in its process-local live registry. `
|
|
1797
|
+
§subscriptions-subscription-registry-routes-cancellation READ on a streaming scheme is a subscription, not a one-shot. The scheme establishes its protocol-specific acquisition boundary, returns `102 Processing`, and stays alive through the `StreamSubscription` returned by `subscriptions.open()`. The service commits that initial operation result normally; later chunk and terminal work cannot rewrite it. Durable terminal truth lives on the subscription and its channels. The service records durable subscription identity and metadata in SQLite and retains the callable `SubscriptionHandle` only in its process-local live registry. SEND signal `499`, worker cancellation, turn-scoped reap, and shutdown all route through that one live registry; no handler-specific cancellation hook or database access is part of the plugin contract.
|
|
1682
1798
|
|
|
1683
1799
|
The durable row is lifecycle evidence and the lookup key, not a serialized callback. `subscriptions.open()` establishes both halves before yielding a composed `StreamSubscription`: an `AbortSignal` whose fused `notifyChunk` and terminal `close` methods are safe to retain without the operation's general `SchemeCtx`. `close(result, summary?)` validates and persists the exact universal operation result, settles channel state, closes the durable row, wakes the worker when appropriate, and unregisters the live handle. `close_status` is a constrained relational projection of `close_result.status`, never an independent result. A durable open row without a live handle is an explicit lifecycle failure, never a fabricated cancellation success. Channel state ({§channel-state}) + log entries ({§no-chunk-rows}) carry lifecycle.
|
|
1684
1800
|
|
|
@@ -1691,7 +1807,7 @@ pretends to reconstruct an opaque plugin connection.
|
|
|
1691
1807
|
|
|
1692
1808
|
### §chunk-accumulation Chunk accumulation
|
|
1693
1809
|
|
|
1694
|
-
§chunk-accumulation-chunks-accumulate SSE event types, WS message types, exec stdout/stderr each map to a named channel. Each stored channel carries `content`, `mimetype`, `
|
|
1810
|
+
§chunk-accumulation-chunks-accumulate SSE event types, WS message types, exec stdout/stderr each map to a named channel. Each stored channel carries `content`, `mimetype`, curation `weight`, and lifecycle `state` ({§channel-state}). The subscription registry owns durable subscription identity and process-local cancellation routing, not a second channel-state representation. Chunks accumulate into the channel as they arrive — not buffered until close.
|
|
1695
1811
|
|
|
1696
1812
|
### §no-chunk-rows No per-chunk log rows
|
|
1697
1813
|
|
|
@@ -1701,12 +1817,13 @@ Model sees lifecycle events in the `log` section per turn.
|
|
|
1701
1817
|
|
|
1702
1818
|
### §deep-slices Deep slices on demand
|
|
1703
1819
|
|
|
1704
|
-
|
|
1820
|
+
`## READ0 (https://feed.example/x#body) <N-M>` pulls a slice into a log row when the model wants a specific line-range of an SSE stream.
|
|
1705
1821
|
|
|
1706
|
-
### §stream-control
|
|
1822
|
+
### §stream-control Stream control and writes
|
|
1707
1823
|
|
|
1708
|
-
- **Cancel:**
|
|
1709
|
-
- **
|
|
1824
|
+
- **Cancel:** `## SEND0 [499] (https://feed.example/x)` — the service invokes the handle registered by `subscriptions.open()` and aborts the composed subscription signal.
|
|
1825
|
+
- **WebSocket write:** `## EDIT0 (wss://feed/x)` or `## SEND0 [200] (wss://feed/x)` with a body sends one whole text frame through the active owner. SEND can follow the opening READ in the same turn; EDIT runs before READ ({§op-mode-phases}) and therefore addresses an owner already open at turn start.
|
|
1826
|
+
- **Other stream write:** `## SEND0 [200] (…)` remains scheme-defined, including exec stdin.
|
|
1710
1827
|
|
|
1711
1828
|
### §stream-constraints Engine constraints
|
|
1712
1829
|
|
|
@@ -1745,16 +1862,18 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
1745
1862
|
### §sql-ts-boundary SQL/TS responsibility boundary
|
|
1746
1863
|
|
|
1747
1864
|
**Lives in SQL:**
|
|
1865
|
+
|
|
1748
1866
|
- Render queries — log assembly + the manifest catalog.
|
|
1749
1867
|
- Cross-scope path collision (CHECK/trigger → 409).
|
|
1750
|
-
-
|
|
1868
|
+
- Logical model-call identity plus cardinal physical provider-request lifecycle and immutable settlement constraints.
|
|
1751
1869
|
- Sequence number issuance (1-based per grammar).
|
|
1752
1870
|
- Entry-vs-log integrity.
|
|
1753
1871
|
|
|
1754
1872
|
**Lives in TS:**
|
|
1873
|
+
|
|
1755
1874
|
- Status-bubble rules (`turn.status` → `loop.status` → `worker.status` → `workspace.status`). Engine UPDATEs explicitly; CHECK constraints enforce; triggers fight branching state machines.
|
|
1756
1875
|
- Tokenization (provider-bound; hot-swap re-tokenizes per {§tokenomics}).
|
|
1757
|
-
- Provider dispatch
|
|
1876
|
+
- Provider dispatch, request-accounting validation, and exact-decimal aggregate projection through the shared contracts-owned path.
|
|
1758
1877
|
- Scheme-handler invocation (connections, subprocesses, fetch).
|
|
1759
1878
|
- Plugin loading ({§plugin-discovery}).
|
|
1760
1879
|
- Stream AbortController lifecycle.
|
|
@@ -1838,7 +1957,7 @@ scheme. External schemes are discovered through
|
|
|
1838
1957
|
dispatcher contract.
|
|
1839
1958
|
|
|
1840
1959
|
The executor registry discovers installed runtimes, probes availability, and
|
|
1841
|
-
routes
|
|
1960
|
+
routes `## EXEC0 [<runtime>]`; core contributes orchestration and the output-scheme
|
|
1842
1961
|
adapter, not runtime implementations. Optional and third-party leaves extend
|
|
1843
1962
|
each family by installation and discovery; they never require a framework or
|
|
1844
1963
|
service manifest edit.
|
|
@@ -1860,8 +1979,8 @@ service manifest edit.
|
|
|
1860
1979
|
|
|
1861
1980
|
- Channel state (`static`/`active`/`closed`/`errored`) — persisted channel metadata owned by core and exposed through the schemes capability contract ({§channel-state}).
|
|
1862
1981
|
- Backpressure caps — none ({§stream-constraints}).
|
|
1863
|
-
- Stream cancel — `
|
|
1864
|
-
- Delete — `KILL` (entry-KILL, the canonical delete, {§move}); `
|
|
1982
|
+
- Stream cancel — SEND signal `499` ({§stream-control}).
|
|
1983
|
+
- Delete — `KILL` (entry-KILL, the canonical delete, {§move}); SEND signal `410` also deletes as a side-effect ({§send-dispatch}).
|
|
1865
1984
|
- §loop-flags-effective-read Per-loop flags — `loops.flags` persists a partial JSON object; every runtime policy read expands it over contracts-owned `DEFAULT_LOOP_FLAGS` and validates the complete `LoopFlags` before use. Missing rows or invalid values fail with the owning loop coordinate and cause. Raw archival copies and forensic rendering do not interpret policy.
|
|
1866
1985
|
- Default-channel wire rendering — {§channel-selection}.
|
|
1867
1986
|
|
|
@@ -1896,10 +2015,13 @@ Model selection: separate alias cascade in `ProviderRegistry` ({§provider-insta
|
|
|
1896
2015
|
| `PLURNK_SERVICE_MAX_TURNS` | `-1` | Operator turn **ceiling** — `-1` = no cap; a positive value clamps `runLoop({maxTurns})`. The effective value is persisted on the durable loop and counts cumulatively across every `202` park/resume. |
|
|
1897
2016
|
| `PLURNK_SERVICE_MAX_COMMANDS` | `-1` | Per-emission action ceiling; `-1` = no cap (default) — every generated op dispatches. A positive value caps dispatched actions: overflow ops drop with one durable `max-commands-exceeded` error row on the next packet. PLAN and the final disposition always dispatch. Tightened per workspace via `settings.maxCommands` (min wins). |
|
|
1898
2017
|
| §operator-config-loop-timeout `PLURNK_SERVICE_LOOP_TIMEOUT` | `86400000` | ms wall-clock budget for a single core loop: 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}). |
|
|
1899
|
-
| `PLURNK_SERVICE_MAX_STRIKES` | `
|
|
2018
|
+
| `PLURNK_SERVICE_MAX_STRIKES` | `6` | Consecutive admitted-turn strike threshold ({§engine-rails}). |
|
|
1900
2019
|
| `PLURNK_SERVICE_EMISSION_ATTEMPTS` | `3` | Completed provider responses allowed beneath one engine turn before an untrustworthy model-turn frame exhausts admission. Bounded interior operation errors are admitted and do not spend this budget. Consecutive exhaustion after the one informed recovery turn terminates independently of strikes. |
|
|
1901
2020
|
| `PLURNK_SERVICE_PREVIEW_LINES` | `16` | Maximum lines in an ordinary bounded log-body projection ({§body-projection}). |
|
|
1902
|
-
| `PLURNK_SERVICE_PREVIEW_CHARS` | `2560` | Maximum
|
|
2021
|
+
| `PLURNK_SERVICE_PREVIEW_CHARS` | `2560` | Maximum Unicode code points in an ordinary bounded log-body projection, with CRLF treated as one indivisible separator; independently contains single-line bodies ({§body-projection}). |
|
|
2022
|
+
| `PLURNK_SERVICE_PROMPT_PROJECTION` | `25%` | Aggregate curation-weight share of the provider-derived input capacity available to automatic prompt-body projection ({§prompt-projection}); alias-scoped overrides are supported. |
|
|
2023
|
+
| `PLURNK_SERVICE_LINE_ANCHOR_CONTEXT_LINES` | `2` | Complete neighboring lines hashed on each side of a model-facing line anchor ({§line-anchors}). |
|
|
2024
|
+
| `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES` | `2` | Surrounding and landed lines shown at each EDIT result boundary ({§edit-result-receipt-projection}). |
|
|
1903
2025
|
| `PLURNK_SERVICE_MIN_CYCLES` | `3` | Min repetitions before cycle detection fires ({§engine-rails}). |
|
|
1904
2026
|
| `PLURNK_SERVICE_MAX_CYCLE_PERIOD` | `4` | Max period length cycle detection examines ({§engine-rails}). |
|
|
1905
2027
|
| `PLURNK_SERVICE_REQUIEM_MAX_TOKENS` | `16384` | Initial forensic witness output allowance ({§digest-requiem}). |
|
|
@@ -1911,6 +2033,7 @@ Model selection: separate alias cascade in `ProviderRegistry` ({§provider-insta
|
|
|
1911
2033
|
Every core knob listed is enforced at its owning read site; `.env.defaults` is the authoritative default ({§operator-config-env-defaults}). Provider, scheme, executor, mimetype, and client-interface knobs are documented by their owning packages and appear in the assembled catalog.
|
|
1912
2034
|
|
|
1913
2035
|
**Two override semantics — ceiling vs default.** Which kind a var is determines what "override" means across the cascade:
|
|
2036
|
+
|
|
1914
2037
|
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-workspace constraint, not a per-call seam argument. `PLURNK_SERVICE_GIT_ALLOWED` ({§operator-config-git-ceiling}), `PLURNK_SERVICE_MAX_COMMANDS`, `PLURNK_SERVICE_MAX_STRIKES`, 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.
|
|
1915
2038
|
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `runLoop({alias})` request overrides it) and the config-time vars (`HOST` / `PORT` / `DB_PATH`).
|
|
1916
2039
|
|
|
@@ -1927,14 +2050,14 @@ template both ways: every `PLURNK_SERVICE_*` the service reads has a
|
|
|
1927
2050
|
declared `PLURNK_SERVICE_*` is read. A half-landed rename therefore fails a test
|
|
1928
2051
|
instead of a user's boot, and a dead knob cannot ship.
|
|
1929
2052
|
|
|
1930
|
-
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture: semantic search enabled, complete catalog orientation, automatic Git membership when the operator ceiling permits Git, and ambient operator-file docs/packet notes cleared. Configuration with a narrower or variable owner stays outside it:
|
|
2053
|
+
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to the safe default model plus gate-wide service posture: Turboderp, semantic search enabled, complete catalog orientation, automatic Git membership when the operator ceiling permits Git, and ambient operator-file docs/packet notes cleared. Configuration with a narrower or variable owner stays outside it:
|
|
1931
2054
|
|
|
1932
2055
|
| Owner | Configuration |
|
|
1933
2056
|
|---|---|
|
|
1934
|
-
| `.env.test` |
|
|
2057
|
+
| `.env.test` | Safe default model selection and universal real-model gate posture; no alias declarations, routes, secrets, model tuning, or cost/sandbox ceilings. |
|
|
1935
2058
|
| Live/demo scripts | The repository personality path and runner topology. |
|
|
1936
2059
|
| Benchlets | Their snapshotted policy, workspace restrictions, and task-specific exceptions; direct env wins over the profile. |
|
|
1937
|
-
| Operator env/shell | Model selection, provider capability such as GBNF, endpoints, credentials, tuning, and deliberate ceiling overrides. |
|
|
2060
|
+
| Operator env/shell | Model alias declarations and explicit selection overrides, provider capability such as GBNF, endpoints, credentials, tuning, and deliberate ceiling overrides. |
|
|
1938
2061
|
| `test/setup.ts` | Mock-only alias, envelope, resource, storage, and isolation fixtures; unit/integration never consume the real-model profile. |
|
|
1939
2062
|
|
|
1940
2063
|
The profile does not repeat `NODE_OPTIONS`: runner selection belongs to the invoking command, and a process-global Node option would leak into the daemon and its children. Hard ceilings such as max turns, max commands, and Git denial remain operator-owned; harnesses bound paid experiments through their per-call contract and never widen a configured ceiling here.
|
|
@@ -1947,14 +2070,13 @@ before assembled package defaults fill unset values:
|
|
|
1947
2070
|
| Configuration family | Zero-pin treatment |
|
|
1948
2071
|
|-----------------------------------------------------------|--------------------|
|
|
1949
2072
|
| Any `PLURNK_PROVIDERS_CONTEXT_WINDOW` | Remove |
|
|
1950
|
-
| Alias-specific
|
|
1951
|
-
| Any `PLURNK_SERVICE_PROMPT_BUDGET` | Remove |
|
|
2073
|
+
| Alias-specific output and reasoning budgets | Remove |
|
|
1952
2074
|
| Model selection, routes, and credentials | Retain |
|
|
1953
|
-
| Bare shipped
|
|
2075
|
+
| Bare shipped generation-envelope defaults | Retain |
|
|
1954
2076
|
| Unrelated environment | Retain |
|
|
1955
2077
|
|
|
1956
2078
|
The floor reports every removed key. A gate that succeeds only with those pins
|
|
1957
|
-
is red because provider capacity
|
|
2079
|
+
is red because provider capacity did not derive for
|
|
1958
2080
|
a fresh-user configuration.
|
|
1959
2081
|
|
|
1960
2082
|
§operator-config-max-turns-ceiling 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.
|
|
@@ -1979,10 +2101,12 @@ The composition families remain distinct so one setting's semantics never
|
|
|
1979
2101
|
leak into another.
|
|
1980
2102
|
|
|
1981
2103
|
*Defaults — explicit-wins (the client replaces/merges freely):*
|
|
2104
|
+
|
|
1982
2105
|
- §operator-config-workspace-files-items `settings.filesItems` (number) **replaces** `PLURNK_SERVICE_FILES_ITEMS` for the workspace: a one-shot opens clean (`0`, no preview), with ordinary markerless pages (`-1`), or with the file list explicitly capped (`N`, other surveys remain markerless). A single scalar — the client value wins outright.
|
|
1983
2106
|
- §operator-config-workspace-md-docs `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 before I/O (a deliberate shadow), so the unselected operator path is not read; every selected non-empty operator path is required, and absence or another read failure rejects materialization with its cause. The client sends content (it owns the file), not a path.
|
|
1984
2107
|
|
|
1985
2108
|
*Ceilings — most-restrictive-wins (the client may only narrow, never widen):*
|
|
2109
|
+
|
|
1986
2110
|
- §operator-config-workspace-max-commands `settings.maxCommands` (number)
|
|
1987
2111
|
**min()s** the `PLURNK_SERVICE_MAX_COMMANDS` per-emission cap for the
|
|
1988
2112
|
workspace: a client tightens the runaway-op guard and never raises it past
|
|
@@ -2040,6 +2164,22 @@ recovers durable lifecycle, and starts modules in registration order. Shutdown
|
|
|
2040
2164
|
closes started and self-closing modules in reverse order and surfaces aggregated
|
|
2041
2165
|
close failures.
|
|
2042
2166
|
|
|
2167
|
+
§module-shutdown-order `Daemon.stop()` returns only after active worker drains,
|
|
2168
|
+
streaming producers, derivations, mimetypes, schemes, and every accepted
|
|
2169
|
+
conclusion-wake task have settled in dependency order. The supervisor owns each
|
|
2170
|
+
asynchronous wake task from acceptance through settlement; a task failure
|
|
2171
|
+
participates in the shutdown aggregate. The database may be released only after
|
|
2172
|
+
the final wake barrier resolves.
|
|
2173
|
+
|
|
2174
|
+
```mermaid
|
|
2175
|
+
flowchart LR
|
|
2176
|
+
stop[Stop accepting work] --> drains[Settle worker drains]
|
|
2177
|
+
drains --> producers[Settle streaming producers]
|
|
2178
|
+
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
2179
|
+
resources --> wakes[Settle conclusion wakes]
|
|
2180
|
+
wakes --> database[Release database]
|
|
2181
|
+
```
|
|
2182
|
+
|
|
2043
2183
|
| Setup function | Contract |
|
|
2044
2184
|
|------------------------------------------------------------------------|----------|
|
|
2045
2185
|
| `registerRuntime({ decl, executor, availability, scheme? })` | Admits one canonical tag under {§executor-runtime-declaration}, then adds its executor and optional claimed scheme facet atomically. |
|
|
@@ -2063,7 +2203,7 @@ Its function names are transport-neutral library calls, not public wire names.
|
|
|
2063
2203
|
| Client observation | `look({ workspaceId, workerId, statement })` | Runs an already-parsed READ through the full resolver without a log row. A non-READ statement is rejected ({§op-look}). |
|
|
2064
2204
|
| §methods-log-read Reads | `readLog({ workspaceId, workerId, ...coordinate })` | Ownership-checks the worker, then reads by ids, recency, or the complete `loopSeq`/`turnSeq`/`sequence` display coordinate. `limit` defaults to 100 and is capped at 1000. |
|
|
2065
2205
|
| §methods-entry-read Reads | `readEntry({ workspaceId, workerId, target, channel?, offset? })` | Resolves the selector from that worker's perspective and returns {§entry-read-result}, either complete or as one channel suffix, without creating action evidence. |
|
|
2066
|
-
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective `
|
|
2206
|
+
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
|
|
2067
2207
|
| Client capabilities | `listClientDisplayCapabilities()` | Composes sorted scheme declarations ({§manifest-client-display}) followed by sorted MIME declarations ({§mimetype-client-display}) into the validated shared wire ({§client-display-capabilities}). The internal `exec` operation handler is excluded; its addressable runtime-tag scheme faces remain included. |
|
|
2068
2208
|
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings?, constraints? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope, materializes current docs and constraints, starts derivation warming, and emits global `workspace/created`. `projectRoot` is established here or the workspace remains headless. |
|
|
2069
2209
|
| §methods-workspace-attach Workspace lifecycle | `attachWorkspace({ workspaceId, workerId?, workerName? })` | Validates ownership and returns a client envelope for an existing world. It does not retain caller or transport binding state in core. |
|
|
@@ -2130,13 +2270,13 @@ loop with a conflicting selection fails before work is accepted. Provider
|
|
|
2130
2270
|
instances are cached; no resume path substitutes a boot default for missing or
|
|
2131
2271
|
malformed durable selection.
|
|
2132
2272
|
|
|
2133
|
-
§methods-loop-run-child-provider **Child-provider selection is
|
|
2134
|
-
policy.** Optional `childModel` (client-resolved `<provider>/<model>`, wins) or
|
|
2135
|
-
`childAlias` selects the provider for every WORK/FORK descendant; omitted uses
|
|
2273
|
+
§methods-loop-run-child-provider **Child-provider selection is one durable
|
|
2274
|
+
subcall policy.** Optional `childModel` (client-resolved `<provider>/<model>`, wins) or
|
|
2275
|
+
`childAlias` selects the provider for every WORK/FORK descendant and BARE inference; omitted uses
|
|
2136
2276
|
`PLURNK_MODEL_CHILD`, while explicit `childAlias: null` means inherit. Core
|
|
2137
2277
|
persists the resolved policy on each loop. A child runs on that provider and
|
|
2138
2278
|
carries the same policy deeper; inherit uses the spawning loop's provider and
|
|
2139
|
-
remains inherit. Packet admission is unchanged: a smaller WORK is valid when
|
|
2279
|
+
remains inherit. BARE consumes the selection without spawning a child. Packet admission is unchanged: a smaller WORK is valid when
|
|
2140
2280
|
its packet fits, and an oversized inherited FORK terminates through the ordinary
|
|
2141
2281
|
child-loop result without preflight assembly or provider fallback.
|
|
2142
2282
|
|
|
@@ -2147,21 +2287,23 @@ coordinate without fetching all rows and matching locally.
|
|
|
2147
2287
|
|
|
2148
2288
|
§methods-log-entry-wire **Log entry wire fidelity.** `readLog` and `log/entry`
|
|
2149
2289
|
preserve causal `source` and parse the row's JSON `attrs` into structured data;
|
|
2150
|
-
|
|
2290
|
+
they also project the row's complete sorted `tags` classification. Client
|
|
2291
|
+
interfaces do not reconstruct these fields from operation or origin.
|
|
2151
2292
|
|
|
2152
2293
|
§op-look **LOOK ownership.** A client-interface module owns the public LOOK
|
|
2153
2294
|
spelling and grammar parsing. It rewrites a valid LOOK statement to READ and
|
|
2154
2295
|
hands the AST to core's `look`; core owns the full resolver and the no-log
|
|
2155
2296
|
invariant. The internal closed, rowless observation segment supplies an honest
|
|
2156
2297
|
numeric loop coordinate for relative `log:///` addressing without leaving
|
|
2157
|
-
active lifecycle behind.
|
|
2298
|
+
active lifecycle behind. LOOK text anchors resolve through the same
|
|
2299
|
+
{§line-anchors} path as READ.
|
|
2158
2300
|
|
|
2159
2301
|
### §notifications Core events
|
|
2160
2302
|
|
|
2161
2303
|
| Event | Payload | When fired |
|
|
2162
2304
|
|--------------------------------------------------------------|---------|------------|
|
|
2163
2305
|
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A `log_entries` row is committed. |
|
|
2164
|
-
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage, attributions }` | One loop reaches a terminal state. `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from
|
|
2306
|
+
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, curationWeight, curationBudget, contextTokens, contextCapacity, meta }, attributions }` | One loop reaches a terminal state. `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `accounting` is the loop's contracts-owned {§provider-accounting}; the two curation facts and two physical-context facts follow {§tokenomics-client-gauge}; `meta` is that turn's opaque provider bag. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from accounting. Worker and loop are an inseparable owning coordinate. |
|
|
2165
2307
|
| §notifications-loop-proposal `loop/proposal` | contracts-owned `ProposalProjection` | Dispatch pauses on a durable 202 proposal. `disposition` is the sole authority for whether a client presents review UI; live and reconnect share {§proposal-projection}. |
|
|
2166
2308
|
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
2167
2309
|
| §notifications-workspace-branch-batch `workspace/branch-batch` | Branch-batch lifecycle payload | A branch batch enters queued, running, completed, failed, or recovery-required state. |
|
|
@@ -2235,12 +2377,12 @@ Conditional absence never reorders the surviving default sections.
|
|
|
2235
2377
|
| Order | Slot | Section | Wire contract |
|
|
2236
2378
|
|------:|:-------|:----------------------|:--------------|
|
|
2237
2379
|
| 1 | system | `definition` | Framework definition; leads the most stable prefix. |
|
|
2238
|
-
| 2 | system | `
|
|
2239
|
-
| 3 | system | `
|
|
2240
|
-
| 4 | system | `
|
|
2241
|
-
| 5 | system | `
|
|
2242
|
-
| 6 | system | `
|
|
2243
|
-
| 7 | system | `
|
|
2380
|
+
| 2 | system | `system-policy` | Operator policy; empty content is omitted on the wire. |
|
|
2381
|
+
| 3 | system | `project-policy` | Project policy; empty content is omitted on the wire. |
|
|
2382
|
+
| 4 | system | `tools` | Executable capability sheet for this loop. |
|
|
2383
|
+
| 5 | system | `optional-operations` | Present only when optional operations are enabled. |
|
|
2384
|
+
| 6 | system | `schemes` | Active resource catalogue. |
|
|
2385
|
+
| 7 | system | `inject` | Present only when operator notes are configured. |
|
|
2244
2386
|
| 8 | user | `log` | Append-mostly model-visible history. |
|
|
2245
2387
|
| 9 | user | `child-streams` | Per-turn status; empty content is omitted. |
|
|
2246
2388
|
| 10 | user | `child-workers` | Per-turn status; empty content is omitted. |
|
|
@@ -2251,8 +2393,8 @@ Conditional absence never reorders the surviving default sections.
|
|
|
2251
2393
|
| 15 | user | `prompt` | Current prompt-entry pointers. |
|
|
2252
2394
|
|
|
2253
2395
|
The order favors prefix-cache locality where semantics permit: the definition
|
|
2254
|
-
|
|
2255
|
-
volatile user-status clump. It does **not** claim that every system byte is
|
|
2396
|
+
and privileged policy lead loop-dependent capabilities, while the append-mostly
|
|
2397
|
+
log leads the volatile user-status clump. It does **not** claim that every system byte is
|
|
2256
2398
|
immutable or that the complete packet is globally monotone in volatility:
|
|
2257
2399
|
capabilities, operator notes, and policies can change. Trust is a separate
|
|
2258
2400
|
admission rule. The system slot contains trusted control-plane material;
|
|
@@ -2274,53 +2416,40 @@ trust gate; an external client action cannot invoke it. Whole-list transformatio
|
|
|
2274
2416
|
the fork-avoidance valve for alternate packet shapes ({§ecosystem}), while
|
|
2275
2417
|
grinding and folding remain closed engine concerns.
|
|
2276
2418
|
|
|
2277
|
-
### §tokenomics Tokenomics: four
|
|
2419
|
+
### §tokenomics Tokenomics: four facts, one curation ruler
|
|
2278
2420
|
|
|
2279
2421
|
Token accounting distinguishes the artifact being measured, the unit, and the
|
|
2280
2422
|
time of measurement.
|
|
2281
2423
|
|
|
2282
|
-
|
|
|
2283
|
-
|
|
2284
|
-
|
|
|
2285
|
-
|
|
|
2286
|
-
|
|
|
2287
|
-
|
|
|
2288
|
-
|
|
2289
|
-
- §tokenomics-
|
|
2290
|
-
- §tokenomics-render-weight-budget **
|
|
2291
|
-
- §tokenomics-context-percent **
|
|
2292
|
-
- §tokenomics-window-partition **
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
leaves the prompt uncapped and omits denominator-dependent gauge telemetry. A
|
|
2304
|
-
configured virtual ceiling still bounds PLURNK's packet without pretending
|
|
2305
|
-
to describe provider physics. An over-policy recovery candidate cannot be
|
|
2306
|
-
physically authorized without a known provider window and output envelope.
|
|
2307
|
-
|
|
2308
|
-
§tokenomics-client-gauge **The client gauge pairs current occupancy with its
|
|
2309
|
-
effective ceiling.** A model switch changes both latest-turn values together;
|
|
2310
|
-
the loop-total usage fields remain billing evidence, not gauge inputs.
|
|
2311
|
-
|
|
2312
|
-
| Surface | `contextTokens` | `promptBudget` |
|
|
2313
|
-
| -------------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
2314
|
-
| `loop/terminated.usage` | Latest provider attempt on the latest turn; `0` when none completed | Latest turn's enforced packet allowance; `null` when uncapped or unknown |
|
|
2315
|
-
| `providers.list` alias row | not present | Active alias's current enforced packet allowance; `null` when not established |
|
|
2424
|
+
| Fact | Owner and unit | Time | Contract |
|
|
2425
|
+
|:-----|:---------------|:-----|:---------|
|
|
2426
|
+
| Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies, and rendered packet slots | Write/build | Stable, model-independent pressure and OPEN/FOLD savings; never a tokenizer claim. |
|
|
2427
|
+
| §tokenomics-context-envelope-admission Provider input capacity | Provider model limits and configured total output envelope, in provider tokens | Before every logical request | `min(maxInputTokens, contextWindow - outputBudget)` over the known terms. The provider alone measures the complete request and admits, defers, or rejects it. |
|
|
2428
|
+
| Provider generation envelope | Provider total output budget and optional reasoning subset, in provider tokens | Before every logical request | One total output budget includes hidden reasoning. A reasoning budget is a strict subset, never an additive reserve. |
|
|
2429
|
+
| Provider usage and cost | Provider-reported input/output/cache/reasoning tokens and monetary evidence | After every physical request | Durable physical-request forensics under {§provider-usage}; never curation state or a preflight estimate. |
|
|
2430
|
+
|
|
2431
|
+
- §tokenomics-weight-stored-at-write **Curation weight, stored at write.** `entry_channels.weight` weighs the complete channel content. `log_entries.weight` weighs the complete canonical `LogBody` content before coordinate and packet presentation; persistence `tx`/`rx` envelopes contribute nothing merely by existing, and proposal settlement recomputes the value when the canonical result changes. Bodyless rows therefore weigh zero. The stored number is a stable content-depth measurement, not a provider-token prediction.
|
|
2432
|
+
- §tokenomics-render-weight-budget **Packet curation budget.** The Curation headline measures the *assembled packet* after section transforms and readout substitution. Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and displayed weight equals the stored request weight. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
|
|
2433
|
+
- §tokenomics-context-percent **Curation percent.** The headline carries packet weight as a percentage of its curation budget beside the absolutes. It reads the capacity already resolved by the provider; no extra provider call.
|
|
2434
|
+
- §tokenomics-window-partition **One capacity derivation; no service-side token budget.** The provider owns model limits and the configured total output envelope. Its resolved `inputCapacity` is the numeric curation-budget calibration as well as the physical denominator exposed to clients. That reuse is policy, not a unit conversion: Core compares stable curation weight with it only to shape context, while provider request-shaped evidence alone admits or rejects I/O. `PLURNK_SERVICE_PROMPT_BUDGET`, `PLURNK_SERVICE_SAFETY`, and the additive reasoning/completion reserve knobs are retired; local and custom deployments tune context window, total output budget, optional reasoning subset, and prompt-projection percentage at their owning layers.
|
|
2435
|
+
- §tokenomics-prompt-projection-share **Prompt projection is stable packet policy.**
|
|
2436
|
+
`PLURNK_SERVICE_PROMPT_PROJECTION` is a required alias-scoped percentage in
|
|
2437
|
+
`(0, 100)`. It allocates that share of the provider-derived curation budget to the
|
|
2438
|
+
aggregate automatic prompt-body projection. It does not bound stored prompt
|
|
2439
|
+
size, provider capacity, an explicit READ/FIND result, or the complete packet.
|
|
2440
|
+
Basing the share on the stable ceiling rather than current free weight keeps
|
|
2441
|
+
one prompt's projection byte-stable as the worker log evolves.
|
|
2442
|
+
- §tokenomics-window-unpollable-deliberate **Unknown provider capacity stays unknown.** When the provider cannot derive `inputCapacity`, Core omits denominator-dependent curation telemetry and uses the ordinary bounded prompt projection. The provider still sends requests whose measurement or limits are estimates or unavailable: ambiguity defers to the upstream capacity oracle rather than becoming a local rejection.
|
|
2443
|
+
|
|
2444
|
+
§tokenomics-client-gauge **Clients receive curation and physical occupancy as separate pairs.** `loop/terminated.usage` carries latest-turn `curationWeight`/`curationBudget` and latest-emission-call `contextTokens`/`contextCapacity`; each unknown fact is `null`. Both physical facts bind to that same call: a preflight rejection may report capacity while its absent physical request leaves `contextTokens=null`, never borrowed from an earlier call. Clients never divide provider-reported physical tokens by Core curation weight. `providers.list` exposes each instantiated alias's `inputCapacity`. A model switch replaces the latest-turn facts together; aggregate provider accounting remains cardinal monetary evidence, not a gauge input.
|
|
2316
2445
|
|
|
2317
2446
|
- **Derivation is eager and exhaustive.** Workspace creation and searchable-resource changes start one coalesced warm. The first model turn joins that warm; later turns derive intervening changes before dispatch. No model operation observes partial graph or vector coverage. A semantic query ranks every eligible candidate in scope, so lexical overlap never gates vector recall. With no embedder, readable-content FTS is the explicit keyword fallback. Progress notices make the wait visible; latency is never hidden by partial semantics. {§derivation-exhaustive}
|
|
2318
2447
|
- §membership-binary-sniff **Binary truth beats the label; no entry dominates the corpus.** A tracked member whose HEAD bytes contain NUL enters {§membership-source-projection} as `application/octet-stream` **regardless of what extension-based detection claims**; byte-level evidence outranks a default label. Every eligible text is tiled losslessly to the embedder window and every tile is embedded before its derivation attaches; semantic ranking max-pools the best chunk per candidate.
|
|
2319
|
-
- §tokenomics-agnostic-ruler **One model-agnostic ruler.** The daemon runs workers on different models in one workspace concurrently, while catalog and log accounting are workspace-wide.
|
|
2320
|
-
- §tokenomics-neutral-telemetry **
|
|
2321
|
-
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as
|
|
2322
|
-
- §tokenomics-provider-usage **Provider
|
|
2323
|
-
- §tokenomics-
|
|
2448
|
+
- §tokenomics-agnostic-ruler **One model-agnostic curation ruler.** The daemon runs workers on different models in one workspace concurrently, while catalog and log accounting are workspace-wide. `contentWeight = ceil(chars/2)` therefore gives one content one stable number without per-model workspace state or recount passes. It controls curation only; every provider call independently measures the complete request as well as it can.
|
|
2449
|
+
- §tokenomics-neutral-telemetry **Curation telemetry is state, with one pressure alarm.** The model-facing Curation section ordinarily has one line: budget, current packet weight and percentage, and free weight. When free weight is negative, exactly one second line directs the model to FOLD or KILL less-relevant log items. Per-entry weights remain on log rows where they describe OPEN cost and FOLD savings. Packet-level composition, rankings, and physical token speculation are absent.
|
|
2450
|
+
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as stable content identity. `weight` is stored beside that content and is never keyed or recomputed by model.
|
|
2451
|
+
- §tokenomics-provider-usage **Provider accounting is physical-request evidence, not curation state.** Every issued physical request has one durable pre-I/O `provider_requests` identity and settles once as response or error. Each record preserves conventional {§provider-usage} quantities and required {§provider-cost} evidence; an unreported quantity remains absent, including on response-less failures, and is never replaced by zero. `model_calls` own logical response/failure evidence, `turn_attempts` specialize emission admission, and `provider_requests` are the sole durable accounting representation. Emissions, BARE calls, rejected responses, retries, failovers, and errors therefore remain cardinal and ordered. Turn, loop, worker, workspace, digest, and protocol accounting are derived from those records through the shared {§provider-accounting} projection; only emission calls contribute the latest-packet context gauge. The baseline stores no floating-point money, denormalized totals, or rollup triggers. A documented direct charge becomes `charged`; otherwise the provider may compute an exact-decimal USD `estimated` amount from complete usage and the exact model's Models.dev rates; insufficient evidence becomes `unknown`. Derived `costUsd` is an exact decimal string only when every request is expressible in USD and is `null` otherwise. This is operational request accounting, not invoice reconciliation. Output and reasoning are quantities the model cannot FOLD, so they never alter the model-facing Budget ledger.
|
|
2452
|
+
- §tokenomics-negative-pressure **Negative curation pressure is honest and nonterminal.** The readout describes the packet the model actually receives: weight and percent may exceed the curation budget, and free weight equals `budget - weight` without flooring. Crossing the budget records {§grinder-overflow-problem}, then the grinder ({§grinder}) folds only the newest boundary. Remaining curation debt never decides physical admission, creates a strike, or establishes a one-turn quota.
|
|
2324
2453
|
|
|
2325
2454
|
### §membership Workspace identity, membership, disk co-location
|
|
2326
2455
|
|
|
@@ -2361,7 +2490,7 @@ query is the absolute identity ({§scheme-address-network}); the sanitized
|
|
|
2361
2490
|
readable projection is the fragmentless default, while faithful DOM, origin
|
|
2362
2491
|
media type, and projection identity remain explicit auxiliary evidence. A
|
|
2363
2492
|
normal
|
|
2364
|
-
|
|
2493
|
+
`## READ0 (https://host/path?query)` therefore publishes only the sanitized body
|
|
2365
2494
|
under that exact URL—never raw HTML, response headers, or a channel-selection
|
|
2366
2495
|
lesson. FIND and embeddings consume the same stored readable
|
|
2367
2496
|
projection and never re-fetch each match. Because the search family is in
|
|
@@ -2369,7 +2498,7 @@ projection and never re-fetch each match. Because the search family is in
|
|
|
2369
2498
|
({§exec-hold-until-concluded}), so the next packet contains final
|
|
2370
2499
|
materialization verdicts and folded ambient rows for every acquired page.
|
|
2371
2500
|
|
|
2372
|
-
§search-gate Coverage protects the composition at distinct seams: HTTP unit tests pin fragmentless-body publication and explicit auxiliary selection; integration tests pin search→materialize→FIND and persistence/publication separation. Model web demos run the live composition end to end — real SearXNG, real pages; stubbed acquisition is confined to unit and integration seams and never appears in a demo. A live positive-control demo requires a materialized HTTPS body and a substantive answer from a real sanitized page. Live discovery demos remain diagnostic and may expose model judgment failures without weakening these assertions. **The search gates** are rail-family accounting — in-memory per-loop state cleaned at the same seam as strikes, restart-drop accepted (a post-restart duplicate re-fetches; the TTL makes it cheap): an IDENTICAL duplicate (same runtime +
|
|
2501
|
+
§search-gate Coverage protects the composition at distinct seams: HTTP unit tests pin fragmentless-body publication and explicit auxiliary selection; integration tests pin search→materialize→FIND and persistence/publication separation. Model web demos run the live composition end to end — real SearXNG, real pages; stubbed acquisition is confined to unit and integration seams and never appears in a demo. A live positive-control demo requires a materialized HTTPS body and a substantive answer from a real sanitized page. Live discovery demos remain diagnostic and may expose model judgment failures without weakening these assertions. **The search gates** are rail-family accounting — in-memory per-loop state cleaned at the same seam as strikes, restart-drop accepted (a post-restart duplicate re-fetches; the TTL makes it cheap): an IDENTICAL duplicate (same runtime + query in one loop) **strikes and serves** — status 409 (the strike rail counts the turn failure) carrying the prior ranked digest re-read live from the original exec entry, no re-fetch, no provenance prose; the per-turn CAP (`PLURNK_SERVICE_SEARCH_MAX_PER_TURN`) is flood control — 429 with a legible steer, nothing served.
|
|
2373
2502
|
|
|
2374
2503
|
**Git is the substrate and the repository is the boundary:**
|
|
2375
2504
|
|
|
@@ -2393,9 +2522,11 @@ materialization verdicts and folded ambient rows for every acquired page.
|
|
|
2393
2522
|
and directs the operator back to the default. The isomorphic untracked scan
|
|
2394
2523
|
remains differential-gated against native
|
|
2395
2524
|
`ls-files --others --exclude-standard`.
|
|
2396
|
-
- §membership-edit-membership-gate **Membership-gated edits.** EDIT is bounded by membership exactly as READ is. An existing **member**'s baseline is its entry snapshot — the body channel the model READ, not a fresh disk read — so the diff is naive against the view the model saw, never empty (the write-side CAS, {§membership-edit-write-cas}, prevents the silent overwrite of out-of-band drift). An existing **non-member** is refused (403) *before* any read or write: the model never reads a file it can't see (no leak into the proposal) and never overwrites one (no wiping a gitignored `.env` it never added). A **new path** stays open — proposal→accept adds it to the manifest. Reaching past membership is
|
|
2525
|
+
- §membership-edit-membership-gate **Membership-gated edits.** EDIT is bounded by membership exactly as READ is. An existing **member**'s baseline is its entry snapshot — the body channel the model READ, not a fresh disk read — so the diff is naive against the view the model saw, never empty (the write-side CAS, {§membership-edit-write-cas}, prevents the silent overwrite of out-of-band drift). An existing **non-member** is refused (403) *before* any read or write: the model never reads a file it can't see (no leak into the proposal) and never overwrites one (no wiping a gitignored `.env` it never added). A **new path** stays open — proposal→accept adds it to the manifest. Reaching past membership is `## EXEC0 [sh]`'s job, not the file scheme's.
|
|
2526
|
+
- §membership-create-parents **Parent-complete creation.** An accepted File creation—whether authored as EDIT or as a COPY/MOVE destination—recursively creates missing parent directories before writing and registering the new member.
|
|
2397
2527
|
|
|
2398
2528
|
**The overlay — `pick | view | hide`, removed by `drop`.** A `workspace_constraints` table is the client's supersede over Git. Resolved membership is `(project repository files ∪ pick) − hide`, with `view` enforced at the edit gate.
|
|
2529
|
+
|
|
2399
2530
|
- §membership-auto-add **Auto-add** — the project repository's membership is its tracked `ls-files` plus untracked-but-not-ignored files (`git ls-files --others --exclude-standard`), with `git` origin. A model-created file is a member the moment it exists—no `git add`—while `.gitignore` still filters it.
|
|
2400
2531
|
- §membership-overlay-pick **`pick`** — admit an untracked file git misses: a targeted client-dictated `node:fs` glob scan over untracked matches (files only), 'constraint' origin, reconciled like git members. Enumerated, so the manifest stays exhaustive. git-absent, `pick` is the *sole* membership source.
|
|
2401
2532
|
- §membership-overlay-hide **`hide`** — exclude a tracked file: resolution drops matches (`node:path.matchesGlob`) and reconciles so the entry set *equals* the member set. The lever to exclude a committed-but-sensitive tracked file; `entries.membership_origin` keeps reconciliation off model-created members.
|
|
@@ -2417,6 +2548,12 @@ identity, and terminal disposition without exposing raw bytes or a base64 lane.
|
|
|
2417
2548
|
|
|
2418
2549
|
§derivation-dedup-parallel **The index dedups then parallelizes.** The derivation identity hashes the exact READ body, mimetype, reader behavior, embedding configuration, and applicable search exclusion. A resource attaches the immutable artifact only after it is complete; identical entry and log bodies therefore share one FTS row, one symbol graph, and one vector set without copying. Distinct artifacts run with bounded producer concurrency (`PLURNK_SERVICE_DERIVE_CONCURRENCY`). Pending artifacts sort by readable content length before entering that pool, so small resources start first while every outlier still derives fully. Unset uses a host-relative square-root fan-out; a positive integer is an exact operator budget and `-1` claims every core. Token-count and embedding batches retain only a pool-sized promise window; graph persistence writes at most `PLURNK_SERVICE_DERIVE_STORE_BATCH` definitions or references per SQLite statement. Every resource completed by a successful pass attaches a terminal classified artifact, identically at concurrency 1 and N. Multi-item warming reports aggregate milestones and heartbeat notices according to `PLURNK_SERVICE_DERIVE_PROGRESS_STEPS` and `PLURNK_SERVICE_DERIVE_PROGRESS_HEARTBEAT_MS`.
|
|
2419
2550
|
|
|
2551
|
+
The artifact also retains a positive `{§mimetype-parse-issues}` count when the
|
|
2552
|
+
exact parsed body reported one. It remains advisory alongside a normally
|
|
2553
|
+
completed semantic disposition; zero and unavailable evidence persist as
|
|
2554
|
+
absence. Catalog projection attaches it only to that parsed body channel, never
|
|
2555
|
+
to sibling channels whose content the artifact does not describe.
|
|
2556
|
+
|
|
2420
2557
|
Every completed artifact records one terminal disposition: `vector`, `lexical`
|
|
2421
2558
|
(only no embedder or an operator size ceiling), `excluded` (the configured
|
|
2422
2559
|
search-exclusion table), `nonsemantic` (empty/binary/no embedding content), or
|
|
@@ -2439,7 +2576,7 @@ Lossless chunk admission requires either the embedder's own counter or an exact
|
|
|
2439
2576
|
|
|
2440
2577
|
§membership-emi-divergence-signal **EMI divergence signal.** The detector that gates the work *is* the one that fires this — one mechanism, not a second full read. When the change-detect finds a member moved out-of-band, the delta detector ({§env-delta}) surfaces it as a system `EDIT` log row naming the file, `source="file"` — the model sees what changed without diffing the manifest against memory. The model's own edits are write-through (the entry equals disk after a File write), so the scan never mis-attributes them as external divergence.
|
|
2441
2578
|
|
|
2442
|
-
§membership-edit-write-cas **The write-back is a compare-and-swap — never a clobber, never a clever merge.** EDIT is *naive against the editable text snapshot*: it diffs the model's change onto the entry's body channel — the exact Unicode the model READ — and the proposal carries the `synced_sig` that snapshot was taken at. Binary sources are refused before this path ({§membership-source-projection}). At accept, `applyResolution` re-stats disk and lands the proposed content only if that signature still matches. If disk moved out-of-band in the propose→accept window — a sibling worker, the user's editor, a build step — the write is **refused** with
|
|
2579
|
+
§membership-edit-write-cas **The write-back is a compare-and-swap — never a clobber, never a clever merge.** EDIT is *naive against the editable text snapshot*: it diffs the model's change onto the entry's body channel — the exact Unicode the model READ — and the proposal carries the `synced_sig` that snapshot was taken at. Binary sources are refused before this path ({§membership-source-projection}). At accept, `applyResolution` re-stats disk and lands the proposed content only if that signature still matches. If disk moved out-of-band in the propose→accept window — a sibling worker, the user's editor, a build step — the write is **refused** with the same neutral `edit-collision` as {§edit-collision}, and **nothing is written**. The engine neither blind-writes over the ambient change (a *clobber*) nor silently re-diffs the model's edit against a state it never saw (getting *clever*) — both would bury a stale-view contract violation under a fallback. The collision surfaces instead: a ≥400 apply downgrades to a reject ({§proposal}), so the model sees that EDIT **did not occur** (400; the `edit_collision` outcome is forensics-only), the next reconcile narrates the real disk content as a `source=file` divergence ({§membership-emi-divergence-signal}), and the model re-reads and re-proposes against the fresh snapshot.
|
|
2443
2580
|
|
|
2444
2581
|
The version travels *with the proposal*, never re-read from the entry at accept: a sibling worker in the same workspace may reconcile while this proposal sits paused, advancing the entry's `synced_sig` to the drifted disk — comparing against the *current* entry sig would wave that clobber through, so the comparison is always against the sig the proposal was computed at. A proposal that assumed an **absent** path (a create) conflicts only if a file has since appeared; a member with **no recorded snapshot** (an un-materialized entry, null `synced_sig`) has no baseline to guard and writes through — the two are told apart by the proposal's `existed` flag, not by a null sig alone. On a clean landing the entry refreshes to the written content and `synced_sig` is **restamped** to it, so the next reconcile recognizes the model's own write (not an external divergence) and a second same-turn edit bases on the landed bytes, not a stale sig. This is the write-side twin of the read-side change-gate ({§membership-change-gated-sync}): one `synced_sig`, gating both the re-read and the write.
|
|
2445
2582
|
|
|
@@ -2449,68 +2586,61 @@ The CAS is the **hard backstop**, at the moment of writing, on every accept path
|
|
|
2449
2586
|
|
|
2450
2587
|
**Rationale.** Workspace is the right scope unit and the containing Git repository is its ordinary development boundary. Membership curation is tiered: Git bounds it by tracking, the client supersedes by overlay, and the model curates its render by READ/FOLD. Supporting several independent repositories as one world would require Plurnk-owned topology, synchronization, and model teaching that Git already solves cleanly by treating them as separate workspaces.
|
|
2451
2588
|
|
|
2452
|
-
**Schema.** The version-1 baseline stores
|
|
2453
|
-
|
|
2454
|
-
|
|
2589
|
+
**Schema.** The version-1 baseline stores logical model calls beneath turns,
|
|
2590
|
+
emission admission as their specialization, and cardinal physical requests
|
|
2591
|
+
beneath each call. Its constraints distinguish pending calls, response
|
|
2592
|
+
evidence, and response-less errors while monetary classification remains
|
|
2593
|
+
explicit.
|
|
2455
2594
|
|
|
2456
2595
|
### §grinder Budget enforcement: the grinder
|
|
2457
2596
|
|
|
2458
|
-
The grinder is the one pre-provider
|
|
2459
|
-
|
|
2597
|
+
The grinder is the one pre-provider curation path for the model-facing gauge.
|
|
2598
|
+
It never decides physical admission. Every request then crosses the provider's
|
|
2599
|
+
request-shaped capacity boundary, where proven exact overflow may reject
|
|
2600
|
+
pre-I/O and ambiguous evidence defers to upstream:
|
|
2460
2601
|
|
|
2461
2602
|
```mermaid
|
|
2462
2603
|
flowchart TD
|
|
2463
|
-
assemble["Assemble and measure<br/>request packet"] --> policy{"
|
|
2464
|
-
policy -->|yes|
|
|
2465
|
-
policy -->|no|
|
|
2604
|
+
assemble["Assemble and measure<br/>request packet"] --> policy{"Packet weight ≤<br/>curation budget?"}
|
|
2605
|
+
policy -->|yes| generate["Provider generate"]
|
|
2606
|
+
policy -->|no| problem["Record nonterminal 413<br/>Token Budget Overflow"]
|
|
2607
|
+
problem --> fold["FOLD newest boundary<br/>and its OPEN effects<br/>tag overflow"]
|
|
2466
2608
|
fold --> rebuild["Rebuild and remeasure"]
|
|
2467
|
-
rebuild -->
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
grant -->|yes| steer["Record recovery Problem<br/>preserve the ordinary operation contract<br/>rebuild"]
|
|
2478
|
-
steer --> finalPhysical{"Final packet physically sendable?"}
|
|
2479
|
-
finalPhysical -->|yes| recovery["One informed<br/>provider call"]
|
|
2480
|
-
finalPhysical -->|no| stop
|
|
2609
|
+
rebuild --> generate
|
|
2610
|
+
generate --> capacity{"Provider capacity<br/>failure?"}
|
|
2611
|
+
capacity -->|no| response["Classify completed response"]
|
|
2612
|
+
capacity -->|yes| prompt{"Withholding automatic<br/>prompt bodies changes request?"}
|
|
2613
|
+
prompt -->|yes| retryPrompt["Persist failure;<br/>rebuild and retry"]
|
|
2614
|
+
retryPrompt --> generate
|
|
2615
|
+
prompt -->|no| boundary{"Newest boundary not yet<br/>rolled back, and rollback<br/>changes request?"}
|
|
2616
|
+
boundary -->|yes| retryBoundary["Persist failure;<br/>roll back, rebuild, retry"]
|
|
2617
|
+
retryBoundary --> generate
|
|
2618
|
+
boundary -->|no| stop["Persist terminal 413"]
|
|
2481
2619
|
```
|
|
2482
2620
|
|
|
2483
2621
|
§grinder-overflow-only **The grinder fires only on actual overflow.** In
|
|
2484
2622
|
`Engine.runTurn`, after `PacketBuilder.buildRequestPacket` assembles the request
|
|
2485
2623
|
and before `provider.generate`, it compares the packet's render-weight
|
|
2486
|
-
({§tokenomics}) with the
|
|
2487
|
-
ships untouched
|
|
2624
|
+
({§tokenomics}) with the provider-derived curation budget. At or under the budget, the packet
|
|
2625
|
+
ships untouched. The grinder never trims
|
|
2488
2626
|
speculatively or "helpfully."
|
|
2489
2627
|
|
|
2490
|
-
- §grinder-
|
|
2491
|
-
- §grinder-
|
|
2492
|
-
- §grinder-
|
|
2493
|
-
- §grinder-hard-413
|
|
2494
|
-
|
|
2495
|
-
- §tokenomics-fetch-fits-free **A retrieval larger than the available packet room arrives folded.** The result lands in the next build; if that build exceeds the ceiling, the grinder folds the newest boundary, which contains the result. Its row and exact body remain durable and re-OPENable. If the packet still cannot fit, the recovery turn reports exact measurements and useful recovery options. No ambient packet text prescribes an ordering strategy.
|
|
2628
|
+
- §grinder-overflow-problem **Token Budget Overflow is a nonterminal 413 Problem.** Every over-ceiling assembly records exactly one `engine/context/token-budget-overflow` Problem before automatic recovery, with pre-recovery `usage`, `ceiling`, and `deficit` evidence. Its exact `detail` is `Token Budget Overflow: Token Usage exceeded Token Ceiling. Newest log items were automatically FOLDed to fit within token budget. Curate the log and/or perform more conservatively scoped or chunked retrieval operations to recover.` The Problem remains durable and model-visible even when folding restores room and the turn later concludes successfully; it does not strike or replace the turn's terminal disposition.
|
|
2629
|
+
- §grinder-layer1-rollback **One rule, every turn: roll back context introduced by the newest boundary.** On overflow the grinder folds, in one set operation, still-open rows born in the immediately prior turn or current pre-model turn plus exact older rows that a successful OPEN in the immediately prior turn transitioned from folded to open. It reads those landed effects from {§fold-open-meta-operations}; it never re-runs the selector, treats an already-open target as newly introduced, or chooses other older history by relevance. Turn 1 has no prior turn, so only its pre-model rows qualify. The same set operation additively applies the `overflow` tag to every row it folds; rows and bodies remain re-OPENable.
|
|
2630
|
+
- §grinder-errors-exempt **Errors, the prompt, AND the plan are exempt.** The grinder never folds an `op='error'` row: errors are the model's durable, curatable record of what went wrong. Nor does it fold the actionless **user prompt** row (`prompt:///<loop>/<N>`): the task frame is not ordinary model-authored memory. Nor a **PLAN row**: the checklist is the model's orientation surface when the grinder fires. All three stay OPEN until the model itself FOLDs or KILLs them.
|
|
2631
|
+
- §grinder-hard-413 **Only a provider capacity failure can terminalize at 413.** Every `provider.generate` assesses its exact request under {§provider-surface-capacity}. A proven exact preflight overflow issues no physical request; estimate, unavailable evidence, and a non-fitting upper bound defer to upstream, whose context rejection normalizes to the same `capacity_exceeded` failure. Core retries only after changing the request: first by withholding automatic prompt-body projection when that changes the packet, then by rolling back the newest boundary once when the grinder has not already done so and that changes the packet. Each failed logical call, any issued physical request, capacity evidence, and model-visible provider Problem remain durable; these recovery calls do not consume the completed-emission attempt budget. Core never resends identical bytes as capacity recovery and never chooses arbitrary older history. If neither recovery changes the request, or the changed request is still rejected, the request-only turn and exact provider Problem terminalize the loop at **413 Content Too Large**.
|
|
2496
2632
|
|
|
2497
|
-
- §
|
|
2633
|
+
- §tokenomics-fetch-fits-free **A retrieval larger than the available packet room arrives folded with a 413 explanation.** The result lands in the next build; if that build exceeds the curation budget, Core records {§grinder-overflow-problem} and folds the newest boundary, which contains the result. Its row and exact body remain durable and re-OPENable, and its `overflow` tag identifies every row selected by the automatic fold. Remaining curation debt follows {§tokenomics-negative-pressure}.
|
|
2498
2634
|
|
|
2499
|
-
§
|
|
2500
|
-
per-turn rail verdict, so an overflow contributes one strike under
|
|
2501
|
-
{§engine-rails}. If cycle detection also fires on that turn, the turn still
|
|
2502
|
-
contributes only one strike and a threshold crossing is classified as 508.
|
|
2635
|
+
- §loop-terminals **Engine-imposed terminals are HTTP-precise** — the loop-status vocabulary, one meaning each: `200` concluded (the model's SEND signal `200`) · `499` model-abandoned (signal `499`, or a cancel) · `429` maxTurns exhausted · `413` provider input-capacity failure after changed-request recovery · `500` strike threshold or invalid-emission exhaustion (distinct Problem types; `508` when the crossing strike was a detected cycle) · `504` loop timeout / exec-timeout restamp · `202` the bounded wait — a loop blocked on a live obligation (the model's `## SEND0 [202] <T,P>`, {§wait-obligation-matrix}); a wait on nothing resolves to `200` unless a successful same-turn FOLD requires the curated next packet · `100`/`102` queued/running. Never a catch-all, never a new value without changing the owning schema.
|
|
2503
2636
|
|
|
2504
|
-
§grinder-
|
|
2505
|
-
is no soft exemption. Folded rows still cost their coordinate lines, so
|
|
2506
|
-
repeated overflow can legitimately reach the strike threshold.
|
|
2637
|
+
§grinder-pressure-surface **What the model sees.** A fold-to-fit packet carries the open {§grinder-overflow-problem} row, its terse `## Errors` pointer, and ordinary folded rows whose complete sorted `tags` include `overflow`. If curation pressure remains negative but the provider accepts the request, the Budget section also reports the negative free-weight value and its one panic line ({§tokenomics-neutral-telemetry}). The curation 413 diagnoses the overflow without striking, terminalizing the turn, or changing the ordinary operation contract.
|
|
2507
2638
|
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
The
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
speculative or non-overflow trimming.
|
|
2639
|
+
The model controls its context; the provider owns physical admission, and Core
|
|
2640
|
+
performs only bounded changed-request recovery without choosing what older
|
|
2641
|
+
history matters. The same boundary applies on turn 1 and turn 101. The grinder
|
|
2642
|
+
folds reversibly, never deletes, and never performs speculative or non-overflow
|
|
2643
|
+
trimming.
|
|
2514
2644
|
|
|
2515
2645
|
### §env-delta The environment delta: what changed since the model last looked
|
|
2516
2646
|
|
|
@@ -2534,7 +2664,8 @@ flowchart LR
|
|
|
2534
2664
|
pre-turn, a worker materializes every other actor's event on shared state after
|
|
2535
2665
|
its last completed observation boundary into its own log. The set is
|
|
2536
2666
|
exhaustive, unranked, and exactly once; the engine makes no relevance decision.
|
|
2537
|
-
Each copied event retains its effect, cause,
|
|
2667
|
+
Each copied event retains its effect, cause, typed attributes, and initial
|
|
2668
|
+
log classifications ({§log-item-tags}). Every
|
|
2538
2669
|
producer appends to one workspace-scoped occurrence journal with a monotonic
|
|
2539
2670
|
identity. A pull captures one closed `(worker cursor, high-water]` interval,
|
|
2540
2671
|
materializes each identity idempotently, then advances the cursor only after the
|
|
@@ -2620,16 +2751,22 @@ the aggregate remains dispatch coordination state.
|
|
|
2620
2751
|
| -------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
2621
2752
|
| Full `revision` | `rev` abbreviated to `PLURNK_SERVICE_EDIT_RECEIPT_REVISION_CHARS` | SHA-256 identity of the complete landed channel body; display correlation only, never a lookup or compare-and-swap token. |
|
|
2622
2753
|
| `unit`, `before`, `after` | `extent` | Whole-line batches use line counts. A batch containing any exact four-coordinate edit uses Unicode code-point counts. |
|
|
2754
|
+
| `parseIssues` | `parseIssues` | Positive parser-recovery count for the complete landed revision; clean, unsupported, and unavailable evidence is omitted. |
|
|
2623
2755
|
| `effect.requested`, `source`, `result` | `range` | The admitted marker and its normalized mapping from the common source snapshot into the landed body. |
|
|
2624
2756
|
| `effect.removed`, `inserted` | `change` | Removed and inserted counts in the receipt unit. |
|
|
2625
|
-
| `effect.context` | Canonical row body | Numbered physical lines
|
|
2757
|
+
| `effect.context` | Canonical row body | Numbered physical lines at each landed boundary, bounded symmetrically by `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES`. |
|
|
2626
2758
|
| `disposition`, `requested` | `disposition`, `requested` | A reviewer-replaced batch preserves the authored marker while stating that its attributed effect was superseded. |
|
|
2627
2759
|
| `replacement` | `replacement`, `change`, canonical proposal-owner body | The one whole-resource effect actually applied by the reviewer replacement; never duplicated across authored rows. |
|
|
2628
2760
|
|
|
2629
2761
|
§edit-result-receipt-truth **Receipts describe committed state.** Every row in
|
|
2630
|
-
one resource-channel EDIT batch carries the same landed revision and
|
|
2762
|
+
one resource-channel EDIT batch carries the same landed revision, extent, and
|
|
2763
|
+
optional positive `parseIssues` count for that complete revision.
|
|
2631
2764
|
When the proposed batch lands unchanged, each row also carries its own requested
|
|
2632
|
-
marker, source/result mapping, counts, and context.
|
|
2765
|
+
marker, source/result mapping, counts, and context. For configured count `C`,
|
|
2766
|
+
the context contains up to `C` surrounding lines and the first and last `C`
|
|
2767
|
+
landed lines at the result boundaries. Overlapping windows coalesce; coordinate
|
|
2768
|
+
jumps expose an omitted middle. A deletion instead shows up to `C` lines on
|
|
2769
|
+
each side of its join.
|
|
2633
2770
|
|
|
2634
2771
|
§edit-result-reviewer-replacement **A resolver replacement is one effect, not a
|
|
2635
2772
|
guess at authorship.** An arbitrary accepted body replaces the batch's proposed
|
|
@@ -2675,7 +2812,10 @@ landed.
|
|
|
2675
2812
|
| `304`, rejection, or cancellation with no landed mutation | `effects` omitted. |
|
|
2676
2813
|
| Cross-selection MOVE source failure after destination success | The failure retains every destination effect that landed. |
|
|
2677
2814
|
|
|
2678
|
-
Core validates the complete ordered array before exposing it.
|
|
2815
|
+
Core validates the complete ordered array before exposing it. Parser-recovery
|
|
2816
|
+
inspection is advisory and occurs against complete resulting text after
|
|
2817
|
+
successful application. A handler or parser failure emits a Notice, omits
|
|
2818
|
+
`parseIssues`, and never changes the mutation outcome.
|
|
2679
2819
|
|
|
2680
2820
|
### §proposal-ownership Loop auto and client YOLO
|
|
2681
2821
|
|
|
@@ -2705,19 +2845,19 @@ when an emission is admitted, its response.** Core assembles and measures the
|
|
|
2705
2845
|
request under {§packet-assembly}. An admitted response extends that same record
|
|
2706
2846
|
before the turn closes; a failed provider call or exhausted invalid emission
|
|
2707
2847
|
leaves the request-only record, while rejected exchanges remain in
|
|
2708
|
-
`turn_attempts`.
|
|
2848
|
+
`model_calls` with their classification in `turn_attempts`.
|
|
2709
2849
|
|
|
2710
2850
|
| Turn state | `turns.packet` |
|
|
2711
2851
|
| ----------------------------- | ----------------------------------------------- |
|
|
2712
2852
|
| No model request assembled | SQL `NULL` |
|
|
2713
|
-
| Request assembled | `{
|
|
2714
|
-
| Response admitted | `{
|
|
2853
|
+
| Request assembled | `{ weight, sections }` |
|
|
2854
|
+
| Response admitted | `{ weight, sections, assistant, assistantRaw }` |
|
|
2715
2855
|
|
|
2716
2856
|
| Field | Presence | Contract |
|
|
2717
2857
|
| ----------------------- | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
2718
|
-
| `
|
|
2858
|
+
| `weight` | Every assembled model request | Curation weight of both rendered request slots. Admission does not change its meaning; it is never response weight or provider usage. |
|
|
2719
2859
|
| `sections` | Every assembled model request | Ordered post-transform request sections. `PacketWire.renderSlot` groups them into system and user messages, and the digest re-renders those stored sections byte-for-byte. |
|
|
2720
|
-
| `sections[].
|
|
2860
|
+
| `sections[].weight` | Every stored section | Independently measured curation weight of that section. Their sum is not the rendered request weight because slot separators and independent rounding remain outside each row. |
|
|
2721
2861
|
| `assistant.content` | Admitted response only | Accepted model content from which operations were parsed. |
|
|
2722
2862
|
| `assistant.ops` | Admitted response only | Parsed operations admitted from that content. |
|
|
2723
2863
|
| `assistant.reasoning` | Admitted response only | Normalized readable reasoning text, or `null`. |
|
|
@@ -2732,7 +2872,7 @@ or request-only note instead of fabricating response files.
|
|
|
2732
2872
|
|
|
2733
2873
|
The external tokenless draft and transformation boundary is owned by
|
|
2734
2874
|
{§scheme-packet-transform}. Core alone extends each validated draft with its
|
|
2735
|
-
measured `
|
|
2875
|
+
measured `weight` field for storage. #74 tracks coverage that mistakes the sum
|
|
2736
2876
|
of section weights for the rendered request weight.
|
|
2737
2877
|
|
|
2738
2878
|
§definition-table-projection The authored `plurnk.md` remains human-aligned. Its `definition` section deterministically removes Markdown table-cell padding and shortens separator cells to three dashes before plugin transforms, measurement, storage, and wire rendering; alignment colons survive, while fenced blocks and all non-table whitespace remain exact.
|
|
@@ -2745,17 +2885,13 @@ of section weights for the rendered request weight.
|
|
|
2745
2885
|
| Core lifecycle | Use the exact Workspace → Worker → Loop → Turn → Op hierarchy in {§lifecycle-terms}. An AG-UI Run or thread is always protocol-qualified. |
|
|
2746
2886
|
| Model-facing packet | Use the model's training distribution: operations mirror HTTP and shell, `display` mirrors CSS, and jsonplurnk remains JSON. Renaming this vocabulary to internal API terminology would discard useful resonance for a standard the model never sees. |
|
|
2747
2887
|
|
|
2748
|
-
`promptBudget` is deliberately distinct from `contextWindow`: it is PLURNK's
|
|
2749
|
-
enforced packet allowance, bounded by provider capacity and optionally tightened
|
|
2750
|
-
by service policy.
|
|
2751
|
-
|
|
2752
2888
|
| PLURNK-native term | Why it remains |
|
|
2753
2889
|
|--------------------------------|----------------|
|
|
2754
2890
|
| `worker` / `loop` / `turn` | The process hierarchy in {§lifecycle-terms}; unqualified `run` names no internal entity. |
|
|
2755
2891
|
| `packet` | The assembled address space, a kernel concept rather than merely a provider request. |
|
|
2756
|
-
| `costUsd` | No standard cost field exists; the explicit currency avoids implied units. |
|
|
2757
|
-
| `
|
|
2758
|
-
| the `chars/2` ruler
|
|
2892
|
+
| `costUsd` | No standard cost field exists; the explicit currency avoids implied units, and the value remains an exact decimal string. |
|
|
2893
|
+
| `curationWeight` / `curationBudget` | Explicitly distinguish Core's model-independent context-shaping facts from physical provider tokens. |
|
|
2894
|
+
| the `chars/2` curation ruler | Model-agnostic by design ({§tokenomics-agnostic-ruler}); it is never presented as a tokenizer. |
|
|
2759
2895
|
|
|
2760
2896
|
Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualified `session` noun, `contextSize`, `decodeBudget`, and moved partition-knob names. <!-- lexicon-allow: this sentence enumerates the retired terms -->
|
|
2761
2897
|
|
|
@@ -2767,7 +2903,7 @@ Core never decodes the blobs or renders them into a model packet; readable
|
|
|
2767
2903
|
reasoning text remains separate in `assistant.reasoning`. The provider-detail
|
|
2768
2904
|
identity and derived classification retain their exact provider-normalized
|
|
2769
2905
|
meaning from {§provider-encrypted-reasoning}; core never reinterprets either as
|
|
2770
|
-
a client entity. The mirror row and
|
|
2906
|
+
a client entity. The mirror row and logical model call remain the lossless
|
|
2771
2907
|
evidence when a downstream standard cannot represent the complete list.
|
|
2772
2908
|
|
|
2773
2909
|
§body-projection **One full body, one packet projection.** Every durable log row has one canonical full body resolved from its stored tx/rx envelope by `LogBody`. READ and FIND over `log:///`, persistent search derivation, and packet rendering all consume that same meaning. Only packet rendering may project it:
|
|
@@ -2775,13 +2911,18 @@ evidence when a downstream standard cannot represent the complete list.
|
|
|
2775
2911
|
| row producer | ordinary OPEN projection |
|
|
2776
2912
|
|---|---|
|
|
2777
2913
|
| any `READ` or `FIND` | complete selected operation result |
|
|
2914
|
+
| any `PLAN` | complete authored working memory |
|
|
2915
|
+
| actionless lowercase `prompt` | budgeted head under {§prompt-projection} |
|
|
2916
|
+
| structured `EDIT` receipt or textual `COPY`/`MOVE` effects | complete receipt-owned join context |
|
|
2778
2917
|
| every other nonempty body | head bounded independently by `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS` |
|
|
2779
2918
|
| bodyless row | `"display":"none","body":""` |
|
|
2780
2919
|
|
|
2781
|
-
READ and FIND own their range or pagination before packet rendering; the packet never applies a second hidden substring bound to their selected result.
|
|
2920
|
+
READ and FIND own their range or pagination before packet rendering; the packet never applies a second hidden substring bound to their selected result. PLAN is likewise complete while OPEN: it is the model's explicit persistent reasoning inventory, not ordinary content that the model should have to retrieve from itself. Prompt rows follow their separate adaptive projection contract. Structured mutation contexts already carry the receipt-owned bound in {§edit-result-receipt-truth}, so packet rendering does not preview them again. Model-emission mirrors, SEND/WORK/FORK bodies, EXEC commands, environment-delta EDIT spans, and extension-produced bodies use the ordinary fixed bound. When an OPEN projection differs from its canonical body, `chunk` follows the displayed `body` with the exact selected and complete extents defined by {§jsonplurnk}; complete and FOLDED bodies omit it. `## READ0 (log:///<coordinate>/<OP>)` applies its default or explicit text range to the canonical body; the unsuffixed exact shorthand and authoritative suffix behavior are defined by {§log-coordinate-hierarchy}. `## FIND0 (log:///...)` and search match that same full body. FOLD hides the ordinary projection, and OPEN restores the producer's projection without changing its bound. System/policy sections are not log bodies. Notices are transient non-log observations; they share the ordinary line/character bounds but have no durable body or recovery URI.
|
|
2782
2921
|
|
|
2783
2922
|
§prompt-entry **Prompt as a first-class entry and log row.** Each prompt is stored once at `prompt:///<loop>/<N>` as an owner-keyed text/markdown entry, then published to its turn as one actionless lowercase `prompt` log row. No synthetic EDIT or READ operation is invented. The row is born OPEN and obeys {§body-projection}. The **Active User Prompts** section closes the user-slot status clump as a paths-only list (`* prompt:///<loop>/<N>`), so every frame remains directly READable even after its log row is folded or killed.
|
|
2784
2923
|
|
|
2924
|
+
§prompt-projection **Prompt storage is unbounded by model context; automatic materialization is not.** Core persists every accepted prompt completely before packet assembly. The selected provider's derived `inputCapacity` and the alias-resolved percentage from `PLURNK_SERVICE_PROMPT_PROJECTION` derive one aggregate curation-weight allowance for OPEN prompt bodies. Complete prompt bodies render when their aggregate weight fits. Otherwise all OPEN prompt rows share the allowance: full bodies consume only their required share, unused shares are redistributed, and partial bodies render the largest leading complete-line region that fits their share or an exact character-bound prefix when the first physical line alone is larger. The sum of their rendered body weights never exceeds the allowance. Every partial body carries its exact `chunk` after `body`; the canonical `prompt:///` entry and `log:///` body remain complete and READ/FIND-addressable. When provider input capacity is unknown the percentage is underivable, so prompt rows retain the ordinary bounded projection rather than inventing capacity. This policy never rejects, summarizes, or discards a prompt because it exceeds a context window.
|
|
2925
|
+
|
|
2785
2926
|
§prompt-self-only The frame is self-only and owner-keyed:
|
|
2786
2927
|
`entries.owner_id` carries worker identity while the address carries only the
|
|
2787
2928
|
loop coordinate. Concurrent workers therefore hold distinct rows at the same
|
|
@@ -2806,7 +2947,7 @@ their row shape, and their ordering are ordinary FIND projections owned by
|
|
|
2806
2947
|
|
|
2807
2948
|
The model's runtime alert surface has two distinct kinds of information:
|
|
2808
2949
|
|
|
2809
|
-
- **Turn failures are log items.** A failed action and an engine-rail failure are durable `log_entries` rows whose `rx` is an RFC 9457 operation result. They fold, kill, and budget like every other row. The `errors` section is a derived pointer index over recent `status_rx ≥ 400` rows; it owns no bodies or failure state. Rejected emissions never become accepted turn content and
|
|
2950
|
+
- **Turn failures are log items.** A failed action and an engine-rail failure are durable `log_entries` rows whose `rx` is an RFC 9457 operation result. They fold, kill, and budget like every other row. The `errors` section is a derived pointer index over recent `status_rx ≥ 400` rows; it owns no bodies or failure state. Rejected emissions never become accepted turn content; their private response and admission evidence remains in `model_calls` and `turn_attempts`, apart from the bounded recovery mirror under {§invalid-emission-attempts}.
|
|
2810
2951
|
- **Notices are transient observations.** Progress and non-fatal diagnostics such as `turn_awaiting_model`, `embed_progress`, and `grammar_unenforced` may appear once in the packet and broadcast live. They neither substitute for a failure result nor influence scheduling or recovery.
|
|
2811
2952
|
|
|
2812
2953
|
The `log` is durable product truth. The `errors` section points at its failures
|
|
@@ -2843,7 +2984,7 @@ retain distinct contracts and lifetimes.
|
|
|
2843
2984
|
| failure | row | status |
|
|
2844
2985
|
|---|---|---|
|
|
2845
2986
|
| action failure | the failed op's own row; the owning scheme supplies Problem Details | 4xx/5xx |
|
|
2846
|
-
|
|
|
2987
|
+
| provider input capacity | `op='error'`, origin `plurnk`, source `provider`; exact provider-owned `capacity-exceeded` Problem Details | 413 |
|
|
2847
2988
|
| max commands exceeded | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/max-commands-exceeded` Problem Details | 429 |
|
|
2848
2989
|
| idle turn | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/idle-turn` Problem Details | 409 |
|
|
2849
2990
|
|
|
@@ -2856,7 +2997,7 @@ retain distinct contracts and lifetimes.
|
|
|
2856
2997
|
|
|
2857
2998
|
§notice-level **Severity on the wire (`level`, required).** Every `Notice` carries `level: "error" | "warn" | "info"`, set by the **producer** at the emit site. The level is client presentation, not operation status: even an `error` notice cannot terminalize work or substitute for a durable Problem. A forwarded `grammar_unenforced` is `warn`; ordinary lifecycle and progress notices are `info`. Clients color straight off `level` without interpreting the open `kind` vocabulary.
|
|
2858
2999
|
|
|
2859
|
-
§operation-result-no-error-scheme Private strike and cycle accounting stays engine-internal ({§rail-accounting-private}). Every failure within an accepted turn - a bounded parse error, failed action, or engine rail - is a LOG ITEM (`log:///<coord>`, `status_rx ≥ 400`) with Problem Details, foldable and re-OPENable. The `errors` section surfaces a derived pointer to each. Rejected
|
|
3000
|
+
§operation-result-no-error-scheme Private strike and cycle accounting stays engine-internal ({§rail-accounting-private}). Every failure within an accepted turn - a bounded parse error, failed action, or engine rail - is a LOG ITEM (`log:///<coord>`, `status_rx ≥ 400`) with Problem Details, foldable and re-OPENable. The `errors` section surfaces a derived pointer to each. Rejected emissions stay in the forensic model-call and admission relations. There is **no bespoke `error://` scheme** and no ephemeral per-category failure buffer.
|
|
2860
3001
|
|
|
2861
3002
|
§notice-event-notify **Client surface.** Engine Notices broadcast live via the `notice/event` WS notification — `{ loopId, notice: { source, kind, level, message?, position?, …kind-specific } }` per the grammar's `Notice` schema — the moment they land, scoped to the loop's workspace. AG-UI projects the same observation as the custom `plurnk.notice` event. Failures do not broadcast on this surface: they are log rows, and the client reads them through `log.read` / the `log/entry` notification, the durable log.
|
|
2862
3003
|
|
|
@@ -2867,10 +3008,10 @@ retain distinct contracts and lifetimes.
|
|
|
2867
3008
|
| Import `@plurnk/plurnk-service/digest` | Ships `Digest` and its co-located `digest.sql`; importing performs no I/O or process action. The CLI wrapper alone invokes it. |
|
|
2868
3009
|
| `run({ dbPath })` | Reads the required database and writes a complete digest to `./test/digest` relative to the caller's working directory. |
|
|
2869
3010
|
| `digestDir` | Selects the output directory. `run` removes and recreates it so stale packet artifacts cannot survive; concurrent callers use distinct directories. |
|
|
2870
|
-
| `workerId` | Narrows workers and every dependent loop, turn, attempt,
|
|
3011
|
+
| `workerId` | Narrows workers and every dependent loop, turn, logical model call, emission attempt, physical request, and log row to that one worker. |
|
|
2871
3012
|
| `workspaceId` | Narrows workers and dependent evidence to one workspace; when both selectors are present they intersect. |
|
|
2872
3013
|
|
|
2873
|
-
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log row, including causal `source` and structured `attrs`, the exact Problem on every failed row,
|
|
3014
|
+
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log row, including causal `source` and structured `attrs`, every exact OPEN/FOLD target effect from {§fold-open-meta-operations}, the exact Problem on every failed row, each loop's exact terminal result, and every ordered physical provider request. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and folded network-entry storage paths render back to their authority form. Its human Markdown waterfall groups identical per-turn op outcomes and typed `entry_materialized` narrations, reporting the exact count and sequence span (`xN (seq A-B)`). Grouping keys include source and the complete target, so distinct causes, authorities, or channels never collapse. Thus amplification is conspicuous without making the diagnostic artifact itself pathological; packet files remain byte-identical records of what the model saw.
|
|
2874
3015
|
|
|
2875
3016
|
§digest-requiem **A requiem is an out-of-band forensic interview, not a worker
|
|
2876
3017
|
turn.** It cannot execute operations or alter the audited history.
|
|
@@ -2880,9 +3021,9 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
2880
3021
|
| Scope | One interview for each worker with model-bearing turns; journal-only workers are omitted. |
|
|
2881
3022
|
| Evidence | The worker's final packet plus every attempt's exact normalized response and admission evidence; opaque raw transport remains in durable forensic artifacts. |
|
|
2882
3023
|
| Witness | An explicitly supplied provider or the active configured provider; absence fails hard. |
|
|
2883
|
-
| Identity | The worker's durable
|
|
3024
|
+
| Identity | The worker's durable provider identity ({§worker-provider-identity}) is sent as both `workerId` and `primaryWorkerId`, making the synthetic interview its own root without asserting a live worker topology. |
|
|
2884
3025
|
| Attempts | One call at `PLURNK_SERVICE_REQUIEM_MAX_TOKENS`; only an empty length-limited response receives one retry at `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS`. |
|
|
2885
|
-
| Artifacts | `requiem.md` carries testimony and nullable
|
|
3026
|
+
| Artifacts | `requiem.md` carries testimony and exact nullable USD accounting. `requiem.json` is durably materialized before each call and preserves logical call state, messages, normalized responses, every physical request's state and accounting, and their shared aggregate projection. |
|
|
2886
3027
|
|
|
2887
3028
|
§turn-lifecycle **Turn-lifecycle liveness.** Provider generation is the long, opaque window in a turn — one or more same-packet emission attempts may occur before the first committed op. A static client screen there is indistinguishable from a hang. The engine brackets the complete attempt window with two `notice/event` notices (`source: "engine:turn"`, `level: "info"`): `turn_awaiting_model` before the first call and `turn_generated` when an emission is accepted or the attempt budget is exhausted. Rejected content never rides the notice channel. Both are suppressed on an aborted loop and broadcast to the workspace like any notice ({§notice-event-notify}).
|
|
2888
3029
|
|
|
@@ -2890,28 +3031,28 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
2890
3031
|
|
|
2891
3032
|
### §tools user.tools — the capability sheet
|
|
2892
3033
|
|
|
2893
|
-
§tools-capability-sheet The
|
|
3034
|
+
§tools-capability-sheet The tools capability sheet renders under `## Registered Tools`, after the policy sections. One generated Markdown table is the closed set of valid executor selectors and states each runtime declaration's model-facing `(target)` role, body role, and exact canonical example. The compact legend leaves required inputs unmarked, marks optional inputs with `?`, pairs mutually exclusive alternatives with `↔`, marks refused buckets with `—`, and locates optional `<timeout,poll>` on the heading. The preface prefers purpose-built Plurnk operations over EXEC scripts. For a declaration with `target.directory: "cwd"`, the target cell distinguishes a local-directory working context from the plugin-authored non-directory target role. Optional non-EXEC operations render separately under `## Enabled Optional Operations` in a `plurnk` fence, so the catalogue remains truthful. `PacketBuilder.#collectTools` assembles both; a prose notice (e.g. the EXEC-disabled line) stays beside the table, and empty sections are omitted.
|
|
2894
3035
|
|
|
2895
3036
|
§tools-loop-affinity **The capability sheet describes the current loop.** The
|
|
2896
3037
|
sheet filters registered capabilities through the same
|
|
2897
3038
|
`SchemeRegistry.resolveForLoop(flags)` predicate the dispatcher enforces. When
|
|
2898
|
-
registered executors exist but EXEC is inactive, their
|
|
3039
|
+
registered executors exist but EXEC is inactive, their table is replaced by
|
|
2899
3040
|
an explicit disabled notice rather than silent absence. The dispatch 403 remains
|
|
2900
3041
|
the backstop and names the non-retryable loop restriction.
|
|
2901
3042
|
|
|
2902
|
-
**Contributors: the wired executor tags.**
|
|
3043
|
+
**Contributors: the wired executor tags.** Every available executor tag contributes exactly one row derived from its required {§executor-invocation} declaration. Its doc is materialized at `worker://plurnk/docs/<tag>.md` and discovered via the turn-0 `## FIND0 [+init,+docs] (worker://plurnk/docs/**)` foist, not linked inline. `PLURNK_SERVICE_DOCS_EXCLUDE` drops a named tag's row and doc. The boot `ExecutorRegistry` probes availability per tag, so the table advertises runnable selectors instead of presuming a particular runtime exists.
|
|
2903
3044
|
|
|
2904
|
-
### §schemes user.schemes — the
|
|
3045
|
+
### §schemes user.schemes — the resource directory
|
|
2905
3046
|
|
|
2906
|
-
§schemes-directory A `##
|
|
3047
|
+
§schemes-directory A `## Resources` section renders in the system slot **after the policy sections and tools sheet** — a terse directory of the scheme families available this workspace, so the model knows what URI resources and operations exist before it acts. Each scheme that ships a `manifest.example` contributes one or more concise canonical ops (no scheme prefix; each example self-documents) into a `plurnk` fence. Scheme example sets are separated by one blank line. The doc is NOT linked inline — it is materialized at `worker://plurnk/docs/<scheme>.md` and discovered via the turn-0 `## FIND0 [+init,+docs] (worker://plurnk/docs/**)` foist, keeping the raw packet free of doc links. Meta-owned `log` and `worker` depth is required teaching ({§teaching-corpus}); a failed source read rejects materialization with its cause and never falls back. Other core and plugin schemes may supply optional `manifest.documentation`; absence contributes no pull doc. The verbose semantics live in that pull doc (materialized like any entry, READ on demand), not the hot path — terse pushes, depth pulls. A scheme with no example (provisional) is omitted; `PLURNK_SERVICE_DOCS_EXCLUDE` drops a named scheme's examples + doc.
|
|
2907
3048
|
|
|
2908
3049
|
### §inject system.inject — the operator injection
|
|
2909
3050
|
|
|
2910
|
-
§packet-inject When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as an `## Operator Notes` section in the system slot
|
|
3051
|
+
§packet-inject When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as an `## Operator Notes` section in the system slot after policy and capability teaching (definition → policy → project policy → tools → resources → inject). 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.
|
|
2911
3052
|
|
|
2912
3053
|
### §policy system.policy — the client's policy injection
|
|
2913
3054
|
|
|
2914
|
-
§policy-sections Two sections ride the system slot **
|
|
3055
|
+
§policy-sections Two sections ride the system slot **after the definition and before loop-dependent capabilities**: `## Policy` from `PLURNK_SERVICE_POLICY` (default `~/.plurnk/AGENTS.md`) and `## Project Policy` from `PLURNK_SERVICE_PROJECT` (default `<projectRoot>/AGENTS.md`, resolved relative to the workspace 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 and scratch docs are NOT policy; `PLURNK_SERVICE_MD_*` materializes them as READ-able entries ({§operator-config}).
|
|
2915
3056
|
|
|
2916
3057
|
On first run, and only when `~/.plurnk` itself is absent, the service seeds
|
|
2917
3058
|
`AGENTS.md` from `@plurnk/plurnk-meta/PLURNK_PERSONALITY.md` ({§teaching-corpus}).
|
|
@@ -2920,7 +3061,7 @@ surfaces with its cause and leaves no apparently initialized home.
|
|
|
2920
3061
|
After that bootstrap the file is user-owned: edits and deletion persist, and a
|
|
2921
3062
|
later boot never refreshes or recreates it.
|
|
2922
3063
|
|
|
2923
|
-
§schemes-self-doc-materialization **The scheme self-doc contract.** `@plurnk/plurnk-schemes` owns `example` and `documentation` in `SchemeManifest` ({§manifest-self-doc}); the former is the hot-path
|
|
3064
|
+
§schemes-self-doc-materialization **The scheme self-doc contract.** `@plurnk/plurnk-schemes` owns `example` and `documentation` in `SchemeManifest` ({§manifest-self-doc}); the former is the hot-path operation example set and the latter is the deep pull doc. `SchemeRegistry.teach()` renders the directory, `SchemeRegistry.docs()` resolves corpus-or-manifest documentation, and `docEntries()` materializes the result when core publishes capabilities for a workspace.
|
|
2924
3065
|
|
|
2925
3066
|
### §packet-git-status The Git status section — compact repository state
|
|
2926
3067
|
|
|
@@ -2968,7 +3109,7 @@ stored a resource.
|
|
|
2968
3109
|
§graph-relations **Graph matching is one-hop, kind-agnostic name matching.**
|
|
2969
3110
|
Source definitions resolve over the complete relationship universe (the
|
|
2970
3111
|
workspace for entry FIND; the worker's complete log for log FIND), while the
|
|
2971
|
-
authored target
|
|
3112
|
+
authored target still constrains every resource returned. Outgoing
|
|
2972
3113
|
references belong to a definition through the handler-reported fully qualified
|
|
2973
3114
|
container identity.
|
|
2974
3115
|
|
|
@@ -2989,7 +3130,6 @@ container identity.
|
|
|
2989
3130
|
§matcher-dispatch-203-soft-fallback On parse failure, 203 returns raw content as the text primitive with `reason`
|
|
2990
3131
|
so the model can use ordinary text retrieval or repair the source.
|
|
2991
3132
|
|
|
2992
|
-
|
|
2993
3133
|
`Matcher.matchCandidates` searches heterogeneous resource sets. A candidate
|
|
2994
3134
|
whose handler returns 415 is omitted when another candidate supports the
|
|
2995
3135
|
dialect; if every candidate is unsupported, the first exact 415 Problem is the
|
|
@@ -3050,7 +3190,7 @@ coordinates, inverted regions, out-of-range coordinates, and other arities are
|
|
|
3050
3190
|
416.
|
|
3051
3191
|
|
|
3052
3192
|
Every successful scoped READ carries its complete resolved `region` in the
|
|
3053
|
-
operation result and packet metadata. The body remains
|
|
3193
|
+
operation result and packet metadata. The body remains coordinate-prefixed from
|
|
3054
3194
|
`startLine`; the region preserves columns that line numbering cannot express.
|
|
3055
3195
|
|
|
3056
3196
|
Every same-resource mutation resolves its replacement offsets against one
|
|
@@ -3060,11 +3200,10 @@ SARIF region/replacement algebra for exact spans and same-snapshot ordering, not
|
|
|
3060
3200
|
adoption of the SARIF interchange envelope.
|
|
3061
3201
|
|
|
3062
3202
|
§slice-semantics-compose-pattern **Compose from evidence.** A match region already uses the four-coordinate
|
|
3063
|
-
scope shape. A follow-up
|
|
3203
|
+
scope shape. A follow-up `## READ0 (resource) <SL,SC,EL,EC>` retrieves that exact
|
|
3064
3204
|
region. JSONPath/XPath remain locators and matchers; they do not introduce a
|
|
3065
3205
|
second structural scope or structural EDIT language.
|
|
3066
3206
|
|
|
3067
|
-
|
|
3068
3207
|
### §ext-mimetype Path-extension declares mimetype
|
|
3069
3208
|
|
|
3070
3209
|
`resolveEntryMimetype` (exported from `@plurnk/plurnk-schemes`): pathname extension → `Mimetypes.detect({ ext })` (with `text/plain` normalized to `text/markdown` per the text-primitive rule {§markdown-primitive}); falls back to scheme manifest channel default when no extension.
|
|
@@ -3078,18 +3217,20 @@ second structural scope or structural EDIT language.
|
|
|
3078
3217
|
mimetype is stored in `entry_channels.mimetype` on write and drives matcher,
|
|
3079
3218
|
projection, and binary handling. Text scope meaning does not vary by mimetype.
|
|
3080
3219
|
|
|
3081
|
-
|
|
3082
3220
|
### §render-rule Render rule
|
|
3083
3221
|
|
|
3084
|
-
§render-rule-line-navigable-prefix Every textual content body with a source
|
|
3085
|
-
prefix on each physical line, independent
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3222
|
+
§render-rule-line-navigable-prefix Every textual content body with a source
|
|
3223
|
+
`startLine` renders with a coordinate prefix on each physical line, independent
|
|
3224
|
+
of mimetype. A successful exact READ whose active scheme declares
|
|
3225
|
+
`textEditScopes: true` and model write authority supplies `@hash N:` with one
|
|
3226
|
+
or more ASCII spaces before `N` under
|
|
3227
|
+
{§line-anchors}; every other body renders `N:`. JSON, XML, and HTML are therefore just as
|
|
3228
|
+
line-addressable as markdown and source code. The prefix is a packet
|
|
3229
|
+
presentation aid, never part of canonical content; matchers and mutations
|
|
3230
|
+
consume canonical bytes before rendering. A producer may set `startLine: null`
|
|
3231
|
+
only when its content is already source-numbered, such as an effect receipt.
|
|
3091
3232
|
|
|
3092
|
-
§render-rule-find-renders-result A log row's canonical full body is resolved once by `LogBody`: READ/FIND, model-emission, prompt, and extension result content comes from `rx.content`; EDIT uses its structured receipt or an environment-delta span; COPY/MOVE concatenate the textual receipt contexts in their ordered `effects`; EXEC and the composed PLAN/SEND/WORK/FORK family use their statement body. Whole-channel COPY/MOVE effects are bodyless rather than fabricating a text projection. Packet rendering applies {§body-projection} and
|
|
3233
|
+
§render-rule-find-renders-result A log row's canonical full body is resolved once by `LogBody`: READ/FIND, model-emission, prompt, and extension result content comes from `rx.content`; EDIT uses its structured receipt or an environment-delta span; COPY/MOVE concatenate the textual receipt contexts in their ordered `effects`; EXEC and the composed PLAN/SEND/WORK/FORK family use their statement body. Whole-channel COPY/MOVE effects are bodyless rather than fabricating a text projection. Packet rendering applies {§body-projection} and the coordinate projection in {§render-rule-line-navigable-prefix}. READ/FIND over `log:///` and search consume the complete canonical body instead. Status and content are orthogonal: a failed terminal stream READ retains its Problem Details and failure status while rendering captured diagnostic output; failure never erases evidence.
|
|
3093
3234
|
|
|
3094
3235
|
An `EDIT` log row renders its bounded effect receipt (`rx.receipt`) as row
|
|
3095
3236
|
metadata and join context, not its input statement. Proposal-gated file EDITs
|
|
@@ -3100,7 +3241,8 @@ any scoped textual receipt contexts under their `log:///` address, never under
|
|
|
3100
3241
|
one operand's resource address. All generated bodies remain under
|
|
3101
3242
|
{§body-projection}. {§edit-result-render}
|
|
3102
3243
|
|
|
3103
|
-
|
|
3244
|
+
Numeric and anchored coordinate prefixes are presentation/reference per
|
|
3245
|
+
plurnk.md ("not part of the source"); matchers operate on canonical content.
|
|
3104
3246
|
|
|
3105
3247
|
### §markdown-primitive Mimetype primitive: text/markdown
|
|
3106
3248
|
|
|
@@ -3122,7 +3264,7 @@ Carried from the contract walk; durable.
|
|
|
3122
3264
|
- §copy-l-source-range **COPY/MOVE source scope** selects only the addressed source channel and
|
|
3123
3265
|
first resolves and, when required, prepares the same canonical
|
|
3124
3266
|
owner-addressed representation as exact READ/FIND. It transfers canonical
|
|
3125
|
-
text without the packet's
|
|
3267
|
+
text without the packet's coordinate prefix. A MOVE removes
|
|
3126
3268
|
that same selected region; an unscoped MOVE removes only the selected
|
|
3127
3269
|
channel, deleting the entry only when no channels remain. A binary marker
|
|
3128
3270
|
is not transferable; a readable binary projection is already a textual
|
|
@@ -3136,10 +3278,11 @@ Carried from the contract walk; durable.
|
|
|
3136
3278
|
Any scoped textual transfer materializes create/update receipts; whole-channel
|
|
3137
3279
|
changes do not. Operand selections remain independently visible per
|
|
3138
3280
|
{§copy-move-observation}.
|
|
3139
|
-
- **READ rx** prefixes every textual line
|
|
3140
|
-
|
|
3281
|
+
- **READ rx** prefixes every textual line under {§render-rule}; eligible
|
|
3282
|
+
editable resources carry `@hash N:`, and all others carry `N:`.
|
|
3283
|
+
- **FIND body matcher** applies to entry content (all dialects), per-candidate via the in-tree `Matcher.matchAgainstContent` ({§matcher-dispatch}; status 200 = content hit → entry selected). The target scope selects candidates; the path-glob is the (target). FIND's signal classifies its own log item ({§log-item-tags}).
|
|
3141
3284
|
- **OPEN/FOLD** operate on the **log** (`log:///`), not entries ({§open-fold}) — FOLD collapses a log row to its path, OPEN restores its body. Aimed at an entry scheme they return 501.
|
|
3142
|
-
- **SEND
|
|
3285
|
+
- **SEND signal `410`** deletes as a side-effect (not the model idiom; {§move}): with `#fragment`, that channel only; without, the whole entry. **SEND signal `499`** resolves the durable open-subscription row and invokes that subscription's exact callable owner through the process-local live registry ({§subscriptions}).
|
|
3143
3286
|
- **File scheme** detects with `Mimetypes.detect({ path })` and classifies with the same configured service ({§mimetype-classification-consumption}). Handler-declared binary sources materialize through {§membership-source-projection}; projected bodies are READ-able, while source-aware EDIT remains 415.
|
|
3144
3287
|
|
|
3145
3288
|
### §send-status-policy Directed-SEND status code policy
|