@plurnk/plurnk-service 1.19.3 → 1.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.defaults +6 -5
- package/SPEC.md +177 -111
- package/dist/build-info.json +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts +2 -2
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +12 -16
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/BareBatchRunner.d.ts +5 -1
- package/dist/core/BareBatchRunner.d.ts.map +1 -1
- package/dist/core/BareBatchRunner.js +86 -50
- package/dist/core/BareBatchRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +2 -2
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +23 -57
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Dispatcher.sql +7 -0
- package/dist/core/LogBody.js +2 -2
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts.map +1 -1
- package/dist/core/LogEntryProjection.js +0 -4
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LoopDriver.d.ts.map +1 -1
- package/dist/core/LoopDriver.js +17 -4
- package/dist/core/LoopDriver.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +3 -2
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +5 -0
- package/dist/core/ProviderRecovery.d.ts +9 -0
- package/dist/core/ProviderRecovery.d.ts.map +1 -0
- package/dist/core/ProviderRecovery.js +22 -0
- package/dist/core/ProviderRecovery.js.map +1 -0
- package/dist/core/ServiceTeardown.d.ts +1 -1
- package/dist/core/ServiceTeardown.d.ts.map +1 -1
- package/dist/core/ServiceTeardown.js +4 -2
- package/dist/core/ServiceTeardown.js.map +1 -1
- package/dist/core/StrikeRail.d.ts +3 -0
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +16 -1
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TurnDispositionHandler.d.ts +3 -2
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +48 -15
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/TurnMaterialization.d.ts.map +1 -1
- package/dist/core/TurnMaterialization.js +7 -1
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +4 -0
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +0 -4
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +32 -100
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/TurnSources.sql +1 -1
- package/dist/core/ambient.sql +2 -2
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +67 -47
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/results.d.ts +1 -0
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +7 -0
- package/dist/core/results.js.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/core/unconcluded-emission.d.ts +7 -0
- package/dist/core/unconcluded-emission.d.ts.map +1 -0
- package/dist/core/unconcluded-emission.js +11 -0
- package/dist/core/unconcluded-emission.js.map +1 -0
- package/dist/core/unconcluded-emission.sql +14 -0
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +1 -0
- package/dist/schemes/Log.js.map +1 -1
- package/dist/server/Retention.d.ts.map +1 -1
- package/dist/server/Retention.js +2 -0
- package/dist/server/Retention.js.map +1 -1
- package/dist/server/Retention.sql +5 -0
- package/dist/server/logEntry.sql +2 -2
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +4 -1
- package/dist/service.js.map +1 -1
- package/migrations/006_log.sql +10 -4
- package/package.json +161 -163
package/SPEC.md
CHANGED
|
@@ -527,8 +527,9 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
|
|
|
527
527
|
parent as an `_plurnk` READ of `ops://<name>/<sequence>` ({§loop-answer}), not a message,
|
|
528
528
|
and that row carries what the child said.
|
|
529
529
|
The occurrence retains that loop's exact terminal result; the READ uses ordinary
|
|
530
|
-
bounded projection
|
|
531
|
-
|
|
530
|
+
bounded projection of {§loop-answer}. The original delegated answer reaches the
|
|
531
|
+
parent here, not as a duplicate reply ({§message-reply-delivery}); other message
|
|
532
|
+
replies remain independent deliveries. Failures and
|
|
532
533
|
cancellations retain their exact status, Problem, and visible explanation,
|
|
533
534
|
including a spawn that fails before its first turn. Observation and wake-up
|
|
534
535
|
follow {§env-delta-child-termination}; a later child loop cannot replace the
|
|
@@ -1023,7 +1024,7 @@ boundary.
|
|
|
1023
1024
|
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** The stream first persists its terminal state. A worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) then **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. An already-active worker needs no injected prompt or second wake because its next packet reads the durable terminal state. A concluded worker receives no synthetic loop from ambient stream closure. The result remains available in the stream's own state under every case.
|
|
1024
1025
|
- §worker-lifecycle-child-wake **Each child task completion notifies its parent.** Terminal-task publication, including failure and cancellation of a parked task, notifies the direct parent without injecting a prompt. Other unfinished tasks or streams in that child remain independent obligations; they cannot suppress notification. The parent's eligible waits requeue in place under {§loop-wake-identity} and the bounded {§worker-optimistic-settlement} opportunity. Durable revisioning covers completion-before-park and restart; drain teardown and whole-worker quiescence are not completion identities.
|
|
1025
1026
|
- §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 program completion; separately, a stream conclusion, direct-child conclusion or addressed reply persists and publishes immediately but holds eligible parked loops' `202→100` requeues 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 arrivals within that window into at most one requeue per eligible loop. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. An arrival after provider dispatch begins retains its next wake, while poll, new-request and operator wakes never open this hold. Only packet/provider dispatch waits: durable state, client events, cancellation and the replying program do not. One redaction-safe span records elapsed time, quiescence versus deadline, and arrival count without entering the packet.
|
|
1026
|
-
- §worker-lifecycle-idle-is-concluded **Idle is not unanswered.** An empty WAIT continues; an answered, observed
|
|
1027
|
+
- §worker-lifecycle-idle-is-concluded **Idle is not unanswered.** An empty WAIT continues; an eligible final response with answered messages, observed results and no held work concludes under {§wait-obligation-matrix}. A concluded worker retains durable history; a later addressed arrival starts a new loop.
|
|
1027
1028
|
- §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.
|
|
1028
1029
|
- §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. Wake selection rechecks shutdown and worker cancellation before requeuing each parked loop.
|
|
1029
1030
|
- §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 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}). A pending client interaction also lost its exact awaiting operation, so boot removes the orphan instead of replaying work or inventing a response ({§client-interactions}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation requeues on an unseen completion or when no live obligation remains. Otherwise it stays parked on surviving children; the drain restores inherited stream observation through the same guarded scheduler ({§worker-wait-timing}). 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.
|
|
@@ -1100,11 +1101,18 @@ are excluded from results; the complete note body still distinguishes activity.
|
|
|
1100
1101
|
irrelevant; operation and array order are preserved. Only the configured
|
|
1101
1102
|
`MIN_CYCLES × MAX_CYCLE_PERIOD` history window is retained. Repeated addresses
|
|
1102
1103
|
alone are not a cycle: changing inputs or observations distinguish activity.
|
|
1104
|
+
A turn that executed nothing has no activity to identify, so an empty turn's
|
|
1105
|
+
identity is its **text** ({§empty-turn}) — the same principle, applied to the only
|
|
1106
|
+
output it produced. Identifying it by its absent program instead makes every empty
|
|
1107
|
+
turn identical, and changing words then read as a repeating one.
|
|
1103
1108
|
This is an exact-repetition backstop, not a semantic judgment of task progress;
|
|
1104
1109
|
new asynchronous invocation identities do not prove repetition of their eventual
|
|
1105
1110
|
effects. Ordinary contract strikes and operator budgets remain independent.
|
|
1106
1111
|
|
|
1107
|
-
§provider-recovery **A recoverable provider failure never ends a loop.**
|
|
1112
|
+
§provider-recovery **A recoverable provider failure never ends a loop.** An isolated BARE
|
|
1113
|
+
call ({§bare-inference}) takes the same recovery as the loop's own inference: each re-issue
|
|
1114
|
+
is its own model call on the ledger, and a spent window leaves the operation's result as the
|
|
1115
|
+
provider's exact failure. When a model
|
|
1108
1116
|
call fails with a network failure, rate limit, deadline, or interrupted resource after
|
|
1109
1117
|
the provider's own retries, the turn records the exact Problem as a `_plurnk` row,
|
|
1110
1118
|
notices the client (`engine:provider` / `provider_unavailable`), waits with
|
|
@@ -1137,7 +1145,7 @@ The contracts, and the violation of each that strikes:
|
|
|
1137
1145
|
| Contract | Violation that strikes |
|
|
1138
1146
|
|---|---|
|
|
1139
1147
|
| operation contract | a hard operation failure (status ≥ 400) in an admitted turn — soft statuses below excluded |
|
|
1140
|
-
| review contract | none:
|
|
1148
|
+
| review contract | none: an eligible final response joins live obligations ({§completion-joins-live-work}) or continues to observe results ({§completion-defers-to-results}) |
|
|
1141
1149
|
| progress contract | a detected operation cycle (`MIN_CYCLES` × period), or an admitted turn with no operation ({§empty-turn}) |
|
|
1142
1150
|
| frame contract | emission attempts exhausted with no admissible turn |
|
|
1143
1151
|
| provider response contract | the provider returned an invalid response |
|
|
@@ -1156,6 +1164,17 @@ independent turn ceiling terminates at **429** ({§loop-terminals}). The streak
|
|
|
1156
1164
|
and cycle verdict are absent from model packets; only the concrete occurrences
|
|
1157
1165
|
in the table are shown. The streak never leaves the daemon.
|
|
1158
1166
|
|
|
1167
|
+
A crossing terminal names the source that struck the crossing turn — `repetition`,
|
|
1168
|
+
`no_operation`, then `operation` — in its detail, in that order when a turn matches more
|
|
1169
|
+
than one. The three are not interchangeable: a turn that authored no operation did not *fail*
|
|
1170
|
+
one, and reporting it as a failed turn misreads a model answering without the fence as a model
|
|
1171
|
+
whose operations broke. This is the crossing turn's source, not the streak's composition; the
|
|
1172
|
+
rail rules on the crossing and does not retain the kinds behind it. What the crossing turn
|
|
1173
|
+
actually said is cited, not discarded ({§terminal-evidence}). Naming the source is not the
|
|
1174
|
+
private accounting {§rail-accounting-private} withholds: the streak, the cycle verdict and
|
|
1175
|
+
attempt counts stay inside the daemon — this is the terminal telling the truth about its own
|
|
1176
|
+
cause, which the reader already sees the shape of.
|
|
1177
|
+
|
|
1159
1178
|
§loop-rail-continuity Rail state belongs to the durable loop, not its execution
|
|
1160
1179
|
segment. The strike streak and bounded cycle history survive driver cleanup and
|
|
1161
1180
|
restart; curation of log evidence cannot alter them.
|
|
@@ -1204,7 +1223,21 @@ Three current entry points:
|
|
|
1204
1223
|
|
|
1205
1224
|
### §emission-admission Provider emission admission
|
|
1206
1225
|
|
|
1207
|
-
A completed provider exchange is an **emission attempt**, not necessarily an engine turn.
|
|
1226
|
+
A completed provider exchange is an **emission attempt**, not necessarily an engine turn.
|
|
1227
|
+
The parser owns its boundaries; core admits determinate work and exposes its failures.
|
|
1228
|
+
|
|
1229
|
+
| Parsed response | Admission |
|
|
1230
|
+
|---|---|
|
|
1231
|
+
| Bounded program, including malformed operations | Admit valid operations and record parser failures; with no authored operation, apply {§empty-turn}. |
|
|
1232
|
+
| Outside response text | Keep it as the model's NOTE under {§response-text-note}; never deliver it or infer completion. |
|
|
1233
|
+
| Lost boundary after a closed operation | Admit the closed operations and record the boundary diagnostic under {§unparsed-tail-boundary}. |
|
|
1234
|
+
| Lost boundary before any closed operation | Reject the attempt; neither outside text nor a reasoning NOTE substitutes for a closed response operation. |
|
|
1235
|
+
|
|
1236
|
+
Warnings and closer recovery ({§closer-fallback}) do not reject. `finish=length`
|
|
1237
|
+
discloses truncation and precludes completion; it is not independently a rejection.
|
|
1238
|
+
Provider interruption is owned by {§provider-interrupted-attempt}. Accepted source
|
|
1239
|
+
and positions remain exact; execution follows {§op-execution-order}. WAIT remains
|
|
1240
|
+
optional, with no omission warning or invented operation ({§turn-shape}).
|
|
1208
1241
|
|
|
1209
1242
|
§safe-uri-target-groups After source and authored-command admission, Core tolerates one target group on READ or KILL only when splitting its raw target at top-level comma or whitespace separators produces at least two members and every member independently parses as an explicit `scheme://` URI. Request-metadata blocks are opaque to this split. Each member becomes one ordinary statement with an independent dispatch outcome and log row, in authored member order at that operation's position under {§op-execution-order}. Otherwise the target remains exactly singular, including local filenames containing spaces or commas. The stored `turnOps` and authored command count remain unexpanded, and no other operation admits target groups.
|
|
1210
1243
|
|
|
@@ -1481,7 +1514,7 @@ Registration precedes loop affinity:
|
|
|
1481
1514
|
| Registered but inactive under flag | The flag gate returns `403 scheme-unavailable`. |
|
|
1482
1515
|
| Registered and active | Dispatch continues to the operation owner. |
|
|
1483
1516
|
|
|
1484
|
-
- §op-execution-order **An admitted turn is an ordered program.** Model, client, and harness operations execute in authored order.
|
|
1517
|
+
- §op-execution-order **An admitted turn is an ordered program.** Model, client, and harness operations execute in authored order. WAIT and parameterless KILL are deferred until all other admitted operations settle or establish their explicitly asynchronous work ({§disposition-anywhere}). The complete program then settles under {§wait-obligation-matrix}, whether or not it contains a lifecycle request; no completion operation or inventory is invented. Existing cycle, no-operation, and resource rails remain effective. An observation records the resource state at its execution point; the model sees that receipt in the next packet. Exact submitted source and actual operation outcomes remain durable. Earlier successful effects survive a later operation failure; a producer requesting fail-on-error stops before subsequent operations.
|
|
1485
1518
|
|
|
1486
1519
|
§bare-inference **BARE is isolated, synchronous retrieval over the durable child-provider policy.**
|
|
1487
1520
|
|
|
@@ -2074,7 +2107,7 @@ streaming are independent. No later turn automatically requests reasoning.
|
|
|
2074
2107
|
|
|
2075
2108
|
AST: `{ op: "KILL", target, matcher: MatcherBody | null, lineMarker: TextLineMarker | null, body: null }` ({§kill-scope} and {§matcher-option} in the contracts SPEC own the grammar).
|
|
2076
2109
|
|
|
2077
|
-
KILL deletes context from the **log** (`log:///`, {§packet}). Without a scope it retires the selected rows from the active projection ({§log-history-projection}). With a one-line or inclusive two-line scope it removes only that body's intersecting body-relative physical lines from the readable projection, and the row stays active. An anchor may be one published on that body or one returned by READing its `log:///` coordinate ({§line-anchors}); an anchor absent from the current body selects no line, as with an out-of-bounds numeric line. Scoped KILL is one-way: intervals accumulate, the durable body is untouched, and subsequent access follows {§log-readable-projection}. A scoped KILL on a bodyless row is a friendly 200 no-op with `matched` reported. A KILL that addresses no row is 404 on an exact coordinate and 204 on a sweep ({§log-curation-folder-idiom}). Selection composes target/glob with an optional heading pattern ({§log-curation-set-selection})
|
|
2110
|
+
KILL deletes context from the **log** (`log:///`, {§packet}). Without a scope it retires the selected rows from the active projection ({§log-history-projection}). With a one-line or inclusive two-line scope it removes only that body's intersecting body-relative physical lines from the readable projection, and the row stays active. An anchor may be one published on that body or one returned by READing its `log:///` coordinate ({§line-anchors}); an anchor absent from the current body selects no line, as with an out-of-bounds numeric line. Scoped KILL is one-way: intervals accumulate, the durable body is untouched, and subsequent access follows {§log-readable-projection}. A scoped KILL on a bodyless row is a friendly 200 no-op with `matched` reported. A KILL that addresses no row is 404 on an exact coordinate and 204 on a sweep ({§log-curation-folder-idiom}). Selection composes target/glob with an optional heading pattern ({§log-curation-set-selection}). Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2078
2111
|
|
|
2079
2112
|
A READ carrying active native media is atomic: any KILL scope is ignored and the entire observation is retired, including its native context contribution ({§packet-attachment-parts}). For a model turn, native activity is the attachment selection in its actual input packet; without a model packet, a native observation is atomic by default. Text-only observations in the same selection retain ordinary scoped behavior. Neither form deletes source data or forensic evidence.
|
|
2080
2113
|
|
|
@@ -2105,21 +2138,29 @@ type and projection facts under {§read-bytes}.
|
|
|
2105
2138
|
The `## Log` section is a sequence of ordinary Markdown records separated by one blank line:
|
|
2106
2139
|
|
|
2107
2140
|
```text
|
|
2108
|
-
### log:///<loop>/<turn>/<item>/<leaf>
|
|
2109
|
-
|
|
2141
|
+
### log:///<loop>/<turn>/<item>/<leaf> · <logTokens>
|
|
2142
|
+
OP (operands) <marks> [metadata] <!-- aside -->
|
|
2143
|
+
{"oneLine":"strict JSON result facts"}
|
|
2110
2144
|
<coordinate-prefixed body lines when visible>
|
|
2111
2145
|
```
|
|
2112
2146
|
|
|
2113
|
-
|
|
2147
|
+
| Line | Content | Rule |
|
|
2148
|
+
|---|---|---|
|
|
2149
|
+
| H3 | The row's complete model-facing identity and canonical READ address, then ` · ` and its `logTokens` charge ({§packet-token-accounting}). | Always present; nothing else repeats the identity, the operation, or the charge. |
|
|
2150
|
+
| written | The request as the language writes it ({§heading-slot-order}): the operation or runtime, every operand in the packet's canonical spelling ({§log-address-metadata}), marks, metadata blocks, matcher, aside. No fence, no body. | Present for every operation and execution row; absent on `error` and `extension` rows. |
|
|
2151
|
+
| facts | One strict JSON object of result facts in stable alphabetical order. | Present only when a fact exists; it never re-encodes the written line. |
|
|
2152
|
+
| body | Coordinate-prefixed lines. | Present when the row is visible. |
|
|
2153
|
+
|
|
2154
|
+
The written line is the model's own request, so a matcher such as `/\bhello\b/i` returns exactly as it was written, never JSON-quoted. Absent fields are not invented. Every physical body line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, so source text cannot create a record boundary. The section contains records only, with no leading prose or enclosing fence.
|
|
2114
2155
|
|
|
2115
2156
|
§log-address-metadata **Addresses name their relationship, not the row's producer.**
|
|
2116
2157
|
|
|
2117
|
-
|
|
|
2158
|
+
| Spelling | Meaning | Where |
|
|
2118
2159
|
|---|---|---|
|
|
2119
|
-
| `path` | The operation's addressed operand
|
|
2120
|
-
| `from
|
|
2121
|
-
| `stream` | An executor invocation's separately created output address, never a READ's alternative spelling of
|
|
2122
|
-
| `resource` | A distinct returned resource under {§operation-resource-receipt}. |
|
|
2160
|
+
| `OP (path)` | The operation's addressed operand: read resource, mutation subject, message recipient, or executor operand. Explicit and automatic READs are written identically. Pathless operations are written bare. | Written line |
|
|
2161
|
+
| `COPY (from) <marks> (to) <marks>` | COPY/MOVE's two operand selections, each retaining its optional scope; neither replaces actor attribution. | Written line |
|
|
2162
|
+
| `stream` | An executor invocation's separately created output address, never a READ's alternative spelling of its operand. | Facts |
|
|
2163
|
+
| `resource` | A distinct returned resource under {§operation-resource-receipt}. | Facts |
|
|
2123
2164
|
|
|
2124
2165
|
Nested mutation effects and delivered attachments name their resource with `path`.
|
|
2125
2166
|
These packet spellings do not rename the submitted AST, durable operation results,
|
|
@@ -2186,7 +2227,7 @@ Field absence carries defaults: `origin` is omitted for the owning model, `sourc
|
|
|
2186
2227
|
records the exact READ coordinates sent without controlling retention. Missing immutable bytes are an
|
|
2187
2228
|
internal integrity failure, never silently dropped content. No ejection message or permanent teaching is
|
|
2188
2229
|
added. These stable curation weights are not provider-token measurements ({§tokenomics-render-weight-budget}).
|
|
2189
|
-
- §packet-token-accounting Every row reports one `logTokens` charge: its complete materialized H3,
|
|
2230
|
+
- §packet-token-accounting Every row reports one `logTokens` charge on its H3 ({§log-wire-format}): its complete materialized H3, written request, facts, visible body, and selected native attachment. The completed record is measured to a fixed point, including the accounting field itself. No `tokensBody`, `tokensMetadata`, or `tokensActive` field is serialized. Hidden text is not charged; metadata-only rows still have a reclaimable charge. Source/FIND-item `tokens` measure source content, not the observation's context footprint. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page differs. All use stable curation weights, not provider tokens or dollars. Native component accounting follows {§packet-attachment-parts}; ordinary addressability and truthful errors follow {§log-wire-format}.
|
|
2190
2231
|
|
|
2191
2232
|
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
2192
2233
|
|
|
@@ -2250,7 +2291,7 @@ single line past the end, a reversed range, empty content, a command's log row
|
|
|
2250
2291
|
§rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program. The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, the canonical model-facing `/attempt` leaf, and the exact latest rejected response. The packet does not duplicate that identity as `kind` metadata. It is born durably body-suppressed and projected visibly only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
2251
2292
|
|
|
2252
2293
|
- §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 ```` ```READ (worker:///docs/) ````. A complete `[start-end]` segment in any numeric coordinate slot selects that inclusive decimal interval; brackets elsewhere retain ordinary path-glob meaning. Every rendered row appends one canonical model-facing leaf: the native operation name or invoked executor name, `/attempt` for a rejected emission. An executor leaf is derived from the durable submitted statement (its `runtime`), never an internal dispatch type or the current tool registry. Digits and punctuation in executor names remain part of the leaf. The leaf names identity rather than adding a resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive leaf is authoritative and a disagreement resolves 404. READ anchors use the canonical suffixed identity even when addressed by shorthand. 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`, `log:///**/python3`, and `log:///**/attempt` deliberately filter canonical leaves. Executor outputs instead use workspace-wide claims such as `sh:///ab3d5678#stdout` ({§execution-output-identity}); their source operation has log coordinates, but resource lifetime and identity are independent of that observation. Error pointers, Problem instances, source attribution, and search use this same identity; client stream coordinates retain the numeric triple. Within a turn, sequence is arrival order. Inbound SEND rows publish before the program runs ({§message-arrival}); a turn receiving messages holds the first at `log:///L/T/1/SEND`, followed by further arrivals oldest first, then the model's operations ({§packet-current-turn} names `L/T`).
|
|
2253
|
-
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — KILL takes 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: ```` ```KILL (log:///1/2) <1,-1> ```` suppresses turn 1/2's bodies. A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`.
|
|
2294
|
+
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — KILL takes 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: ```` ```KILL (log:///1/2) <1,-1> ```` suppresses turn 1/2's bodies. A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2254
2295
|
- §log-curation-set-selection **Row selection and body scope are independent** — target/glob and an optional heading pattern (```` ```KILL (log:///**) [{"pattern": "~stale"}] ````, every dialect a FIND over rows accepts) compose by intersection into the affected row set. An optional `<L>` or `<SL,EL>` then intersects each selected canonical body; it never paginates or changes the selected set. Thus ```` ```KILL (log:///**/READ) <17,-1> ```` may change long READs and no-op on short ones while reporting every selected row in `matched`.
|
|
2255
2296
|
|
|
2256
2297
|
§log-kill-meta-operation **A log KILL changes working context, never the underlying resources or execution history.** Receipt visibility depends on the target and result, not the producer, attribution, or age of the turn:
|
|
@@ -2559,7 +2600,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2559
2600
|
SEND AST: `{ op: "SEND", target: ParsedPath | null, body: SendBody | null, metadata, lineMarker }`.
|
|
2560
2601
|
|
|
2561
2602
|
- **Message:** SEND delivers to an actor, endpoint or exact message address. Targetless SEND answers observed Open Messages.
|
|
2562
|
-
- **Workflow:** WAIT yields
|
|
2603
|
+
- **Workflow:** WAIT yields. Parameterless KILL requests successful completion ({§kill-conclusion}). NOTE retains memory.
|
|
2563
2604
|
|
|
2564
2605
|
§worker-obligations A worker holds its unresolved children and open non-detached
|
|
2565
2606
|
streams (`worker_obligations`); `loop_obligations` names them per loop. The
|
|
@@ -2573,17 +2614,20 @@ same durable liveness.
|
|
|
2573
2614
|
| Worker or loop already cancelled/terminal | Preserve that result. |
|
|
2574
2615
|
| Administrative program | Finish its transaction without adjudicating another model loop's work. |
|
|
2575
2616
|
| New unpublished message | Continue; publish it in the next packet. |
|
|
2576
|
-
|
|
|
2577
|
-
| WAIT
|
|
2617
|
+
| Fresh operation/parser failure, without an authored WAIT | Continue before any automatic parking. |
|
|
2618
|
+
| Neither an authored WAIT nor an eligible completion request ({§kill-conclusion}) | Continue, regardless of earlier replies or live work. |
|
|
2619
|
+
| Live work and either WAIT or an eligible completion request | Park the same loop; message arrival, child or stream settlement, or stream cadence wakes it. No final-answer body is delivered while joining. |
|
|
2620
|
+
| WAIT without live work | Continue; never invent a future wake. The first such WAIT is an honest yield and its row says only `Nothing is in flight. Continuing.`; a second in the same loop is the model waiting on a wake nothing can send, so its own row instead names what WAIT is for and what to reach for — `WAIT doesn't wait unless there's a child worker or stream to wait on. Use schedule for specific timing decisions.` The correction rides the operation's own result, which is the surface the model is certain to read (operator, 2026-09-22). |
|
|
2578
2621
|
| Unanswered messages | Continue. |
|
|
2579
2622
|
| Unobserved operation results, failures, child results or stream conclusions | Continue; the next packet presents them. |
|
|
2580
|
-
|
|
|
2623
|
+
| Eligible completion request with no outstanding messages, live work or unobserved results | Conclude successfully. |
|
|
2581
2624
|
|
|
2582
|
-
An empty emission is handled by {§empty-turn}
|
|
2583
|
-
|
|
2584
|
-
require another observation turn
|
|
2625
|
+
An empty emission is handled by {§empty-turn}. Ordinary strikes, cycles and
|
|
2626
|
+
execution limits remain independent. NOTE and successful targeted KILL do not themselves
|
|
2627
|
+
require another observation turn, but neither requests completion. Failed KILL
|
|
2628
|
+
and every other operational result require observation.
|
|
2585
2629
|
|
|
2586
|
-
§loop-response-messages **A response is a recorded delivery.** A successful SEND reply records
|
|
2630
|
+
§loop-response-messages **A response is a recorded delivery.** A successful SEND reply or admitted final KILL answer records
|
|
2587
2631
|
the exact message addresses it answers. All replies remain independently recoverable in
|
|
2588
2632
|
message history, in execution order, regardless of producer. An actor-addressed SEND that
|
|
2589
2633
|
does not answer a message, WAIT, NOTE, asides, inherited rows and ambient observations are
|
|
@@ -2598,7 +2642,7 @@ conclusion carries its execution outcome under {§send-undelivered-child-term},
|
|
|
2598
2642
|
| `NULL` | The model's own terminal or an engine verdict whose exact result already carries the story. | No authorship marker. |
|
|
2599
2643
|
| `cancel` | The structured scope was explicitly cancelled, through the client or worker KILL ({§methods-loop-cancel}). | COLLECT and the termination delta prepend a cancellation marker to the exact Problem's presentation, so cancellation cannot masquerade as a deliverable. The model's prior log rows remain untouched. |
|
|
2600
2644
|
|
|
2601
|
-
The engine's failure terminals — **500** (strike threshold) and **508** (cycle), {§engine-rails} — are never the model's to pick; they are the engine ruling the loop failed. The model answers, waits or cancels its scope; the engine derives the lifecycle outcome from that state.
|
|
2645
|
+
The engine's failure terminals — **500** (strike threshold) and **508** (cycle), {§engine-rails} — are never the model's to pick; they are the engine ruling the loop failed. The model answers, waits or cancels its scope; the engine derives the lifecycle outcome from that state. The ruling never softens, and it never destroys the evidence: every terminal cites what the model last left unconcluded ({§terminal-evidence}).
|
|
2602
2646
|
|
|
2603
2647
|
Disposition outcomes follow {§wait-obligation-matrix},
|
|
2604
2648
|
{§completion-joins-live-work}, and {§completion-defers-to-results}. Strike
|
|
@@ -2615,47 +2659,77 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2615
2659
|
answers its ordinary factual 501 without grafting a guessed recovery onto it.
|
|
2616
2660
|
- §send-response-receipt **A reply records exactly which messages it answers.** A successful
|
|
2617
2661
|
reply carries `answers`, the immutable message addresses it answered, not recipient actors. Targetless SEND
|
|
2618
|
-
answers this loop's published, unanswered messages, oldest first.
|
|
2662
|
+
answers this loop's published, unanswered messages, oldest first. When none remain,
|
|
2663
|
+
a nonempty targetless SEND replies to the loop's original published message, so a
|
|
2664
|
+
follow-up can revise its answer. A targetless SEND with no body content or attachments
|
|
2665
|
+
delivers nothing, carries no `answers`, and cannot erase an earlier reply. An accepted
|
|
2666
|
+
parameterless KILL answer uses the same reply routing and empty-body rules. SEND to an exact message
|
|
2619
2667
|
address answers only that message; SEND to an actor endpoint remains ordinary communication
|
|
2620
2668
|
and answers no assignment implicitly. An unpublished arrival cannot be answered by the
|
|
2621
2669
|
targetless shorthand. Failed delivery answers nothing. Reply accounting reads executed
|
|
2622
2670
|
delivery evidence, never log visibility or the mere existence of a later SEND.
|
|
2623
|
-
- §
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2671
|
+
- §kill-conclusion **Successful completion requires an explicit parameterless KILL.** The response
|
|
2672
|
+
contains exactly one KILL without a target, scope, matcher or metadata, no hard
|
|
2673
|
+
parse error or lost boundary, and was not cut at the provider's output allowance.
|
|
2674
|
+
The operation limit must admit the entire program.
|
|
2675
|
+
SEND, NOTE (outside text included, {§response-text-note}) and log-targeted KILL may
|
|
2676
|
+
accompany it; every other operation requires continuation. This tolerance is unadvertised:
|
|
2677
|
+
model teaching requests KILL alone. Reasoning-side NOTEs remain ordinary notes.
|
|
2678
|
+
An aside is allowed. After the program settles, {§wait-obligation-matrix} admits the
|
|
2679
|
+
completion or returns a non-striking continuation/parking receipt explaining the
|
|
2680
|
+
outstanding condition. Valid sibling operations always execute. Only an admitted
|
|
2681
|
+
KILL delivers its literal body through {§send-response-receipt}; a deferred body
|
|
2682
|
+
remains forensic evidence, never a stored draft to replay automatically. An empty
|
|
2683
|
+
KILL concludes without repeating an already-delivered answer, but cannot abandon an
|
|
2684
|
+
unanswered message. SEND, NOTE and targeted KILL never request successful
|
|
2685
|
+
completion. New arrivals still guard the terminal transition atomically
|
|
2686
|
+
({§completion-defers-to-messages}); an arrival concurrent with an accepted reply
|
|
2687
|
+
remains unanswered and keeps the loop running. No implicit successful exit exists.
|
|
2688
|
+
- §response-text-note **Text outside the operations is the model's NOTE, never delivered.** Each
|
|
2689
|
+
span {§response-text} supplies becomes an ordinary NOTE in source order, unmarked, so the
|
|
2690
|
+
model's own log files its self-narration where it belongs. It is not an authored operation:
|
|
2691
|
+
{§empty-turn} still strikes a turn that holds only text, and the exact emission is retained.
|
|
2692
|
+
Delivered as a SEND, the text read as an answer and confirmed that speaking outside operations
|
|
2693
|
+
works; reported as a count of invalid characters, it sent a model to repair its prose into
|
|
2694
|
+
live operations (`demo-show-dont-run-qdN9u2` executed the KILL it meant to show). A NOTE
|
|
2695
|
+
neither delivers nor concludes (operator, 2026-09-22). This is the far end of the teaching
|
|
2696
|
+
scale: text outside every operation breaks the first rule of `plurnk.md` — *"YOU MUST ONLY
|
|
2697
|
+
use valid Plurnk OPs"* — and takes the largest reinterpretation, while a
|
|
2698
|
+
departure as small as a missing closer is read as meant ({§closer-fallback}).
|
|
2643
2699
|
- §loop-answer **A loop's address is what it said.** READ `ops://<worker>/<loop>` resolves to
|
|
2644
|
-
the latest reply the loop gave to the message that started it:
|
|
2645
|
-
|
|
2646
|
-
ended without one is its terminal problem (404 when it ended 2xx)
|
|
2647
|
-
|
|
2648
|
-
`
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2700
|
+
the latest reply the loop gave to the message that started it: the body of a SEND
|
|
2701
|
+
or accepted final KILL that answered that message. A running loop without one is 425; a loop that
|
|
2702
|
+
ended without one is its terminal problem (404 when it ended 2xx) — and that problem cites what
|
|
2703
|
+
the model last left unconcluded, so the loop's own address never reports silence from a loop that
|
|
2704
|
+
spoke ({§terminal-evidence}). `ops://<worker>/<loop>/<turn>`
|
|
2705
|
+
remains that turn's emission. A concluded child's `loop_termination` row to its parent
|
|
2706
|
+
READs this same loop resource. Witness: `test/intg/loop-answer.test.ts`.
|
|
2707
|
+
- §empty-turn **No authored response operation is a recoverable turn, never completion.**
|
|
2708
|
+
Count parsed response operations before outside-text and reasoning NOTEs join them;
|
|
2709
|
+
neither enters the count. When none exist and no boundary was lost, retain the turn and its raw
|
|
2710
|
+
sources and count one progress-contract strike, whether or not the turn carried text
|
|
2711
|
+
({§response-text-note}). The strike sends no notice of its own; the threshold terminal is
|
|
2712
|
+
where it becomes visible, and it says why ({§engine-rails}). An empty turn uses its exact
|
|
2713
|
+
text as the cycle fingerprint ({§engine-cycle-evidence});
|
|
2714
|
+
different empty programs are not a repeated cycle merely because neither contained
|
|
2715
|
+
operations. Lost-boundary handling remains {§unparsed-tail-boundary}; no confirmation
|
|
2716
|
+
token or private retry is invented here.
|
|
2717
|
+
- §terminal-evidence **A terminal rules the loop over; it does not decide the model said nothing.**
|
|
2718
|
+
Every engine terminal — strike threshold (500), cycle (508), turn ceiling (429), loop timeout
|
|
2719
|
+
(504), provider unavailable ({§provider-recovery}) — keeps its status and its authorship: the
|
|
2720
|
+
engine ruled, the model did not conclude, and no terminal is ever softened into a 200 the model
|
|
2721
|
+
never declared. What a terminal may not do is discard the last thing the model said. When the
|
|
2722
|
+
loop's last inference turn performed no authored operation in either response or
|
|
2723
|
+
reasoning and kept text, its Problem Details
|
|
2724
|
+
carries the extension member `unconcluded`, the
|
|
2725
|
+
`ops://<worker>/<loop>/<turn>` address of that emission. It is a citation, never the bytes
|
|
2726
|
+
({§turn-ops-entry}: the reader READs the source, and an emission of any length never rides
|
|
2727
|
+
wholesale into a parent's packet). The member is named for what it is — an emission left
|
|
2728
|
+
unconcluded — and never `answer`: the harness cannot warrant that text is complete or final,
|
|
2729
|
+
because it did not conclude under {§kill-conclusion}. Earlier deliveries remain delivered;
|
|
2730
|
+
the citation neither sends them again nor destroys them. A terminal whose last inference
|
|
2731
|
+
turn performed authored operations carries no `unconcluded`: an absent member is not an
|
|
2732
|
+
empty one. Attachment is owned by the one terminal seam.
|
|
2659
2733
|
- §metadata-ignored **Options a scheme does not take are dropped, not refused.** A READ, FIND,
|
|
2660
2734
|
EDIT or KILL carrying `[metadata]` for a scheme whose manifest takes none runs without it,
|
|
2661
2735
|
and the packet carries one `metadata_ignored` notice naming the scheme (operator,
|
|
@@ -2663,40 +2737,26 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2663
2737
|
path; it is lifted into the matcher at parse time ({§matcher-option}). SEND recipients,
|
|
2664
2738
|
executions, WORK and FORK own their input and receive it whole ({§send-resource-attachments},
|
|
2665
2739
|
{§env-option}); a key they do not take is their own 400.
|
|
2666
|
-
- §send-looks-like-operation **A reply never begins with an operation heading.** When a model's
|
|
2667
|
-
untargeted SEND has, as its first non-blank line, a line that parses alone as one clean
|
|
2668
|
-
heading naming an operation this worker could perform — a Plurnk operation, or a registered
|
|
2669
|
-
executor or MCP service — dispatch refuses it 400 `send-looks-like-operation`, naming the
|
|
2670
|
-
`heading`, and delivers nothing. Since the fences chapter's unlabeled-fence SEND was retired
|
|
2671
|
-
({§interstitial-fence}), this guards only an explicit `SEND` block; a heading written outside
|
|
2672
|
-
any fence is prose with the parser's own advisory ({§bare-heading-advisory}), and an emission
|
|
2673
|
-
made only of such lines is an empty turn carrying those advisories ({§empty-turn}). This is
|
|
2674
|
-
admission, not promotion: the line is never run as the operation it resembles, and the neutral
|
|
2675
|
-
recovery says only where each intent belongs (an operation on the fence line, a quoted example
|
|
2676
|
-
inside a delimited SEND body). A first line that does not parse alone (prose after the word), a
|
|
2677
|
-
name no registry knows, or an inner fence is an ordinary reply; so is a heading whose
|
|
2678
|
-
only irregularity is a multi-word sigil-less matcher after the path (`READ (belfry.md)
|
|
2679
|
-
returned nothing because the file is empty.`), which {§naked-pattern} would otherwise lift
|
|
2680
|
-
as a literal — on a reply's first line that is prose. Origin: the 2026-09-11 dogfood,
|
|
2681
|
-
where four operations on the line after their fences were delivered as four 200 replies and the
|
|
2682
|
-
loop then parked fifteen minutes on receipts that could never arrive.
|
|
2683
2740
|
- §send-idle-turn **NOTE is memory, not a yield.** NOTE does not imply parking.
|
|
2684
|
-
|
|
2685
|
-
|
|
2741
|
+
A NOTE-only response does not request completion, even after every message is answered.
|
|
2742
|
+
Repetition remains subject to {§engine-cycle-evidence}.
|
|
2686
2743
|
- §send-premature-terminate **Completion follows observation.** Every fired operation except
|
|
2687
2744
|
SEND, NOTE, WAIT and successful KILL requires a subsequent packet. This barrier uses durable
|
|
2688
2745
|
executed evidence, not curated rows. Fast completion, an empty result or curation cannot
|
|
2689
2746
|
erase it. New arrivals are protected by {§completion-defers-to-messages}; no terminal verb
|
|
2690
2747
|
or prose overrides this rule.
|
|
2691
|
-
- §completion-joins-live-work **
|
|
2692
|
-
|
|
2693
|
-
as WAIT does.
|
|
2694
|
-
|
|
2748
|
+
- §completion-joins-live-work **A completion request joins its live obligations.** An eligible
|
|
2749
|
+
parameterless KILL parks on live children or
|
|
2750
|
+
non-detached streams, as WAIT does. Ordinary programs continue regardless of earlier
|
|
2751
|
+
replies. Each wake presents the newly settled state; the next program expresses its
|
|
2752
|
+
own disposition. Only targeted KILL owns cancellation; parameterless KILL never
|
|
2753
|
+
cancels work and delivers no final answer while joining.
|
|
2695
2754
|
- §completion-defers-to-results **Results keep the loop running until observed.** Same-turn
|
|
2696
2755
|
operations and failures, plus undelivered child or stream conclusions, require the next
|
|
2697
|
-
packet. This is ordinary continuation, not a strike
|
|
2698
|
-
|
|
2699
|
-
|
|
2756
|
+
packet. This is ordinary continuation, not a strike. A premature parameterless KILL
|
|
2757
|
+
receives a factual continuation receipt without delivering its body. Earlier SEND
|
|
2758
|
+
replies remain delivered. If observation warrants no further work or revision, a
|
|
2759
|
+
lone empty parameterless KILL requests completion without repeating an earlier answer.
|
|
2700
2760
|
- §send-administrative-terminal **Administrative programs close their own transaction.**
|
|
2701
2761
|
Their caller closes the administrative loop after execution; no terminal operation is
|
|
2702
2762
|
manufactured. Initialization runs in the model loop without concluding it.
|
|
@@ -2951,14 +3011,14 @@ two states and no others:
|
|
|
2951
3011
|
| state | what the model receives |
|
|
2952
3012
|
|---|---|
|
|
2953
3013
|
| active | nothing in the Log. The `## Delegation` stream pointer names the stream with each channel's size and its growth since the last packet ({§child-orientation}); the model READs any range it wants, and every READ of a stream channel carries `terminal: false` while it runs and `terminal: true` once it has concluded, so an empty page is never mistaken for a finished command that printed nothing (operator, 2026-09-13). |
|
|
2954
|
-
| terminal | ONE `origin=_plurnk` READ at the execution's channel address, born visible, that is exactly a markerless READ of the channel — its bounded first page ({§read-selection-projection}, the whole channel when it fits, the channel's own mimetype), the `range` or `region`, terminal status and Problem, `terminal: true`, and any producer-supplied integer `exitCode`. The packet
|
|
3014
|
+
| terminal | ONE `origin=_plurnk` READ at the execution's channel address, born visible, that is exactly a markerless READ of the channel — its bounded first page ({§read-selection-projection}, the whole channel when it fits, the channel's own mimetype), the `range` or `region`, terminal status and Problem, `terminal: true`, and any producer-supplied integer `exitCode`. The packet writes the read resource as its operand, exactly as an explicit READ does ({§log-address-metadata}). |
|
|
2955
3015
|
|
|
2956
3016
|
§stream-observation-result **One liveness fact.** The durable READ result owns
|
|
2957
3017
|
`terminal`, derived from its selected channel's state, for explicit and automatic
|
|
2958
3018
|
observations alike, independently of mimetype: `active` gives false, `closed` or
|
|
2959
3019
|
`errored` gives true, and `static` has no streaming liveness field. Packet
|
|
2960
|
-
projection preserves that Boolean
|
|
2961
|
-
integer `exitCode`, even for an empty body. An automatic observation's atomic
|
|
3020
|
+
projection preserves that Boolean, any included
|
|
3021
|
+
integer `exitCode`, and a producer's `page` receipt ({§executor-page-receipt}), even for an empty body. An automatic observation's atomic
|
|
2962
3022
|
publication transition consumes the same result flag; private log attributes
|
|
2963
3023
|
retain only the publication offset, not a second liveness value.
|
|
2964
3024
|
|
|
@@ -3334,10 +3394,10 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
3334
3394
|
| §db-process-triggers Processes beside their owners | A trigger that writes rows — a cascade, a capture, an ambient event, a publication cursor, a landed curation — is a process, not shape. It is declared as an `-- INIT: <trigger name>` block in the `.sql` file beside the statements that fire it (`ambient.sql` for the ambient feed, `LoopLifecycle.sql`, `Turn.sql`, `Engine.sql` for model calls, `_entry-crud.sql`, `Log.sql`, `ChannelWrite.sql`), as `DROP TRIGGER IF EXISTS` then `CREATE TRIGGER`, so the definition is current on every open of a database whose shape is current. `MIGRATE` always precedes `INIT` and `INIT` runs on the writer only, so a process may reference any table regardless of file order and never runs on the read pool. `test/intg/schema-composition.test.ts` fails on a baseline trigger that writes, an `INIT` trigger that only guards, a block not named after its trigger or not dropping first, and a live trigger set that differs from the declared set after a first and a second open. |
|
|
3335
3395
|
| §db-fk-indexes Foreign-key check paths | Every foreign-key column a delete, cascade, or parent replacement can check carries an index (partial where the column is nullable), and no registry statement's plan scans a growing table: `test/intg/schema-query-plans.test.ts` runs `EXPLAIN QUERY PLAN` over every `-- PREP` statement against the baseline and fails on a `SCAN` of a growing table, except statements that read a whole table by design (digest, startup recovery, whole-workspace listings, scheduled-loop claims). An index claim is a plan, never a grep of index names. |
|
|
3336
3396
|
| §db-index-owners Every index has an owner | An explicit index earns its place one of three ways: a registry statement's plan uses it, its leading column is a foreign key whose check it serves, or it enforces uniqueness. The same test fails on any other index, naming it: an index nobody reads is a write on every insert. Duplicates of a `UNIQUE` constraint's own index and sort-only indexes no plan selects were removed on this rule; a column no statement reads (`symbol_refs.col`, `ambient_events.created_at`) is not stored. |
|
|
3337
|
-
| §db-maintenance-optimize Statistics at shutdown | The daemon's last database step before the caller closes SQLite is `PRAGMA optimize` on the writer (`maintenance_optimize`), so `sqlite_stat1` reflects tables the connection planned against, bounded by SQLite's own analysis limit; a failure there is a reported shutdown error, never silent. Retention runs before it under the operator's policy ({§retention-policy})
|
|
3397
|
+
| §db-maintenance-optimize Statistics at shutdown | The daemon's last database step before the caller closes SQLite is `PRAGMA optimize` on the writer (`maintenance_optimize`), so `sqlite_stat1` reflects tables the connection planned against, bounded by SQLite's own analysis limit; a failure there is a reported shutdown error, never silent. Retention runs before it under the operator's policy ({§retention-policy}) and ends with a WAL truncation ({§db-space-reclamation}); no periodic `ANALYZE` runs. |
|
|
3338
3398
|
| §db-space-reclamation The daemon keeps its own file healthy | `PLURNK_SERVICE_AUTO_VACUUM` (`incremental`, the default, or `none`) names the mode the daemon keeps its file in. At start, before any drain, a database in another mode is converted (set the mode, one `VACUUM`, which rewrites the file and needs free disk about its size) and the journal says so with page counts before and after. Under `incremental`, every retention pass ends by stepping `PRAGMA incremental_vacuum` to completion once free pages reach `PLURNK_SERVICE_RECLAIM_MIN_FREE_BYTES` (0, the default, = every pass), and reports `reclaimedPages`; below the floor, free pages stay for SQLite to reuse. Under `none` the file never shrinks and freed pages are reused. No operator step is involved beyond the knobs. The WAL stays bounded by SQLite's automatic checkpoint (#764). |
|
|
3339
3399
|
| §content-store Every body is stored once | `contents` holds each settled body once, addressed by its SHA-256, however many channels, workspaces, forks or derivations carry it; rows are immutable. `entry_channel_rows` points a settled channel at its body and keeps an active stream's body as a private buffer until it settles, when it is interned. Every reader and writer uses the `entry_channels` view, whose `INSTEAD OF` triggers intern bodies, refuse a bound `content_hash` that is not the content's, and write each column group only when it changed, so a search attachment is never a representation write. SQLite counts no changes for a view, so a write that must know whether its channel exists returns the channel's name; an outer join cannot flatten the view, so the two statements that need one read `entry_channel_rows` and `contents` directly. `derivation_fts` is an external-content index over `derivation_texts` (a derivation joined to its body); `derivations.content_id` names the indexed text, and the triggers in `_entry-fts.sql` move the index with it and forget it on delete. A body no channel holds and no derivation indexes is collected by retention under `PLURNK_SERVICE_COLLECT_CONTENTS` (1). Witnesses: `test/intg/retention.test.ts`, `test/intg/entries.test.ts`, `test/intg/fulltext-index.test.ts`. |
|
|
3340
|
-
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon construction (the two storage knobs are {§db-space-reclamation}) and runs four set statements in dependency order — on `PLURNK_SERVICE_RETENTION_INTERVAL_MS` cadence while the daemon runs (0 = shutdown only) and once more at shutdown before `PRAGMA optimize`. `PLURNK_SERVICE_RETAIN_PACKET_TURNS` (-1 = every packet) and `PLURNK_SERVICE_RETAIN_PACKET_MS` (-1 = no age limit) retire a completed turn's packet composition (`turn_sections`, {§packet-items}) once it is beyond the newest N packet-bearing turns of its loop or older than the age; the turn, its bag, its log rows and its accounting stay, and an open turn is never retired. `PLURNK_SERVICE_COLLECT_PACKET_ITEMS` (1) collects items no composition references. `PLURNK_SERVICE_COLLECT_CONTENTS` (1) collects stored bodies nothing holds ({§content-store}), after the collectors that release them. `PLURNK_SERVICE_COLLECT_DERIVATIONS` (1) collects derivations no channel, turn source, or log row cites — superseded editions — with their symbols (cascade) and their full-text shadow (`derivations_delete_fts`, a process trigger beside the FTS statements, on every delete path). `PLURNK_SERVICE_RETAIN_RESPONSE_TURNS` (-1) and `PLURNK_SERVICE_RETAIN_RESPONSE_MS` (
|
|
3400
|
+
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon construction (the two storage knobs are {§db-space-reclamation}) and runs four set statements in dependency order — on `PLURNK_SERVICE_RETENTION_INTERVAL_MS` cadence while the daemon runs (0 = shutdown only) and once more at shutdown before `PRAGMA optimize`. `PLURNK_SERVICE_RETAIN_PACKET_TURNS` (-1 = every packet) and `PLURNK_SERVICE_RETAIN_PACKET_MS` (thirty days; -1 = no age limit) retire a completed turn's packet composition (`turn_sections`, {§packet-items}) once it is beyond the newest N packet-bearing turns of its loop or older than the age; the turn, its bag, its log rows and its accounting stay, and an open turn is never retired. `PLURNK_SERVICE_COLLECT_PACKET_ITEMS` (1) collects items no composition references. `PLURNK_SERVICE_COLLECT_CONTENTS` (1) collects stored bodies nothing holds ({§content-store}), after the collectors that release them. `PLURNK_SERVICE_COLLECT_DERIVATIONS` (1) collects derivations no channel, turn source, or log row cites — superseded editions — with their symbols (cascade) and their full-text shadow (`derivations_delete_fts`, a process trigger beside the FTS statements, on every delete path). `PLURNK_SERVICE_RETAIN_RESPONSE_TURNS` (-1) and `PLURNK_SERVICE_RETAIN_RESPONSE_MS` (thirty days) retire a settled call's response body (`model_call_responses`) once it is beyond the newest N body-bearing calls of its loop or its turn is older than the age; the call's identity, failure, capacity, admission and accounting stay, and the digest renders such a call request-only. Under the shipped defaults the durable record is kept forever, while packets and response bodies — transient evidence — are collected after thirty days, so a daemon left running for months stops growing (#788). A malformed knob refuses daemon construction. Witness: `test/intg/retention.test.ts`. |
|
|
3341
3401
|
|
|
3342
3402
|
- DDL = storage truth; JSON Schemas = wire truth. They are allowed to differ where ergonomics demand.
|
|
3343
3403
|
- §entry-identity-no-null **Identity components are never NULL.** `(workspace_id, scheme, authority, pathname)` is a unique key. `workspace_id` references the workspace directly with cascading deletion. Namespace schemes use empty authority; resource schemes retain their canonical authority. File members use nonempty `scheme="file"` and render as bare paths. Registration refuses `storedScheme: null`.
|
|
@@ -3750,7 +3810,10 @@ The database may be released only after the final settlement barrier resolves.
|
|
|
3750
3810
|
(`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
|
|
3751
3811
|
is abandoned with a named error instead of hanging the daemon on a child that
|
|
3752
3812
|
never closes. A wedged child costs a forced shutdown; it must never cost an
|
|
3753
|
-
unbounded one.
|
|
3813
|
+
unbounded one. When the teardown settles, success or failure, the process ends
|
|
3814
|
+
itself: `0` after a clean teardown, `1` after a reported one. A handle an abandoned
|
|
3815
|
+
wait left alive never keeps a stopped daemon running; the supervisor's kill is a
|
|
3816
|
+
backstop, not the exit.
|
|
3754
3817
|
|
|
3755
3818
|
```mermaid
|
|
3756
3819
|
flowchart LR
|
|
@@ -4487,7 +4550,7 @@ flowchart TD
|
|
|
4487
4550
|
| Status | Outcome |
|
|
4488
4551
|
|---|---|
|
|
4489
4552
|
| 100 / 102 | Queued / running |
|
|
4490
|
-
| 202 | WAIT or
|
|
4553
|
+
| 202 | WAIT or an eligible final response joining a live obligation ({§wait-obligation-matrix}, {§worker-wait-timing}) |
|
|
4491
4554
|
| 200 | Messages answered, results observed, held work settled |
|
|
4492
4555
|
| 499 | Worker-scope or client cancellation |
|
|
4493
4556
|
| 429 | Turn allowance exhausted |
|
|
@@ -4496,7 +4559,9 @@ flowchart TD
|
|
|
4496
4559
|
| 504 | Loop timeout or exec-timeout restamp |
|
|
4497
4560
|
|
|
4498
4561
|
An empty WAIT continues at 102. The exact terminal result retains its Problem;
|
|
4499
|
-
status classes are not catch-all replacements for that evidence.
|
|
4562
|
+
status classes are not catch-all replacements for that evidence. No failing status
|
|
4563
|
+
here is ever softened because the model wrote something the harness could not read;
|
|
4564
|
+
every one of them cites it instead ({§terminal-evidence}).
|
|
4500
4565
|
|
|
4501
4566
|
### §env-delta The environment delta: what changed since the model last looked
|
|
4502
4567
|
|
|
@@ -4549,8 +4614,8 @@ ordinary operation evidence still reaches that child's direct parent.
|
|
|
4549
4614
|
|
|
4550
4615
|
| Producer / event | Durable occurrence | Observer projection |
|
|
4551
4616
|
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
4552
|
-
| §env-delta-child-activity Direct-child activity | Child-authored final EDIT, COPY, MOVE, SEND, executor invocation, WORK, FORK, and non-log KILL receipts, including failures. `_plurnk` initialization, maintenance, and operation turns stay with the worker. A reply already delivered to the parent uses its reply occurrence instead ({§message-reply-delivery}). | Direct parent only; one exact attributed row born body-suppressed. Incoming message projections ({§message-arrival}), NOTE, READ (including executor-output READs), FIND, BARE, WAIT, and log KILL never create activity occurrences. Provider reasoning, calls, rejected emissions, and turn sources do not cross automatically. |
|
|
4553
|
-
| §env-delta-child-termination Direct-child termination | The child's exact terminal loop result, except loops containing only `_plurnk` operation or maintenance turns. A conclusion before the first turn still reports, including failed spawns. `source` names the actor; the READ
|
|
4617
|
+
| §env-delta-child-activity Direct-child activity | Child-authored final EDIT, COPY, MOVE, SEND, executor invocation, WORK, FORK, and targeted non-log KILL receipts, including failures. `_plurnk` initialization, maintenance, and operation turns stay with the worker. A reply already delivered to the parent uses its reply occurrence instead ({§message-reply-delivery}). | Direct parent only; one exact attributed row born body-suppressed. Incoming message projections ({§message-arrival}), successful targetless SEND without delivery ({§send-response-receipt}), NOTE, READ (including executor-output READs), FIND, BARE, WAIT, parameterless KILL, and log KILL never create activity occurrences. Provider reasoning, calls, rejected emissions, and turn sources do not cross automatically. |
|
|
4618
|
+
| §env-delta-child-termination Direct-child termination | The child's exact terminal loop result, except loops containing only `_plurnk` operation or maintenance turns. A conclusion before the first turn still reports, including failed spawns. `source` names the actor; the READ selects the exact loop ({§loop-answer}). | Direct parent only; bounded, initially visible READ under {§worker-scheme-collect}, never the child's potentially newer loop. Excluded administrative loops create no pending child-result edge. |
|
|
4554
4619
|
| §env-delta-commons-mutation Commons mutation | One successful resolved operation whose landed effects touch `worker:///...`. | Every existing worker; one body-suppressed row per observer, deduplicated with any lineage audience. |
|
|
4555
4620
|
| §env-delta-filesystem-narration Project-file divergence | Runtime-owned reconciliation evidence remains in the runtime actor's own log. | No ambient observer row. Current content remains addressable and stale hash edits reject at their owned boundary. |
|
|
4556
4621
|
| §env-delta-entry-materialization Executor `entry()` sink | The runtime records typed materialization evidence under its owning actor. | No ambient observer row unless the resulting operation itself is direct-child activity or a commons mutation ({§exec-entry-sink}). |
|
|
@@ -4718,7 +4783,7 @@ Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualifie
|
|
|
4718
4783
|
| inbound `SEND` from outside the workspace | budgeted head under {§message-projection} |
|
|
4719
4784
|
| structured `EDIT` receipt or textual `COPY`/`MOVE` effects | complete receipt-owned join context |
|
|
4720
4785
|
| every other nonempty body | head bounded independently by `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS` |
|
|
4721
|
-
| bodyless row |
|
|
4786
|
+
| bodyless row | heading, written request, and any facts; no coordinate lines; `logTokens` includes any selected native part |
|
|
4722
4787
|
|
|
4723
4788
|
§markerless-first-page **Every markerless retrieval takes the same implicit marker.** A marker's
|
|
4724
4789
|
unit is whatever its projection counts, so `PLURNK_SERVICE_PREVIEW_LINES` is the first page of
|
|
@@ -4743,8 +4808,8 @@ READ and FIND own their range or pagination before packet rendering; the packet
|
|
|
4743
4808
|
|
|
4744
4809
|
Every accepted message enters its recipient loop's inbox in arrival order, with its selected
|
|
4745
4810
|
paths, and publishes exactly once at the next turn boundary. **Open Messages** lists the
|
|
4746
|
-
unanswered messages by their immutable source address (`path`) and
|
|
4747
|
-
not a log coordinate. Each arrival receipt's `resource` names that same retained source.
|
|
4811
|
+
unanswered messages by their immutable source address (`path`) and their sender — a causal
|
|
4812
|
+
`source`, or `"origin": "user"` for the operator's own ({§message-causal-source}) — not a log coordinate. Each arrival receipt's `resource` names that same retained source.
|
|
4748
4813
|
Trusted protocol modules supply message addresses in their own scheme;
|
|
4749
4814
|
native arrivals use `message://<recipient>/<opaque-id>`, separate from the worker's
|
|
4750
4815
|
actor and scratch addresses. Ordinary worker scratch remains writable. Source bodies
|
|
@@ -4758,15 +4823,17 @@ another admission is a 409 conflict, not a second message or an implicit content
|
|
|
4758
4823
|
| Audience | Delivery | Effect |
|
|
4759
4824
|
|---|---|---|
|
|
4760
4825
|
| Assigned worker | Its conversation, even when another actor answered | Visible reply; wakes eligible parked work without a new Open Message or loop. |
|
|
4761
|
-
| Original native sender | That worker, if distinct from the assigned worker |
|
|
4826
|
+
| Original native sender | That worker, if distinct from the assigned worker; original delegated-task answers use {§worker-scheme-collect} instead | Other replies use the same wake and observation path. |
|
|
4762
4827
|
| Exterior sender | The assigned conversation's protocol adapter | The adapter delivers the answer through its standard message channel. |
|
|
4763
4828
|
| Replying actor | Its own executed SEND | No duplicate ambient occurrence. |
|
|
4764
4829
|
|
|
4765
4830
|
The successful SEND and its addressed occurrences commit together. Reply occurrences use
|
|
4766
4831
|
the ordinary durable ambient cursor and wake revision; curation cannot revoke delivery or
|
|
4767
4832
|
replay it. An addressed reply replaces the same parent's generic activity observation.
|
|
4768
|
-
|
|
4769
|
-
|
|
4833
|
+
The child's reply to its original parent-delegated message reaches that parent once,
|
|
4834
|
+
through the conclusion READ under {§worker-scheme-collect}; it is not a separate reply
|
|
4835
|
+
occurrence. Other replies remain ordinary messages. The exact loop remains addressable
|
|
4836
|
+
under {§loop-answer}.
|
|
4770
4837
|
Unobserved replies prevent conclusion just as unobserved child results do. All operation
|
|
4771
4838
|
producers notify the same settlement path after durable execution; reply wake-up shares
|
|
4772
4839
|
{§worker-optimistic-settlement}, without delaying the replying program.
|
|
@@ -4780,7 +4847,7 @@ additional alias. Answering either reaches the same message. Origin (operator, 2
|
|
|
4780
4847
|
packet showed a 77-character `agui://anonymous/threads/…/messages/<uuid>` twice per open message,
|
|
4781
4848
|
while the docs taught the short form.
|
|
4782
4849
|
|
|
4783
|
-
§message-causal-source **Message authorship and delivery are distinct facts.** The harness publishes every arrival row; the row's `source` carries the canonical address of the causal actor. Native WORK, FORK, and directed worker SEND derive `worker://<sender>` from the authenticated sender worker ID. A trusted exterior adapter supplies its own canonical actor address through {§methods-loop-run}: the AG-UI bridge names the client's message under `agui://` ({§agui-run-source}), the inbound A2A adapter under `a2a://`. An absent source
|
|
4850
|
+
§message-causal-source **Message authorship and delivery are distinct facts.** The harness publishes every arrival row; the row's `source` carries the canonical address of the causal actor. Native WORK, FORK, and directed worker SEND derive `worker://<sender>` from the authenticated sender worker ID. A trusted exterior adapter supplies its own canonical actor address through {§methods-loop-run}: the AG-UI bridge names the client's message under `agui://` ({§agui-run-source}), the inbound A2A adapter under `a2a://`. An absent source is the operator. Attribution persists with the message through the inbox, parking, orphan recovery, restart, and later log projection; model syntax cannot author it. The wire renders the row's `source` in place of its `origin`, which is constant for every arrival, except where the source is the transport that minted this very message, which says nothing the address does not ({§message-short-identity}). The operator's message is then the one arrival with no sender to show, so it renders `"origin": "user"`: left bare, it read as the model's own SEND, and a model that had finished the work could no longer find the request it was answering (operator, 2026-09-22). The Open Messages pointer carries the same attribution ({§message-arrival}).
|
|
4784
4851
|
|
|
4785
4852
|
§message-projection **Message storage is unbounded by model context; automatic materialization is not.** Core persists every accepted message 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 the visible bodies of arrivals other than a peer worker's — every `source` that is not a `worker://` address, the loop's own assignment included. Complete bodies render when their aggregate weight fits. Otherwise all such visible 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 `preview` under {§packet-extent-metadata}. The row remains complete and READable by coordinate; its `log:///` body additionally obeys deliberate curation under {§log-readable-projection}. A peer worker's message takes the ordinary bounds. When provider input capacity is unknown the percentage is underivable, so arrival rows retain the ordinary bounded projection rather than inventing capacity. This policy never rejects, summarizes, or discards a message because it exceeds a context window.
|
|
4786
4853
|
|
|
@@ -5388,8 +5455,8 @@ only when its content is already source-numbered, such as an effect receipt.
|
|
|
5388
5455
|
An EDIT or scoped entry KILL log row renders its bounded effect receipt (`rx.receipt`) as row
|
|
5389
5456
|
metadata and join context, not its input statement. Proposal-gated file EDITs
|
|
5390
5457
|
compute the accepted receipt from what actually lands. Environment-delta EDITs
|
|
5391
|
-
render their resulting `rx.span`. COPY/MOVE rows
|
|
5392
|
-
|
|
5458
|
+
render their resulting `rx.span`. COPY/MOVE rows write both operand
|
|
5459
|
+
selections ({§log-address-metadata}), render compact ordered `effects` metadata, and
|
|
5393
5460
|
any scoped textual receipt contexts under their `log:///` address, never under
|
|
5394
5461
|
one operand's resource address. All generated bodies remain under
|
|
5395
5462
|
{§body-projection}. {§edit-result-render}
|
|
@@ -5483,7 +5550,6 @@ database is a benchmark artifact like any other: the lane's run directory lives
|
|
|
5483
5550
|
checkout holds source only — never run output. `test:intg` stamps `PLURNK_TEST_RUN` once and every
|
|
5484
5551
|
test process inherits it, so one suite's databases land in one directory without a pretest step, a
|
|
5485
5552
|
marker file or a sweep; an unstamped invocation is not a special case with its own rules, it is
|
|
5486
|
-
simply an unstamped run with its own directory.
|
|
5487
|
-
|
|
5488
|
-
it. A cross-package test may reuse Core's migration fixture only by passing a path inside the
|
|
5553
|
+
simply an unstamped run with its own directory. A stamped run that passes is reclaimed when it
|
|
5554
|
+
exits; a failed suite's evidence is never touched and stays exactly where the run reported it. A cross-package test may reuse Core's migration fixture only by passing a path inside the
|
|
5489
5555
|
caller's own run directory.
|