@plurnk/plurnk-service 1.4.0 → 1.6.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 +37 -31
- package/INSTALL.md +16 -11
- package/README.md +1 -0
- package/SPEC.md +631 -397
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +2 -3
- package/dist/Paths.js.map +1 -1
- 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 +5 -1
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +4 -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/line-marker.d.ts +7 -3
- package/dist/content/line-marker.d.ts.map +1 -1
- package/dist/content/line-marker.js +2 -1
- package/dist/content/line-marker.js.map +1 -1
- package/dist/content/matcher.d.ts +1 -1
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +2 -2
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/read-projector.d.ts +20 -0
- package/dist/content/read-projector.d.ts.map +1 -0
- package/dist/content/read-projector.js +122 -0
- package/dist/content/read-projector.js.map +1 -0
- package/dist/content/read-resolve.d.ts +2 -7
- package/dist/content/read-resolve.d.ts.map +1 -1
- package/dist/content/read-resolve.js +32 -107
- package/dist/content/read-resolve.js.map +1 -1
- package/dist/core/BranchReceipt.d.ts +0 -1
- package/dist/core/BranchReceipt.d.ts.map +1 -1
- package/dist/core/BranchReceipt.js +0 -3
- package/dist/core/BranchReceipt.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.d.ts +3 -0
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +3 -3
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +4 -3
- package/dist/core/CoreSchemeServices.d.ts +10 -2
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +14 -8
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +656 -312
- 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 +24 -18
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +620 -392
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +188 -119
- 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 +4 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +46 -2
- 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/LoopLifecycle.d.ts +1 -2
- package/dist/core/LoopLifecycle.d.ts.map +1 -1
- package/dist/core/LoopLifecycle.js +1 -4
- package/dist/core/LoopLifecycle.js.map +1 -1
- package/dist/core/LoopLifecycle.sql +1 -4
- package/dist/core/ModelCall.d.ts +27 -0
- package/dist/core/ModelCall.d.ts.map +1 -0
- package/dist/core/ModelCall.js +125 -0
- package/dist/core/ModelCall.js.map +1 -0
- package/dist/core/PacketBuilder.d.ts +13 -12
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +146 -118
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProblemLog.d.ts.map +1 -1
- package/dist/core/ProblemLog.js +1 -0
- package/dist/core/ProblemLog.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +3 -4
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ProviderInstantiate.d.ts.map +1 -1
- package/dist/core/ProviderInstantiate.js +23 -6
- package/dist/core/ProviderInstantiate.js.map +1 -1
- package/dist/core/SchemeRegistry.js +7 -7
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/StrikeRail.d.ts +5 -2
- package/dist/core/StrikeRail.d.ts.map +1 -1
- package/dist/core/StrikeRail.js +10 -7
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/TerminalResult.d.ts +16 -0
- package/dist/core/TerminalResult.d.ts.map +1 -0
- package/dist/core/TerminalResult.js +49 -0
- package/dist/core/TerminalResult.js.map +1 -0
- package/dist/core/caps/CapsResolve.d.ts +5 -1
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +9 -5
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +6 -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 +21 -8
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +6 -4
- package/dist/core/caps/DbNotifyCaps.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/DbSubscriptionCaps.d.ts +4 -6
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +27 -13
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +11 -3
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +10 -8
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- 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 +35 -14
- package/dist/core/git-iso.d.ts +3 -2
- package/dist/core/git-iso.d.ts.map +1 -1
- package/dist/core/git-iso.js +27 -12
- package/dist/core/git-iso.js.map +1 -1
- package/dist/core/git-membership.d.ts +1 -0
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +87 -26
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +8 -1
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +36 -17
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/namespace.d.ts +1 -0
- package/dist/core/namespace.d.ts.map +1 -1
- package/dist/core/namespace.js +20 -0
- package/dist/core/namespace.js.map +1 -1
- package/dist/core/optimistic-settlement.d.ts +2 -0
- package/dist/core/optimistic-settlement.d.ts.map +1 -0
- package/dist/core/optimistic-settlement.js +14 -0
- package/dist/core/optimistic-settlement.js.map +1 -0
- package/dist/core/packet-wire.d.ts +5 -2
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +318 -114
- 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/results.d.ts +4 -2
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +6 -0
- package/dist/core/results.js.map +1 -1
- package/dist/core/scheme-types.d.ts +0 -12
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js.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/token-ruler.js +1 -1
- package/dist/core/token-ruler.js.map +1 -1
- package/dist/core/turn-scheduler.js +1 -1
- package/dist/core/turn-scheduler.js.map +1 -1
- package/dist/core/worker-ops.sql +4 -4
- package/dist/core/workspace-settings.js +1 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +349 -76
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +42 -16
- package/dist/schemes/Exec.d.ts +4 -4
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +167 -134
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts +4 -5
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +12 -20
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts +5 -6
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +35 -41
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +16 -5
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +229 -262
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +50 -27
- package/dist/schemes/Prompt.d.ts +3 -4
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +5 -5
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts +4 -5
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -3
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts +7 -7
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +99 -84
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +2 -2
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +29 -10
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +11 -17
- package/dist/schemes/_entry-find.d.ts +41 -14
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +159 -166
- 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 +28 -24
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +13 -8
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +98 -119
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +31 -6
- package/dist/schemes/_entry-semantic.d.ts +5 -7
- package/dist/schemes/_entry-semantic.d.ts.map +1 -1
- package/dist/schemes/_entry-semantic.js +10 -31
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/schemes/_entry-semantic.sql +2 -2
- 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 +16 -6
- 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/BranchBatches.d.ts +1 -0
- package/dist/server/BranchBatches.d.ts.map +1 -1
- package/dist/server/BranchBatches.js +1 -0
- package/dist/server/BranchBatches.js.map +1 -1
- package/dist/server/Daemon.d.ts +6 -8
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +238 -68
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +3 -3
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/branch-batch.sql +3 -1
- package/dist/server/client-input.d.ts +2 -1
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +7 -1
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/drain.sql +8 -7
- package/dist/server/envelope.d.ts +0 -2
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +0 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +3 -4
- package/dist/server/lifecycle-recovery.sql +44 -1
- package/dist/server/logEntry.d.ts +2 -1
- package/dist/server/logEntry.d.ts.map +1 -1
- package/dist/server/logEntry.js +15 -2
- package/dist/server/logEntry.js.map +1 -1
- package/dist/server/logEntry.sql +5 -1
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +13 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +730 -221
- package/package.json +53 -46
- package/dist/core/BudgetOverflow.d.ts +0 -23
- package/dist/core/BudgetOverflow.d.ts.map +0 -1
- package/dist/core/BudgetOverflow.js +0 -61
- 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 -44
- package/dist/core/caps/DbTagCaps.js.map +0 -1
- package/dist/matrix/live-specimen.d.ts +0 -9
- package/dist/matrix/live-specimen.d.ts.map +0 -1
- package/dist/matrix/live-specimen.js +0 -37
- package/dist/matrix/live-specimen.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -26,11 +26,12 @@ 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 context-envelope rejection · 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
|
-
| **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}). |
|
|
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. |
|
|
34
35
|
| **dispatch** | Core | Routing a statement to its scheme's op handler. |
|
|
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. |
|
|
@@ -41,7 +42,7 @@ flowchart LR
|
|
|
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
|
+
| **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`. |
|
|
45
46
|
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, `tokens`, `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). |
|
|
@@ -75,11 +76,14 @@ These are the complete strike sources:
|
|
|
75
76
|
|
|
76
77
|
| Strike source | Exact trigger | Model-visible occurrence |
|
|
77
78
|
|---------------------|------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------|
|
|
78
|
-
| Hard result | An admitted 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. |
|
|
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. |
|
|
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
|
+
`EXEC` results remain exact model-visible evidence but are always soft: an
|
|
84
|
+
executor error is not a PLURNK contract violation. Cycle and terminal steering
|
|
85
|
+
remain independent strike sources.
|
|
86
|
+
|
|
83
87
|
A struck turn increments the consecutive streak once; a clean admitted turn
|
|
84
88
|
resets it to zero. Reaching `MAX_STRIKES` terminates at **508 Loop Detected**
|
|
85
89
|
when the crossing turn is cycle-detected, otherwise **500**. Rejected emission
|
|
@@ -94,6 +98,7 @@ shown. The current streak may ride first-party provider metadata
|
|
|
94
98
|
| **verdict** | The end-of-turn ruling computed inline in `Engine.runLoop` from the strike rail and independent loop terminals. No filter chain. |
|
|
95
99
|
| **strike** | One admitted turn matching at least one source above. |
|
|
96
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}). |
|
|
97
102
|
| **cycle** | A repeated turn fingerprint across consecutive turns. Detection strikes silently under the rule above. |
|
|
98
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. |
|
|
99
104
|
| **flag** | Per-loop value: `mode`, `noWeb`, and `noInteraction` shape scheme authority ({§manifest-flag-affinity}); `auto` and `noProposals` select proposal settlement. |
|
|
@@ -122,9 +127,10 @@ real PLURNK loop: the production packet, a model-selected operation, its
|
|
|
122
127
|
materialized result, and completion.** Transport-only completions are not
|
|
123
128
|
conformance evidence. Provider-exposed reasoning must survive in the durable
|
|
124
129
|
assistant packet and digest; a provider with no private reasoning is valid when
|
|
125
|
-
the observable operation cycle succeeds.
|
|
126
|
-
|
|
127
|
-
in plurnk-core)
|
|
130
|
+
the observable operation cycle succeeds. One package-owned runner executes the
|
|
131
|
+
full tier or exactly one registered specimen (`npm run test:live:specimen --
|
|
132
|
+
<exact test name>` in plurnk-core), rejecting absent and duplicate names before
|
|
133
|
+
execution. The ledger and classification taxonomy live in
|
|
128
134
|
`plurnk-providers/README.md` and report authorization/credential failures
|
|
129
135
|
distinct from model failures and repeated stochastic failures separately from
|
|
130
136
|
stable ones, never with weakened assertions.
|
|
@@ -226,7 +232,7 @@ preserving the originating failure.
|
|
|
226
232
|
flowchart LR
|
|
227
233
|
actor["Worker A"] -->|"shared file or shared entry op"| state["Shared project files<br/>and shared workspace entries"]
|
|
228
234
|
state -->|"folded attributed delta<br/>environment door"| log["Worker B log"]
|
|
229
|
-
actor -->|"SEND
|
|
235
|
+
actor -->|"SEND to worker B<br/>voice door"| log
|
|
230
236
|
client["User / client"] -->|"loop.inject<br/>voice door"| log
|
|
231
237
|
```
|
|
232
238
|
|
|
@@ -248,7 +254,7 @@ file or ancestry-authorized entry through ordinary dispatch ({§worker-read-scop
|
|
|
248
254
|
| Door | Carries | Wake behavior |
|
|
249
255
|
| ----------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
250
256
|
| 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}). |
|
|
251
|
-
| Voice | A directed `loop.inject` or
|
|
257
|
+
| Voice | A directed `loop.inject` or `## SEND0 (worker://name)` message. | An active worker folds it into its next turn; an idle one wakes. |
|
|
252
258
|
|
|
253
259
|
§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.
|
|
254
260
|
|
|
@@ -285,7 +291,9 @@ the `plurnk` worker's log; the model sees the shared entry through its own READ.
|
|
|
285
291
|
Client-provided workspace documents union with the operator set at the same
|
|
286
292
|
entry surface.
|
|
287
293
|
|
|
288
|
-
§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
|
|
294
|
+
§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.
|
|
295
|
+
|
|
296
|
+
§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.`
|
|
289
297
|
|
|
290
298
|
### §machine-processes The machine and its processes: workspace, worker, fork
|
|
291
299
|
|
|
@@ -321,8 +329,8 @@ terminal history.**
|
|
|
321
329
|
| Project files ({§machine-processes-one-filesystem}) | Workspace | Shared live; a fork does not create another checkout. |
|
|
322
330
|
| Shared worker entries (`worker:///...`) | Workspace commons | Shared live. |
|
|
323
331
|
| Membership overlay ({§machine-processes-one-overlay}) | Workspace | Shared unchanged; divergent membership requires another workspace. |
|
|
324
|
-
| 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.
|
|
325
|
-
| §machine-processes-fork-cost **
|
|
332
|
+
| 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. |
|
|
333
|
+
| §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. |
|
|
326
334
|
| Private worker entries (`worker://~/...`) | Worker | Deep-copied with ownership remapped; parent and child then diverge. |
|
|
327
335
|
| Active loops, turns, and cancellation | Worker | Never copied as live work; inherited structure is terminal history, then a new loop starts. |
|
|
328
336
|
|
|
@@ -339,6 +347,14 @@ therefore carries only shared project-file and shared-entry changes
|
|
|
339
347
|
|
|
340
348
|
§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}).
|
|
341
349
|
|
|
350
|
+
§worker-provider-identity **A worker owns a durable provider identity distinct
|
|
351
|
+
from its database id.** Creation mints a globally unique, opaque 128-bit value;
|
|
352
|
+
forks mint their own value. Core supplies it as the provider `workerId` for every
|
|
353
|
+
emission and supplies the lineage root's value as `primaryWorkerId`
|
|
354
|
+
({§provider-cache-identity}). Database ids remain the internal relational and
|
|
355
|
+
client coordinate. BARE calls use isolated per-call provider identities rather
|
|
356
|
+
than either worker value.
|
|
357
|
+
|
|
342
358
|
§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.
|
|
343
359
|
|
|
344
360
|
§machine-processes-fork-shares-the-world **A fork copies worker-owned history
|
|
@@ -397,9 +413,9 @@ literal `workers.name` value.
|
|
|
397
413
|
| `READ` | existing literal name | Collect the named worker's deliverable. |
|
|
398
414
|
| `KILL` | existing literal name, `~` | Terminate the named worker or caller. |
|
|
399
415
|
|
|
400
|
-
- §worker-scheme-spawn **Spawn** —
|
|
401
|
-
- §worker-scheme-irc **irc** —
|
|
402
|
-
- §worker-scheme-fork **Fork** —
|
|
416
|
+
- §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.
|
|
417
|
+
- §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.
|
|
418
|
+
- §worker-scheme-fork **Fork** — `## FORK0 (worker://<name>)` with a task body branches the
|
|
403
419
|
current worker into a **named** sister: its log is deep-copied
|
|
404
420
|
({§machine-processes-fork-copies-the-log}), which continues with `task`; the
|
|
405
421
|
world is shared, never copied ({§machine-processes-fork-shares-the-world}).
|
|
@@ -414,7 +430,7 @@ literal `workers.name` value.
|
|
|
414
430
|
parent's private entries — its own space deep-copied with the owner
|
|
415
431
|
remapped (source → branch) — so the branch opens with the parent's notes and
|
|
416
432
|
diverges on its own edits: *fork = everything-in-common-but-name*.
|
|
417
|
-
- **Git branch batch** —
|
|
433
|
+
- **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.
|
|
418
434
|
- §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.
|
|
419
435
|
- §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.
|
|
420
436
|
|
|
@@ -448,7 +464,7 @@ sequenceDiagram
|
|
|
448
464
|
participant B as Branch batch
|
|
449
465
|
participant C1 as Child branch 1
|
|
450
466
|
participant C2 as Child branch 2
|
|
451
|
-
P->>B: WORK
|
|
467
|
+
P->>B: WORK branch-1, FORK branch-2
|
|
452
468
|
P->>G: queue exclusive before releasing shared turn
|
|
453
469
|
G-->>B: all earlier turns drained
|
|
454
470
|
B->>B: snapshot clean project repository; create both refs from frozen base
|
|
@@ -464,30 +480,31 @@ sequenceDiagram
|
|
|
464
480
|
|
|
465
481
|
§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.
|
|
466
482
|
|
|
467
|
-
§worker-branch-batch-return **A child returns commits, never a dirty checkout.** SEND
|
|
483
|
+
§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.
|
|
484
|
+
|
|
485
|
+
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}
|
|
468
486
|
|
|
469
|
-
§worker-branch-batch-receipt **The parent reconciles; the engine does not merge.** The ordinary child deliverable remains the model's exact SEND
|
|
487
|
+
§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.
|
|
470
488
|
|
|
471
489
|
§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.
|
|
472
490
|
|
|
473
491
|
The remaining worker surfaces are:
|
|
474
492
|
|
|
475
|
-
- **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-
|
|
476
|
-
- §worker-scheme-terminate **Terminate** —
|
|
493
|
+
- **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.
|
|
494
|
+
- §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`.
|
|
477
495
|
- §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.
|
|
478
496
|
- §worker-scheme-collect **Collect** — a worker's loop reaching a terminal status
|
|
479
497
|
surfaces to its sisters as an ambient delta ({§env-delta}): a `SEND` from
|
|
480
|
-
`worker://<name>` carrying the loop's
|
|
481
|
-
|
|
498
|
+
`worker://<name>` carrying the loop's exact terminal operation result. A
|
|
499
|
+
**2xx deliverable is born OPEN** (its body
|
|
482
500
|
materialized into the parent's packet, not hidden behind a fold): a child's
|
|
483
501
|
success must reach the parent open and awakening, never a bodyless row. An
|
|
484
|
-
|
|
502
|
+
non-2xx result surfaces folded; a failure retains its exact status and Problem. Every death-path is stamped uniformly,
|
|
485
503
|
so no termination is silent; collection is the shared world moving, never a
|
|
486
504
|
verb. The **pull** side mirrors the push: a path-absent
|
|
487
|
-
|
|
488
|
-
loop's terminal message (the result, or the abandonment reason) for a
|
|
505
|
+
`## READ0 (worker://<name>)` collects that same result on demand for a
|
|
489
506
|
concluded worker; a worker **still running** has not delivered, so the READ
|
|
490
|
-
returns **425** (Too Early) and the turn's bare `
|
|
507
|
+
returns **425** (Too Early) and the turn's bare SEND signal `102` **becomes a
|
|
491
508
|
parked loop (202) on the join** ({§join-blocking-collect}) until the worker
|
|
492
509
|
delivers — the engine holds the join, the model never drives a park. A
|
|
493
510
|
missing name is 404. The model therefore reads the worker itself for its
|
|
@@ -505,7 +522,7 @@ The remaining worker surfaces are:
|
|
|
505
522
|
|
|
506
523
|
### §worker-loop-lifecycle Worker and loop lifecycle: drain, reap, and passive wake
|
|
507
524
|
|
|
508
|
-
- §join-blocking-collect **A `READ` on a running child is a blocking join, not a poll.** A path-absent
|
|
525
|
+
- §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.
|
|
509
526
|
|
|
510
527
|
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.
|
|
511
528
|
|
|
@@ -526,8 +543,8 @@ stateDiagram-v2
|
|
|
526
543
|
§stream-catalog-lifecycle Streams are independently durable subscriptions owned by a worker. Payload and
|
|
527
544
|
lifecycle are orthogonal: zero bytes is a valid payload for both success and
|
|
528
545
|
failure, while the closed subscription and its status are the terminal fact.
|
|
529
|
-
Every stream entry exposes that durable state
|
|
530
|
-
`stream: { state, ... }
|
|
546
|
+
Every stream entry exposes that durable state on its catalog group's default
|
|
547
|
+
channel (`[0].stream: { state, ... }`): active streams carry `seconds`; terminal streams carry
|
|
531
548
|
their exact `status` and derive `closed` (status below 400), `killed` (499), or
|
|
532
549
|
`failed` (other failure status). An entry with no subscription has no `stream`
|
|
533
550
|
member. This is historical state, not merely a live-process hint.
|
|
@@ -546,7 +563,7 @@ stateDiagram-v2
|
|
|
546
563
|
Observed --> [*]
|
|
547
564
|
```
|
|
548
565
|
|
|
549
|
-
| §worker-lifecycle-subscription-matrix Subscription state at `
|
|
566
|
+
| §worker-lifecycle-subscription-matrix Subscription state at SEND signal `202` | Terminal observation already in a packet | Result |
|
|
550
567
|
|-------------------------------------------------------------------------|---:|---|
|
|
551
568
|
| open | no | park; polling or closure may wake it |
|
|
552
569
|
| closed, any status, empty or non-empty | no | continue directly to the observation turn |
|
|
@@ -573,9 +590,9 @@ sequenceDiagram
|
|
|
573
590
|
participant C as Child loop
|
|
574
591
|
participant S as Child stream
|
|
575
592
|
P->>C: WORK or FORK
|
|
576
|
-
P->>P: SEND
|
|
593
|
+
P->>P: SEND 202 parks on live child
|
|
577
594
|
C->>S: EXEC opens subscription
|
|
578
|
-
C->>C: SEND
|
|
595
|
+
C->>C: SEND 202 parks on live stream
|
|
579
596
|
loop backoff, fixed cadence, or explicit arrival
|
|
580
597
|
S-->>C: optional progress observation
|
|
581
598
|
C->>C: continue or park
|
|
@@ -596,15 +613,17 @@ sequenceDiagram
|
|
|
596
613
|
| cancelled or failed terminal | no | same wake/delivery path as success; outcome remains non-2xx |
|
|
597
614
|
|
|
598
615
|
A stream's close status and a loop's terminal status are separate layers. A
|
|
599
|
-
stream may close 4xx/5xx and wake its worker to recover. Model
|
|
600
|
-
|
|
601
|
-
`
|
|
616
|
+
stream may close 4xx/5xx and wake its worker to recover. Model SEND signals
|
|
617
|
+
`4xx/5xx` report a failed action and continue; signal `200` concludes successfully and
|
|
618
|
+
signal `499` explicitly abandons the worker. Only a concluded loop crosses the
|
|
602
619
|
parent edge as the child's result.
|
|
603
620
|
|
|
604
621
|
§worker-lifecycle-terminal-result **Terminal truth is a result, not a lifecycle code.** `loops.terminal_result`
|
|
605
622
|
stores the exact universal operation result. A failure therefore retains its
|
|
606
623
|
RFC 9457 Problem Details and exact status through persistence, restart,
|
|
607
|
-
parent collection, and `loop/terminated
|
|
624
|
+
parent collection, and `loop/terminated`; successful terminal SEND content and
|
|
625
|
+
mimetype remain in the same result. Cancellation markers and branch receipts
|
|
626
|
+
are derived presentation, never a second stored outcome. The constrained `loops.status`
|
|
608
627
|
column remains only the scheduler's compact lifecycle projection: known
|
|
609
628
|
terminal classes remain themselves, other 2xx/3xx statuses project to `200`,
|
|
610
629
|
and other 4xx/5xx statuses project to `500`; exact `202` is forbidden because
|
|
@@ -632,15 +651,16 @@ observe their terminal results. No effect is replayed across an unknown
|
|
|
632
651
|
boundary.
|
|
633
652
|
|
|
634
653
|
- §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.
|
|
635
|
-
- §worker-lifecycle-total-reap **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's `
|
|
654
|
+
- §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).
|
|
636
655
|
- §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.
|
|
637
656
|
- §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.
|
|
638
657
|
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** When a backgrounded stream concludes, the daemon 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.
|
|
639
|
-
- §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 — the daemon resumes its parent **in place** if the parent is blocked on the join (`#onDrainExit` → the shared `#wakeParkedWorker`, the same 202→100 resume a stream conclusion uses). So a parent that spawns work and blocks
|
|
640
|
-
- §worker-
|
|
658
|
+
- §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 — the daemon resumes its parent **in place** if the parent is blocked on the join (`#onDrainExit` → 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.)
|
|
659
|
+
- §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.
|
|
660
|
+
- §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.
|
|
641
661
|
- §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.
|
|
642
662
|
- §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.
|
|
643
|
-
- §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.
|
|
663
|
+
- §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.
|
|
644
664
|
|
|
645
665
|
---
|
|
646
666
|
|
|
@@ -650,13 +670,12 @@ Author-facing contract: [`@plurnk/plurnk-providers`](../plurnk-providers/SPEC.md
|
|
|
650
670
|
|
|
651
671
|
### §provider-surface Consumption surface
|
|
652
672
|
|
|
653
|
-
|
|
673
|
+
Two current entry points:
|
|
654
674
|
|
|
655
|
-
- §provider-surface-generate `provider.generate(args)` — once per
|
|
656
|
-
- §provider-surface-prompt-measurement `provider.countPromptTokens(messages, signal)` — cancellable measurement of the complete provider request with `exact`, `upper_bound`, or `estimate` provenance. It is used only for
|
|
657
|
-
- §provider-surface-calculate-cost `provider.calculateCost(usage)` — once per persisted provider attempt carrying usage; estimated USD. Engine aggregates every attempt into `turns.usage_cost_usd`; triggers cascade to `workers.cost_usd` / `workspaces.cost_usd`.
|
|
675
|
+
- §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}).
|
|
676
|
+
- §provider-surface-prompt-measurement `provider.countPromptTokens(messages, signal)` — cancellable measurement of the complete provider request with `exact`, `upper_bound`, or `estimate` provenance. It is used only for hard context-envelope admission ({§tokenomics-context-envelope-admission}); model-facing stored and rendered weights use the provider-agnostic ruler.
|
|
658
677
|
|
|
659
|
-
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow` (
|
|
678
|
+
§provider-surface-identity Provider capacity and identity are immutable for one instance. `contextWindow` (the effective total context envelope, or `null` when unknown) and the optional reasoning/completion reserves define the natural prompt partition and hard admission check ({§tokenomics}); `model` identifies persisted turn/provider evidence. Local GBNF boot verification also consumes `constrainsOutput` ({§grammar-enforcement-verified-at-boot}).
|
|
660
679
|
|
|
661
680
|
§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}).
|
|
662
681
|
|
|
@@ -665,30 +684,37 @@ Three entry points:
|
|
|
665
684
|
- `messages` is a complete prompt (the section list, pre-assembled into the system + user messages). Provider does not reorder.
|
|
666
685
|
- §provider-guarantees-signal-wired `signal` is wired to the worker's AbortController.
|
|
667
686
|
- §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.
|
|
687
|
+
- 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}).
|
|
688
|
+
- §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.
|
|
668
689
|
- §provider-guarantees-assistantraw-opaque `assistantRaw` is opaque to the engine (forensics-only).
|
|
669
|
-
- `countPromptTokens` receives the exact `PacketWire` messages later supplied to `generate`. The engine calls it only
|
|
690
|
+
- `countPromptTokens` receives the exact `PacketWire` messages later supplied to `generate`. The engine calls it only when negative ruler pressure requires hard context-envelope admission; it may perform provider I/O and receives the loop cancellation signal.
|
|
670
691
|
|
|
671
692
|
### §emission-admission Provider emission admission
|
|
672
693
|
|
|
673
694
|
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}).
|
|
674
695
|
|
|
675
|
-
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
|
|
696
|
+
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. 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.
|
|
676
697
|
|
|
677
|
-
|
|
698
|
+
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.
|
|
699
|
+
|
|
700
|
+
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.
|
|
678
701
|
The Problem recovery states that only the failed operation needs correction
|
|
679
702
|
because its parsed siblings were retained; the parser-owned detail states the
|
|
680
703
|
specific syntax rule.
|
|
681
704
|
|
|
682
|
-
§invalid-emission-attempts Exhausting the emission-attempt budget
|
|
683
|
-
|
|
705
|
+
§invalid-emission-attempts Exhausting the emission-attempt budget opens the
|
|
706
|
+
single informed recovery turn above. Consecutive exhaustion of that turn
|
|
707
|
+
terminates the loop at 500 without spending an engine strike.
|
|
684
708
|
|
|
685
709
|
§turn-never-blank An admitted turn whose operation fails — during parsing or
|
|
686
710
|
dispatch — is categorically different: its failed operation row enters
|
|
687
711
|
model-visible history and the next engine turn may recover. A `ProviderError`
|
|
688
|
-
means no
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
712
|
+
means no exchange was admitted (auth, exhausted transport retries, rate limit,
|
|
713
|
+
or provider-declared interruption). Core settles and retains every physical
|
|
714
|
+
request's known or unknown {§provider-request-accounting}; when the error carries
|
|
715
|
+
interrupted response evidence, Core stores it unaccepted without duplicating its
|
|
716
|
+
accounting. The failed turn still stores the exact request and never fabricates
|
|
717
|
+
an assistant or a zero-valued observation.
|
|
692
718
|
|
|
693
719
|
### §attribution Plugin-authored attribution folksonomy
|
|
694
720
|
|
|
@@ -701,10 +727,10 @@ shared contract {§plugin-attribution}:
|
|
|
701
727
|
|
|
702
728
|
| Stage | Contract |
|
|
703
729
|
| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
704
|
-
| Collection | Immediately before each
|
|
730
|
+
| 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. |
|
|
705
731
|
| Composition | Core flattens, deduplicates, and sorts the tags. The resulting non-empty array rides `generate({ attributions })`; an empty set omits that provider field. |
|
|
706
732
|
| 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}). |
|
|
707
|
-
| Request evidence | The stored request packet carries the exact set most recently forwarded for that turn.
|
|
733
|
+
| 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. |
|
|
708
734
|
| 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. |
|
|
709
735
|
|
|
710
736
|
Runtime hooks are synchronous and receive only the attempt coordinates. A hook
|
|
@@ -729,16 +755,20 @@ plugin discovery is the last protocol-extension seam.
|
|
|
729
755
|
The ANTLR grammar always defines and validates the PLURNK language. Separately,
|
|
730
756
|
an operator may configure `PLURNK_PROVIDERS_GBNF_<alias>` for a local
|
|
731
757
|
llama-server. The provider must advertise GBNF transport and satisfy a forcing
|
|
732
|
-
probe whose
|
|
733
|
-
`PLURNK-RAILS-LIVE` sentinel, or boot
|
|
758
|
+
probe whose sampled sentence and pre-projection response match the selected
|
|
759
|
+
rail's template boundary followed by the `PLURNK-RAILS-LIVE` sentinel, or boot
|
|
760
|
+
fails. The setting is resolved
|
|
734
761
|
per alias and is unset by default. Configuring it on a cloud or endpoint-managed
|
|
735
762
|
provider is an error, not a request for best-effort filtering.
|
|
763
|
+
Alias-scoped `PLURNK_PROVIDERS_GBNF_DEBUG` deliberately withholds transport, so
|
|
764
|
+
boot skips the enforcement probe while real requests retain the configured rail
|
|
765
|
+
for local syntax validation and the engine's withheld-rail verdict.
|
|
736
766
|
Runtime injection uses the provider's registered alias, falling back only to
|
|
737
767
|
the process's active alias. Suffixed rail settings with neither identity fail
|
|
738
768
|
instead of guessing. A configured package variant or explicit path that cannot
|
|
739
769
|
be loaded also fails; it never silently becomes unconstrained.
|
|
740
770
|
|
|
741
|
-
§gbnf-requires-reasoning
|
|
771
|
+
§gbnf-requires-reasoning Both shipped PLURNK rails require reasoning. The same alias-scoped configuration
|
|
742
772
|
must resolve reasoning to `adaptive` or `on`; `off` with GBNF is rejected before
|
|
743
773
|
the probe or any model generation. Reasoning-off remains valid when no GBNF rail
|
|
744
774
|
is configured.
|
|
@@ -746,7 +776,8 @@ is configured.
|
|
|
746
776
|
§rail-truth-engine-verdict **Local constraint truth is independently observed.**
|
|
747
777
|
For a configured local GBNF, the provider returns the pre-projection sentence as
|
|
748
778
|
`grammarEvidence` under `plurnk-providers` {§gbnf-response-observation}. The engine
|
|
749
|
-
requires that evidence, independently validates `grammarEvidence.input
|
|
779
|
+
requires that evidence, independently validates `grammarEvidence.input` with the
|
|
780
|
+
artifact's declared response root, and
|
|
750
781
|
stamps `railsAttached: "client"` when transported or `"withheld"` in debug mode
|
|
751
782
|
plus `railsVerdict`; it never validates projected
|
|
752
783
|
`assistant.content` as though the required reasoning enclosure were still
|
|
@@ -847,7 +878,7 @@ Every fact names the canonical key, never the host root or an echo of the
|
|
|
847
878
|
model's spelling. These classes let a caller distinguish a wrong address, an
|
|
848
879
|
invalid range, read-only authority, and occupied hidden state without guessing.
|
|
849
880
|
|
|
850
|
-
§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
|
|
881
|
+
§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.
|
|
851
882
|
|
|
852
883
|
### §scheme-manifest Manifest
|
|
853
884
|
|
|
@@ -878,9 +909,11 @@ Registration precedes loop affinity:
|
|
|
878
909
|
| Registered but inactive under flag | The flag gate returns `403 scheme-unavailable`. |
|
|
879
910
|
| Registered and active | Dispatch continues to the operation owner. |
|
|
880
911
|
|
|
881
|
-
- §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.
|
|
912
|
+
- §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.
|
|
913
|
+
|
|
914
|
+
§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.
|
|
882
915
|
|
|
883
|
-
- §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
|
|
916
|
+
- §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.
|
|
884
917
|
|
|
885
918
|
- §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.
|
|
886
919
|
|
|
@@ -914,14 +947,14 @@ Details state `destinationWritten: true` and identify the destination.
|
|
|
914
947
|
|
|
915
948
|
Directed SEND (non-null path) routes to scheme's `send`. Status = intent:
|
|
916
949
|
|
|
917
|
-
-
|
|
918
|
-
-
|
|
950
|
+
- `## SEND0 [200] (path)` — write body into resource (WS message, exec stdin).
|
|
951
|
+
- `## SEND0 [499] (path)` — cancel active subscription ({§stream}).
|
|
919
952
|
|
|
920
|
-
- §log-uniform-query **Log speaks the universal query contract** —
|
|
921
|
-
- §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
|
|
922
|
-
- §matcher-selection-signal **Matching carries navigation evidence** - a matcher is a boolean resource predicate.
|
|
953
|
+
- §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.
|
|
954
|
+
- §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.
|
|
955
|
+
- §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.
|
|
923
956
|
|
|
924
|
-
|
|
957
|
+
`## 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}).
|
|
925
958
|
|
|
926
959
|
§send-dispatch-entry-schemes-501-on-non-410 Other status codes return 501 from entry-bearing schemes by default.
|
|
927
960
|
|
|
@@ -956,11 +989,25 @@ The optional engine-/daemon-populated capabilities (the notifiers, `injectWorker
|
|
|
956
989
|
Engine → scheme guarantees:
|
|
957
990
|
|
|
958
991
|
- `ctx` is fresh per call. No mutation across calls.
|
|
959
|
-
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
992
|
+
- §universal-read-composition **Exact READ has one composition.** Core resolves
|
|
993
|
+
canonical identity and owner once, gives a data scheme its optional
|
|
994
|
+
`prepareRepresentation({ target, pathname })` opportunity, reads the complete
|
|
995
|
+
canonical channels, selects the authored channel, applies binary and
|
|
996
|
+
text-coordinate rules, and finally composes that channel's durable producer
|
|
997
|
+
result. Preparation receives neither fragment nor `lineMarker`; finite work
|
|
998
|
+
returns `200`, while only a retained live representation may return `102`
|
|
999
|
+
({§read-preparation}). No public handler can replace READ.
|
|
1000
|
+
- Exact FIND uses the same resolved identity and representation preparation
|
|
1001
|
+
before standard entry selection, then composes the exact default channel's
|
|
1002
|
+
durable producer result with the core-owned query projection. Broad FIND may invoke a custom `find()` for
|
|
1003
|
+
genuinely protocol-owned candidate enumeration, or `prepareFind()` followed
|
|
1004
|
+
by the standard catalog query. Acquisition never owns matcher, pagination,
|
|
1005
|
+
or result-unit semantics. Every prepared write and query preserves all
|
|
1006
|
+
identity components owned by {§scheme-address}.
|
|
1007
|
+
- COPY/MOVE source selection resolves and prepares that same canonical
|
|
1008
|
+
representation before selecting a channel. Its independent source scope
|
|
1009
|
+
remains raw transfer semantics—markerless means the complete channel rather
|
|
1010
|
+
than READ's preview—and is structurally unavailable to the producer.
|
|
964
1011
|
- `ctx.writer` reflects the actual writer at this dispatch.
|
|
965
1012
|
- §scheme-surface-writableby-403 `manifest.writableBy` is checked BEFORE invocation; engine returns 403 directly on exclusion.
|
|
966
1013
|
- `ctx.signal` is wired to the worker's AbortController ({§provider-guarantees-signal-wired}).
|
|
@@ -1033,10 +1080,10 @@ plurnk-service is mimetype-illiterate. Engine hands channel content + mimetype l
|
|
|
1033
1080
|
|
|
1034
1081
|
§mimetype-owned-lifecycle `Daemon` owns and disposes the `Mimetypes` instance
|
|
1035
1082
|
it constructs. A constructor-injected instance remains caller-owned. Shutdown
|
|
1036
|
-
quiesces model work,
|
|
1037
|
-
instance exactly once; mimetype teardown failures
|
|
1038
|
-
the same aggregate as module and scheme shutdown
|
|
1039
|
-
repeated stop does not acquire or dispose resources.
|
|
1083
|
+
quiesces model work, cancels and settles active derivation warming, then
|
|
1084
|
+
disposes the daemon-owned instance exactly once; mimetype teardown failures
|
|
1085
|
+
retain their causes and join the same aggregate as module and scheme shutdown
|
|
1086
|
+
failures. A pre-start or repeated stop does not acquire or dispose resources.
|
|
1040
1087
|
|
|
1041
1088
|
§mimetype-classification-consumption Every engine-owned binary decision uses
|
|
1042
1089
|
the configured `Mimetypes.classify()` path. An installed handler declaration
|
|
@@ -1061,8 +1108,8 @@ discovery ({§mimetype-discovery}).
|
|
|
1061
1108
|
projection is independent of packet budgeting. Core uses the stable
|
|
1062
1109
|
model-independent ruler for stored/catalog weights and the model-facing budget
|
|
1063
1110
|
({§tokenomics-agnostic-ruler}). The provider's request-shaped measurement is
|
|
1064
|
-
confined to
|
|
1065
|
-
({§tokenomics-
|
|
1111
|
+
confined to hard context-envelope admission
|
|
1112
|
+
({§tokenomics-context-envelope-admission}).
|
|
1066
1113
|
|
|
1067
1114
|
§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.
|
|
1068
1115
|
|
|
@@ -1133,7 +1180,7 @@ A published default channel renders under the entry's ordinary fragmentless addr
|
|
|
1133
1180
|
|
|
1134
1181
|
### §no-visibility Entries carry no visibility
|
|
1135
1182
|
|
|
1136
|
-
Every entry is uniformly listed in the catalog (
|
|
1183
|
+
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.
|
|
1137
1184
|
|
|
1138
1185
|
### §channel-mimetype Mimetype is a (scheme, channel) property — never a default
|
|
1139
1186
|
|
|
@@ -1167,14 +1214,7 @@ Op implications:
|
|
|
1167
1214
|
|
|
1168
1215
|
Client-interface target parameters carry fragments inline (`{ target: "sh:///1/1/2#stderr" }`).
|
|
1169
1216
|
|
|
1170
|
-
**Wire rendering: default channel is path-only.**
|
|
1171
|
-
|
|
1172
|
-
```
|
|
1173
|
-
<<notes.md:...:notes.md — file scheme (bare)
|
|
1174
|
-
<<sh:///1/1/2:...:sh:///1/1/2 — exec output default (stdout)
|
|
1175
|
-
<<sh:///1/1/2#stderr:...:sh:///1/1/2#stderr — non-default
|
|
1176
|
-
<<log:///1/1/0:...:log:///1/1/0 — atomic log row
|
|
1177
|
-
```
|
|
1217
|
+
**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`.
|
|
1178
1218
|
|
|
1179
1219
|
### §channel-state Channel state — metadata, not gating
|
|
1180
1220
|
|
|
@@ -1195,56 +1235,151 @@ Model uses state to anticipate growth between turns. Clients use state for UI (s
|
|
|
1195
1235
|
|
|
1196
1236
|
Per-op semantics. AST shapes come from `@plurnk/plurnk-contracts`'s `PlurnkStatement`. Engine dispatches by `op`; scheme implements per author contract ({§scheme}).
|
|
1197
1237
|
|
|
1238
|
+
### §line-anchors Text line anchors
|
|
1239
|
+
|
|
1240
|
+
A scheme declaring `textEditScopes: true` with model write authority publishes
|
|
1241
|
+
the contracts-owned {§text-line-anchor-syntax}. For canonical model-facing
|
|
1242
|
+
resource identity `R`, one-based line ordinal `L`, configured non-negative
|
|
1243
|
+
neighbor count `C`, and ordered content array `W` containing that line and up to
|
|
1244
|
+
`C` complete lines on either side (all excluding separators), core hashes the
|
|
1245
|
+
JSON tuple `["plurnk-line-anchor-v1",R,L,C,W]` with SHA-256, interprets the
|
|
1246
|
+
digest as a big-endian integer modulo `62^5`, and encodes five fixed-width
|
|
1247
|
+
characters with alphabet `0-9A-Za-z`. The universal READ projector derives
|
|
1248
|
+
anchors from the complete canonical selected channel before applying the
|
|
1249
|
+
authored text slice; its durable result retains the canonical derivation
|
|
1250
|
+
identity and anchors aligned with returned lines. Packet rendering right-aligns
|
|
1251
|
+
`L` to the decimal width of the complete canonical selected channel's final
|
|
1252
|
+
addressable line and emits `@xxxxx L:<content>` with one or more ASCII spaces
|
|
1253
|
+
before `L`; a source line therefore retains the same prefix across projections
|
|
1254
|
+
of one revision.
|
|
1255
|
+
An explicit default-channel fragment and its fragmentless spelling share that
|
|
1256
|
+
identity; a selected non-default channel retains its canonical `#channel`.
|
|
1257
|
+
|
|
1258
|
+
For READ/LOOK and COPY/MOVE source or destination selection, core resolves every
|
|
1259
|
+
anchor against the addressed current complete content before applying the
|
|
1260
|
+
ordinary numeric text-coordinate contract. Exactly one current match lowers to
|
|
1261
|
+
its numeric line; zero or multiple matches return 409 `line-anchor-collision`,
|
|
1262
|
+
and an anchor in a column position returns 400. COPY/MOVE mutation owners retain
|
|
1263
|
+
the resolved endpoint neighborhoods as compare-and-swap preconditions. There is
|
|
1264
|
+
no revision sidecar or fuzzy relocation. A range authenticates both endpoint
|
|
1265
|
+
neighborhoods, so every line of a range up to `2C + 2` lines is covered; a
|
|
1266
|
+
longer range retains an unauthenticated interior gap. The shipped `C = 2`
|
|
1267
|
+
covers ranges through six lines.
|
|
1268
|
+
|
|
1198
1269
|
### §edit EDIT
|
|
1199
1270
|
|
|
1200
|
-
AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
|
|
1271
|
+
AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker?: TextLineMarker }`.
|
|
1201
1272
|
|
|
1202
1273
|
- Resolves target channel from fragment ({§channel-selection}); unknown channel → 400; undeclared in manifest → engine crash ({§channel-mimetype}).
|
|
1203
1274
|
- §edit-null-clears Writes the body; `body: null` clears it.
|
|
1204
1275
|
- §edit-status-201-200 Returns `{ status: 201, entryId }` for a new entry and
|
|
1205
1276
|
`{ status: 200, entryId }` for a content update.
|
|
1206
|
-
- §edit-noop-304 A write that changes nothing — identical content
|
|
1207
|
-
- §edit-tags-additive Tags from `signal[]` apply additively via `entry_tags` (scheme may vary).
|
|
1277
|
+
- §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}).
|
|
1208
1278
|
- §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.
|
|
1279
|
+
- §edit-line-anchors An anchored EDIT resolves under {§line-anchors} and carries
|
|
1280
|
+
its endpoint checks as a core-private mutation precondition. Otherwise-valid
|
|
1281
|
+
zero/multiple matches and later precondition misses share {§edit-collision};
|
|
1282
|
+
malformed positions and schemes without textual EDIT scopes return 400 before
|
|
1283
|
+
handler invocation, while an upstream current-read failure preserves its
|
|
1284
|
+
status. The model-facing teaching recommends anchors for EDIT because this
|
|
1285
|
+
rejection is deliberate stale-target protection; parser support for anchors
|
|
1286
|
+
on observations does not imply the same recommendation.
|
|
1287
|
+
- §edit-collision Every standard entry EDIT lands by compare-and-swap against
|
|
1288
|
+
the exact channel content used to calculate it, including numeric-only EDITs.
|
|
1289
|
+
A concurrent creator that wins the resource identity or channel, an anchor
|
|
1290
|
+
that no longer identifies exactly one line, a selected endpoint neighborhood
|
|
1291
|
+
that changes before mutation, or a representation that changes in the final
|
|
1292
|
+
check/write gap returns the same neutral **409 `edit-collision`** and preserves
|
|
1293
|
+
the winner's content. Its public detail says only that EDIT collided with
|
|
1294
|
+
another change and directs the model to READ and retry; it does not assign
|
|
1295
|
+
fault or reveal which detection layer won. Concurrent correct workers are an
|
|
1296
|
+
ordinary cause. Core resolves anchors, scheme handlers receive only numeric
|
|
1297
|
+
coordinates, the shared entry mutation owner rechecks selected endpoint
|
|
1298
|
+
neighborhoods against its exact snapshot, and atomic identity/channel claims
|
|
1299
|
+
and storage predicates close the remaining races.
|
|
1209
1300
|
|
|
1210
1301
|
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.
|
|
1211
1302
|
|
|
1212
1303
|
### §read READ
|
|
1213
1304
|
|
|
1214
|
-
AST: `{ op: "READ", target, body:
|
|
1305
|
+
AST: `{ op: "READ", target, body: null, signal: tags | null, lineMarker? }`.
|
|
1306
|
+
|
|
1307
|
+
Matcher-bearing or path-glob READ syntax has already normalized to canonical
|
|
1308
|
+
FIND before dispatch under {§read-find-normalization}; core has no second READ
|
|
1309
|
+
selection or fan-out path.
|
|
1215
1310
|
|
|
1216
1311
|
- §read-read-content Returns channel content and mimetype.
|
|
1217
1312
|
- §read-read-404 Returns 404 when the channel is absent.
|
|
1218
|
-
- `
|
|
1219
|
-
|
|
1220
|
-
|
|
1313
|
+
- §read-selection-projection READ applies `lineMarker` as text coordinates to one
|
|
1314
|
+
exact target under {§read-exact-target}. Markerless READ synthesizes
|
|
1315
|
+
`<1,16>`; `<1,-1>` explicitly selects all text. Successful positional reads
|
|
1316
|
+
carry the compact requested/returned extent and available total
|
|
1317
|
+
({§range-extent}). Anchors resolve under {§line-anchors} before selection. An
|
|
1318
|
+
invalid text region is 416.
|
|
1319
|
+
|
|
1320
|
+
§log-item-tags **Tags classify durable log items.** Under {§log-tag-signal}, FIND, READ, EDIT, COPY,
|
|
1321
|
+
and MOVE strip any leading `+` from every signal term and add the resulting tag
|
|
1322
|
+
to the one log row created for that operation. The row receives its complete
|
|
1323
|
+
deduplicated set even when the operation fails or has no body; the signal never
|
|
1324
|
+
filters candidates, changes a resource, or persists on an entry. OPEN and FOLD
|
|
1325
|
+
use every unsigned signal term as an ALL-tags filter over existing log rows;
|
|
1326
|
+
their optional target and matcher intersect that set. They then strip and add
|
|
1327
|
+
each `+tag` and strip and remove each `-tag` on the exact selected rows while
|
|
1328
|
+
applying the requested visibility. Signed terms never select: a curation
|
|
1329
|
+
operation requires a target, matcher, or unsigned tag. Add and remove terms for
|
|
1330
|
+
the same tag conflict. Successful visibility and classification changes land as
|
|
1331
|
+
one curation event whose exact per-row deltas are durable. Engine policy may
|
|
1332
|
+
apply its separately specified diagnostic classifications, such as `overflow`.
|
|
1333
|
+
Every classification lives once in `log_tags`, is erased with its row, and is
|
|
1334
|
+
copied with log history on fork.
|
|
1221
1335
|
|
|
1222
1336
|
### §open-fold OPEN / FOLD
|
|
1223
1337
|
|
|
1224
1338
|
AST: `{ op: "OPEN"|"FOLD", target, body: MatcherBody | null, signal: tags | null, lineMarker: null }`.
|
|
1225
1339
|
|
|
1226
|
-
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
|
|
1340
|
+
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.
|
|
1227
1341
|
|
|
1228
1342
|
### §jsonplurnk The Log's wire format
|
|
1229
1343
|
|
|
1230
|
-
The `## Log` section renders as a
|
|
1344
|
+
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 ruler-weight of the row's ordinary packet body: 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}
|
|
1231
1345
|
|
|
1232
|
-
§
|
|
1346
|
+
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
1233
1347
|
|
|
1234
|
-
|
|
1348
|
+
The packet projects one actionable owner for each retrieval fact:
|
|
1235
1349
|
|
|
1236
|
-
|
|
1350
|
+
| Result mode | Extent | Result-body evidence | Additional aggregate fact |
|
|
1351
|
+
|---|---|---|---|
|
|
1352
|
+
| line READ | compact `line` range | none | none |
|
|
1353
|
+
| exact-coordinate READ | none | top-level `region` | none |
|
|
1354
|
+
| READ-shaped materialization notice | none | none | generic body `lines` |
|
|
1355
|
+
| catalog/path FIND | compact `resource` range | none | none |
|
|
1356
|
+
| broad matcher FIND | compact `resource` range | per-resource match-location counts | nonzero complete `matchLocationCount` |
|
|
1357
|
+
| exact matcher FIND | compact `matchLocation` range | each row's locator/region | none |
|
|
1358
|
+
|
|
1359
|
+
The compact range is `{ unit, total, requested: [first,last], returned?:
|
|
1360
|
+
[first,last] }` ({§range-extent}); empty results omit `returned`. Transparent
|
|
1361
|
+
coordinates let the model determine whether more material exists and choose
|
|
1362
|
+
its own next request, so packet metadata never prescribes `next`, `complete`,
|
|
1363
|
+
or `all`. FIND range cardinality replaces top-level `items`, `lines`, and
|
|
1364
|
+
`matchingPathCount`; line READ likewise omits the rendered-body `lines` count
|
|
1365
|
+
and its internally resolved whole-line region. Exact READ retains only its
|
|
1366
|
+
region. A failed retrieval's Problem owns its range extension rather than
|
|
1367
|
+
repeating it at top level. Generic `tokens` always weighs the rendered body;
|
|
1368
|
+
generic body `lines` remains available on READ-shaped materialization notices
|
|
1369
|
+
that have no retrieval extent. FIND content weights follow {§jsonplurnk};
|
|
1370
|
+
ordinary bounded bodies expose their displayed and complete chunk extents there.
|
|
1237
1371
|
|
|
1238
|
-
|
|
1239
|
-
- §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}) exactly as `READ(worker:///docs/)` fans out a folder: `FOLD(log:///1/2)` folds turn 1/2's rows. A **well-formed glob that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. 400 remains for a malformed target only (no coordinate, no glob, no slash).
|
|
1240
|
-
- §log-region-tagging **Named region tagging: FOLD applies, OPEN/FIND filter** — the log's write-op is **FOLD**, because EDIT can't reach engine-written rows (the OP×resource matrix): `FOLD[tag](region)` folds the region AND stamps the tag on it, additively ({§edit-tags-additive}), via `log_tags` (CASCADE-erased with the row on KILL). The read-ops filter: `OPEN[tag]` and `FIND[tag]` select rows carrying EVERY listed tag ({§find-tag-filter-and-semantics}) — a **targetless** `OPEN[tag]` recalls the whole tagged working-set across the worker, a scoped one filters within its glob; an unknown tag matches nothing (204, no-op success). `[tag]`-applies-on-the-write-op / `[tag]`-filters-on-the-read-ops is the same split entries already use (EDIT vs FIND); FOLD merely stands in for EDIT because the log is not an entry scheme. A fork carries a row's tags with its fold-state ({§machine-processes-fork-copies-the-log}). The model curates named working-sets of its own memory: file-away-under-a-name, recall-by-name.
|
|
1241
|
-
- §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.
|
|
1372
|
+
### §model-entry The model's own emission, mirrored back
|
|
1242
1373
|
|
|
1243
|
-
|
|
1374
|
+
§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.
|
|
1244
1375
|
|
|
1245
|
-
§
|
|
1376
|
+
- §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.
|
|
1377
|
+
- §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.
|
|
1378
|
+
- §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.
|
|
1246
1379
|
|
|
1247
|
-
§
|
|
1380
|
+
§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.
|
|
1381
|
+
|
|
1382
|
+
§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.
|
|
1248
1383
|
|
|
1249
1384
|
### §log-sensitive-request-evidence Durable request evidence
|
|
1250
1385
|
|
|
@@ -1259,7 +1394,7 @@ secret detection.
|
|
|
1259
1394
|
| Query and authored body | Preserved exactly; they are authored content and URI identity, not structurally identifiable credential slots. |
|
|
1260
1395
|
| Parser failure | Preserves the structural diagnosis and source position without quoting request-metadata contents ({§path-request-metadata}). |
|
|
1261
1396
|
| Client, fork, packet, and digest | Consume the stored projection; none owns a second redaction policy. |
|
|
1262
|
-
|
|
|
1397
|
+
| 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. |
|
|
1263
1398
|
|
|
1264
1399
|
### §copy COPY (engine-orchestrated)
|
|
1265
1400
|
|
|
@@ -1268,17 +1403,20 @@ body: ResourceSelection (destination), signal: tags | null }`.
|
|
|
1268
1403
|
|
|
1269
1404
|
1. §copy-missing-source-404 Resolve source path, channel, and optional text scope; missing resource or
|
|
1270
1405
|
channel is 404. A binary marker is not a byte channel and returns 415;
|
|
1271
|
-
readable projections are ordinary text sources under
|
|
1406
|
+
readable projections are ordinary text sources under
|
|
1407
|
+
{§membership-source-projection}. Source anchors resolve under
|
|
1408
|
+
{§line-anchors}.
|
|
1272
1409
|
2. Resolve destination path, channel, and optional text scope. Source and
|
|
1273
|
-
destination mimetypes must agree or the result is 415.
|
|
1410
|
+
destination mimetypes must agree or the result is 415. Destination anchors
|
|
1411
|
+
resolve independently under {§line-anchors}.
|
|
1274
1412
|
3. A scoped destination must already exist and is mutated through the
|
|
1275
1413
|
destination scheme's `editBatch`.
|
|
1276
1414
|
4. An unscoped destination writes only its selected channel. Existing other
|
|
1277
1415
|
channels survive.
|
|
1278
1416
|
- §copy-conflict-409 Different content in that channel is 409.
|
|
1279
|
-
- §copy-noop-304 Identical content
|
|
1280
|
-
5.
|
|
1281
|
-
|
|
1417
|
+
- §copy-noop-304 Identical content is 304.
|
|
1418
|
+
5. The signal classifies the COPY log item and never changes either resource
|
|
1419
|
+
({§log-item-tags}).
|
|
1282
1420
|
|
|
1283
1421
|
§copy-cross-scheme-copy The result is 201 for a new entry, 200 for a write, 304 for an exact no-op, or
|
|
1284
1422
|
202 when the owning scheme requires proposal review. Same- and cross-scheme
|
|
@@ -1292,8 +1430,14 @@ body: ResourceSelection (destination), signal: tags | null }`.
|
|
|
1292
1430
|
- §move-relocation-deletes-source MOVE first performs the destination mutation under {§copy}, then removes only
|
|
1293
1431
|
the selected source region or channel. A whole-channel MOVE deletes the
|
|
1294
1432
|
source entry only when that was its final channel.
|
|
1433
|
+
- §move-canonical-whole-source The canonical whole-content source scope
|
|
1434
|
+
`<1,-1>` resolves as a whole-channel selection for MOVE: it removes the
|
|
1435
|
+
selected channel and deletes the source entry when that was its final
|
|
1436
|
+
channel. Every other source scope remains regional even when it currently
|
|
1437
|
+
covers all available text; resource deletion is never inferred from extent.
|
|
1295
1438
|
- A same-channel regional MOVE applies destination insertion and source
|
|
1296
|
-
deletion in one same-snapshot `editBatch`;
|
|
1439
|
+
deletion in one same-snapshot `editBatch`; source and destination anchor
|
|
1440
|
+
preconditions compose against that snapshot, and overlapping regions are 409.
|
|
1297
1441
|
- A cross-resource destination failure leaves the source untouched. A source
|
|
1298
1442
|
failure after destination success is an explicit partial failure with
|
|
1299
1443
|
`destinationWritten: true`. Proposal acceptance/rejection follows the same
|
|
@@ -1312,42 +1456,62 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
1312
1456
|
|
|
1313
1457
|
AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal: tags | null, lineMarker? }`.
|
|
1314
1458
|
|
|
1315
|
-
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive
|
|
1459
|
+
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit plugin contract, never inferred from URL punctuation.
|
|
1316
1460
|
- An exact target resolves to the same canonical `(scheme, pathname)` identity
|
|
1317
1461
|
as READ, entry CRUD, and any preceding `prepareFind()`. URI authorities are
|
|
1318
1462
|
identity-bearing: `https://example.com/page` queries
|
|
1319
1463
|
`(https, /example.com/page)`, never `(https, /page)`.
|
|
1320
1464
|
- §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.
|
|
1321
|
-
- §find-semantic-
|
|
1322
|
-
- §find-tag-filter-and-semantics `signal` is a tag filter; entries match if they have ALL listed tags.
|
|
1465
|
+
- §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.
|
|
1323
1466
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
1324
|
-
- §find-result-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1467
|
+
- §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, itemsTokenTotal, returnedItemsTokenTotal }`:
|
|
1468
|
+
|
|
1469
|
+
| Target | Matcher body | `range.unit` | Result rows |
|
|
1470
|
+
|---|---|---|---|
|
|
1471
|
+
| exact | absent | `resource` | the one catalog channel group |
|
|
1472
|
+
| glob or folder | absent | `resource` | catalog channel groups |
|
|
1473
|
+
| glob or folder | present | `resource` | matching channel groups with `matchLocationCount` on `[0]` |
|
|
1474
|
+
| exact | present | `matchLocation` | flat `{ locator?, region? }` locations |
|
|
1475
|
+
|
|
1476
|
+
A glob or folder remains resource mode when it resolves to one path. An exact
|
|
1477
|
+
target remains location mode when it has many locations. A valid exact match
|
|
1478
|
+
with no addressable location is status 200 with `matchingPathCount: 1`,
|
|
1479
|
+
`matchLocationCount: 0`, and no fabricated row; a matcher selecting no
|
|
1480
|
+
resource is 204. A body-less broad empty catalog survey is status 200; an
|
|
1481
|
+
absent exact resource is 404.
|
|
1482
|
+
|
|
1483
|
+
Inside `FindResult`, `matchingPathCount` and `matchLocationCount` describe the
|
|
1484
|
+
complete selection before pagination; the packet curates those facts under
|
|
1485
|
+
{§retrieval-packet-metadata}. `path` is reserved for resource or channel identity;
|
|
1486
|
+
broad results never nest locations, and exact location rows never repeat the
|
|
1487
|
+
resource path. A **body-less** FIND is the **catalog**. Its outer result array
|
|
1488
|
+
contains one nonempty, flat channel array per resource. Element `[0]` is always
|
|
1489
|
+
the default channel and carries the bare resource path; later elements carry
|
|
1490
|
+
their complete `path#channel` addresses. Each channel is
|
|
1491
|
+
`{ path, mimetype, tokens, lines, parseIssues? }`; `parseIssues` is the
|
|
1492
|
+
positive-only advisory projection of `{§mimetype-parse-issues}` for the exact
|
|
1493
|
+
body derivation under `{§scheme-catalog-parse-issues}`. Resource-level `stream` and broad-match
|
|
1494
|
+
`matchLocationCount` live only on `[0]`. A single-channel resource is therefore
|
|
1495
|
+
a one-element array, with no path-owning wrapper or duplicated channel map.
|
|
1328
1496
|
A terminal single-star path scope is a one-level map: direct entries retain
|
|
1329
|
-
that shape, while deeper first-segment directories collapse to
|
|
1330
|
-
`{ path: "dir/**", items, tokens }`, where the selector and both aggregates
|
|
1497
|
+
that shape, while deeper first-segment directories collapse to the one-element
|
|
1498
|
+
group `[{ path: "dir/**", items, tokens }]`, where the selector and both aggregates
|
|
1331
1499
|
describe the exact recursive subtree. Scope summaries are navigation
|
|
1332
|
-
metadata, not resources
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
budget, `overflow` and `itemsTokenTotal` carry the count and aggregate weight
|
|
1348
|
-
while `results`, `matches`, and `pathnames` are empty; no caller may perform
|
|
1349
|
-
hidden work from content the model was denied. The gate is independent of
|
|
1350
|
-
model window size. `0`/unset disables it.
|
|
1500
|
+
metadata, not resources. Markerless FIND returns positions 1–16 in the
|
|
1501
|
+
selected unit; `<N,M>` selects an inclusive page and `<1,-1>` explicitly
|
|
1502
|
+
selects all. `range` reports the unit, complete result total, normalized
|
|
1503
|
+
request, and returned positions ({§range-extent}). `itemsTokenTotal` weighs the complete matched set while
|
|
1504
|
+
`returnedItemsTokenTotal` weighs the returned resource page; in exact
|
|
1505
|
+
location mode both weigh the one selected resource once. Resource order is
|
|
1506
|
+
rank for `~`semantic and candidate order otherwise; location order is dialect
|
|
1507
|
+
order and exact duplicates deduplicate. The intended drill-down is broad FIND
|
|
1508
|
+
to choose paths, exact-target FIND to choose locations, then exact READ.
|
|
1509
|
+
`content` uses the shared generated-JSON projection
|
|
1510
|
+
({§json-result-rendering}), so universal packet numbering makes result
|
|
1511
|
+
ordinal N addressable as line N, matching `<N>` pagination without a second
|
|
1512
|
+
coordinate system. Pagination is the only FIND materialization bound; no
|
|
1513
|
+
hidden complete row or location collection is retained behind the public
|
|
1514
|
+
projection.
|
|
1351
1515
|
|
|
1352
1516
|
### §send SEND
|
|
1353
1517
|
|
|
@@ -1362,16 +1526,16 @@ AST: `{ op: "SEND", target: ParsedPath | null, body: SendBody | null, signal: nu
|
|
|
1362
1526
|
|---|---|---|---|
|
|
1363
1527
|
| **102** continue | next turn | next turn | next turn |
|
|
1364
1528
|
| **200** done | **resolved** — terminal, loop ends | **refused** — Premature-Terminate (KILL to abandon, or wait) | **refused** — forced next turn to see the result |
|
|
1365
|
-
| **202** wait | **resolves like 200
|
|
1529
|
+
| **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) |
|
|
1366
1530
|
|
|
1367
|
-
§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.
|
|
1531
|
+
§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.
|
|
1368
1532
|
|
|
1369
1533
|
§loop-terminal-authorship **Terminal authorship is explicit when external.**
|
|
1370
1534
|
|
|
1371
1535
|
| `terminated_by` | Meaning | Presentation |
|
|
1372
1536
|
|---|---|---|
|
|
1373
1537
|
| `NULL` | The model's own terminal or an engine verdict whose exact result already carries the story. An already-drained join is the model's own successful terminal. | No authorship marker. |
|
|
1374
|
-
| `cancel` | An external client cancelled the structured scope ({§methods-loop-cancel}). | COLLECT and the termination delta prepend a cancellation marker to the
|
|
1538
|
+
| `cancel` | An external client cancelled the structured scope ({§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. |
|
|
1375
1539
|
|
|
1376
1540
|
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 surface is small on purpose: the model says done, waiting, or giving up, and is never asked to hold a correct opinion about *how* it failed or *whether* it can be woken — the engine decides those from state.
|
|
1377
1541
|
|
|
@@ -1385,7 +1549,7 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1385
1549
|
| Idle turn | An engine-rail error row with the corrective disposition | One strike |
|
|
1386
1550
|
| Refused disposition | The final SEND's 409 row with its exact Problem Detail | One strike |
|
|
1387
1551
|
|
|
1388
|
-
- §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
|
|
1552
|
+
- §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.
|
|
1389
1553
|
- §send-premature-terminate **Premature terminate — the pending set.**
|
|
1390
1554
|
Completion is gated by one rule: *nothing pending may be silently
|
|
1391
1555
|
discarded*. Pending work has two states: **live obligations** (open
|
|
@@ -1398,67 +1562,76 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1398
1562
|
retrieval-only refusal. The pending kind changes the corrective message, not
|
|
1399
1563
|
rail accounting. `[499]` deliberately abandons regardless.
|
|
1400
1564
|
- §send-undelivered-child-term **Completion is not delivery.** A result becomes
|
|
1401
|
-
observed only after crossing a packet boundary. `
|
|
1565
|
+
observed only after crossing a packet boundary. SEND signal `202` parks only on
|
|
1402
1566
|
live obligations. If work has completed but is unobserved, it continues
|
|
1403
1567
|
directly to the next packet because the wake edge has already fired; only a
|
|
1404
|
-
genuinely empty set resolves immediately like `[200]`.
|
|
1405
|
-
- §send-300-choices **SEND
|
|
1568
|
+
genuinely empty set with no successful same-turn FOLD resolves immediately like `[200]`.
|
|
1569
|
+
- §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.
|
|
1406
1570
|
|
|
1407
1571
|
### §exec EXEC
|
|
1408
1572
|
|
|
1409
|
-
AST: `{ op: "EXEC", target (optional
|
|
1573
|
+
AST: `{ op: "EXEC", target (optional runtime-specific target), body: string | null (runtime-specific input), signal: string | null (runtime tag), lineMarker (timeout/poll) }`.
|
|
1410
1574
|
|
|
1411
|
-
§exec-target-routing Engine routes unconditionally to the `exec` scheme
|
|
1412
|
-
|
|
1575
|
+
§exec-target-routing Engine routes unconditionally to the `exec` scheme,
|
|
1576
|
+
resolves the runtime first, and enforces that runtime's required
|
|
1577
|
+
{§executor-invocation} declaration before effect admission. Core owns target
|
|
1578
|
+
realization; neither filesystem type nor body presence may invent a target role
|
|
1579
|
+
the selected runtime did not declare. With no declared directory override,
|
|
1413
1580
|
`cwd` is the workspace's `project_root`, where the File scheme writes — never
|
|
1414
|
-
the daemon's own cwd.
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
|
1420
|
-
|
|
|
1421
|
-
|
|
|
1422
|
-
|
|
|
1423
|
-
|
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
A
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1581
|
+
the daemon's own cwd.
|
|
1582
|
+
|
|
1583
|
+
| Declared target kind | Authored target | Canonical effect target | Executor realization |
|
|
1584
|
+
| -------------------- | --------------------------------------- | ----------------------- | --------------------------------------------------------- |
|
|
1585
|
+
| Omitted | Any present target | — | Refuse 400 before admission. |
|
|
1586
|
+
| `literal` | Any target | Complete authored string | Preserve that exact string; perform no stat or scheme read. |
|
|
1587
|
+
| `path` | Local or `file://` path | Local path | Pass the path directly. |
|
|
1588
|
+
| `path` | Non-file address | — | Refuse 400 before admission. |
|
|
1589
|
+
| `resource` | Local or `file://` path | Local path | Pass the path directly. |
|
|
1590
|
+
| `resource` | Non-file data-scheme address | Complete authored address | Resolve one exact READ after acceptance and pass its temporary local file. |
|
|
1591
|
+
|
|
1592
|
+
A local directory becomes `cwd` with an absent executor target only when the
|
|
1593
|
+
runtime declaration explicitly sets `target.directory: "cwd"`; otherwise it
|
|
1594
|
+
remains the target. Core stats only for that declared rule. `ENOENT` remains a
|
|
1595
|
+
target so the runtime reports its own not-found. Any other stat failure stops
|
|
1596
|
+
before effect admission with a core-owned 500 Problem whose bounded diagnostic
|
|
1597
|
+
states the occurrence-specific cause while daemon diagnostics retain the
|
|
1598
|
+
complete error.
|
|
1599
|
+
|
|
1600
|
+
Body and target requirements come from the same runtime declaration. A runtime
|
|
1601
|
+
with no target declaration refuses a target; required body or target fields are
|
|
1602
|
+
enforced independently; every EXEC requires at least one of them; and an
|
|
1603
|
+
`exclusive` declaration refuses an invocation containing both. A target retains
|
|
1604
|
+
its one declared role whether the body is empty or non-empty. Runtime selection,
|
|
1605
|
+
target validation, and body/target relation failures therefore occur before
|
|
1606
|
+
effect classification or proposal creation.
|
|
1607
|
+
|
|
1608
|
+
§exec-source-temporary A non-file `resource` target is materialized into one
|
|
1609
|
+
core-owned temporary file after acceptance. Core reparses the complete authored
|
|
1610
|
+
address and resolves one exact `<1,-1>` READ through
|
|
1611
|
+
{§universal-read-composition}; internal source consumption never borrows the
|
|
1612
|
+
model-facing 16-line preview. The file lives through the executor run and core
|
|
1613
|
+
removes it after the subscription's terminal result has settled. A removal
|
|
1614
|
+
failure is reported to daemon diagnostics with its complete cause; it cannot
|
|
1615
|
+
rewrite the execution result, stream state, or completion wake.
|
|
1616
|
+
|
|
1617
|
+
Loop-flag authority follows the selected runtime's declaration:
|
|
1618
|
+
|
|
1619
|
+
| Target realization | Schemes that must be active |
|
|
1620
|
+
| ------------------------------------------------------- | -------------------------------------- |
|
|
1621
|
+
| Absent, `literal`, local `path`, or local `resource` | `exec` |
|
|
1622
|
+
| Non-file `resource` | `exec` and the addressed source scheme |
|
|
1623
|
+
|
|
1451
1624
|
Worker and runtime-stream authorities, query, fragment, request metadata, and
|
|
1452
|
-
every other
|
|
1453
|
-
failed source READ is preserved as the proposal-application
|
|
1454
|
-
successful READ with no string representation is refused 422;
|
|
1455
|
-
|
|
1456
|
-
command.
|
|
1625
|
+
every other component of a `resource` address retain their owning READ
|
|
1626
|
+
semantics. A failed source READ is preserved as the proposal-application
|
|
1627
|
+
failure. A successful READ with no string representation is refused 422; an
|
|
1628
|
+
empty string remains a present representation and is materialized faithfully.
|
|
1457
1629
|
|
|
1458
|
-
Core calls `effect()` once against
|
|
1459
|
-
stores the resulting fact with the invocation, and reuses it
|
|
1460
|
-
proposal policy, application, stream registration, and
|
|
1461
|
-
policy. The post-acceptance materialization path never
|
|
1630
|
+
Core calls `effect()` once against the canonical target shown above, without
|
|
1631
|
+
body text, stores the resulting fact with the invocation, and reuses it
|
|
1632
|
+
unchanged for proposal policy, application, stream registration, and
|
|
1633
|
+
effect-qualified hold policy. The post-acceptance materialization path never
|
|
1634
|
+
triggers reclassification.
|
|
1462
1635
|
|
|
1463
1636
|
§exec-registry-resolves The runtime slot (`signal`) selects an executor,
|
|
1464
1637
|
resolved against the boot-time `ExecutorRegistry`: siblings are discovered and
|
|
@@ -1469,7 +1642,7 @@ put a complete command in bare `EXEC`; they are never reinterpreted as shell
|
|
|
1469
1642
|
command words. An unavailable runtime is also 501 and carries the probe
|
|
1470
1643
|
`detail`.
|
|
1471
1644
|
|
|
1472
|
-
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
|
|
1645
|
+
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}
|
|
1473
1646
|
|
|
1474
1647
|
**Timeout and poll — `<T,P>` on the `<L>` slot (grammar 0.74.20).** EXEC
|
|
1475
1648
|
repurposes the line-marker slot as `<timeout, poll>` in **seconds**, the same
|
|
@@ -1485,10 +1658,10 @@ before the turn's own spawns, so it never survives into the subsequent turn;
|
|
|
1485
1658
|
its terminal output surfaces born-OPEN like any close ({§exec-stream}).
|
|
1486
1659
|
|
|
1487
1660
|
§exec-poll `P` (`mark[1]`) is the **poll cadence**, stored on the subscription.
|
|
1488
|
-
While the loop is blocked on a `
|
|
1661
|
+
While the loop is blocked on a SEND signal `202` wait for that stream, the daemon arms
|
|
1489
1662
|
a per-worker timer for the tightest open poll cadence and resumes the blocked
|
|
1490
|
-
loop every P seconds, floored by `
|
|
1491
|
-
faster than
|
|
1663
|
+
loop every P seconds, floored by `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS` so it cannot tick
|
|
1664
|
+
faster than the optimistic settlement scale, to inspect progress. It does **nothing while the
|
|
1492
1665
|
loop is active** because ambient stream deltas already surface progress. An
|
|
1493
1666
|
open stream without `P` uses exponential backoff
|
|
1494
1667
|
(`PLURNK_SERVICE_EXEC_POLL_SEC` and `PLURNK_SERVICE_EXEC_POLL_TURNS`); explicit
|
|
@@ -1512,11 +1685,31 @@ edge. Stream closure remains a wake edge under every poll policy.
|
|
|
1512
1685
|
|
|
1513
1686
|
§worker-auto-name **Auto-names are id-free ordinals** — worker names are the addressable authority, so an auto-name is `<prefix>-<N>` (per-workspace monotonic count, the fork `<parent>-fork-<N>` pattern), never a timestamp-hash that would leak machine identity through the hostname. The semantic suffix remains intact; when the complete name would exceed `WORKER_NAME`, generation shortens only the inherited prefix until the predicate admits it. Auto-names never reuse an existing literal and pass through {§worker-name-minting} like explicit names. Name selection and worker creation are one atomic claim: concurrent allocators receive distinct literals, while concurrent ensures of a workspace's default conversation converge on one root worker.
|
|
1514
1687
|
|
|
1515
|
-
§exec-readpure-ungated A `read` runtime (observes external state, e.g. search) or `pure` runtime (no observable effect, e.g. `:memory:` sqlite) is side-effect-free → **auto-run**: no proposal, no human gate, no notification. Core persists the prepared operation before applying it; that write-ahead staging has no resolution waiter and therefore cannot enter proposal discovery ({§proposal-list}). It skips the gate a host command faces, but it does NOT resolve in-band — like every exec it backgrounds and streams, its output reaching the model through the environment-observation injector (a foisted READ of
|
|
1688
|
+
§exec-readpure-ungated A `read` runtime (observes external state, e.g. search) or `pure` runtime (no observable effect, e.g. `:memory:` sqlite) is side-effect-free → **auto-run**: no proposal, no human gate, no notification. Core persists the prepared operation before applying it; that write-ahead staging has no resolution waiter and therefore cannot enter proposal discovery ({§proposal-list}). It skips the gate a host command faces, but it does NOT resolve in-band — like every exec it backgrounds and streams, its output reaching the model through the environment-observation injector (a foisted READ of newly publishable stream content each turn, {§exec-stream}), never a same-turn receipt.
|
|
1516
1689
|
|
|
1517
|
-
|
|
1690
|
+
After all non-SEND operations dispatch, the initiating turn applies {§worker-optimistic-settlement} to only the EXEC streams it started, then dispatches its terminal SEND against the refreshed lifecycle state. An older stream receives no renewed opportunity merely because another turn began. This is a settlement barrier before disposition, not sibling-operation serialization: dependent EXECs remain separate observed turns.
|
|
1518
1691
|
|
|
1519
|
-
|
|
1692
|
+
§exec-stream **Stream surfacing.** An exec's output is *observed, not fetched*.
|
|
1693
|
+
Each turn the environment-observation injector publishes newly publishable content
|
|
1694
|
+
from each owned channel as an `origin=plurnk` READ at
|
|
1695
|
+
`<runtime>:///<coord>#<channel>`, preserving the channel's current mimetype:
|
|
1696
|
+
|
|
1697
|
+
| channel mimetype | while active | at terminal state |
|
|
1698
|
+
|---|---|---|
|
|
1699
|
+
| `text/*` (including `text/stream`) | every new text segment | every remaining segment |
|
|
1700
|
+
| `application/jsonl` or `application/x-ndjson` | complete newline-terminated records | every remaining record |
|
|
1701
|
+
| every other mimetype | nothing | the complete atomic document |
|
|
1702
|
+
|
|
1703
|
+
The per-channel cursor advances only through content actually published, so an
|
|
1704
|
+
active atomic document or trailing partial JSONL record never reaches the model
|
|
1705
|
+
as malformed structured data. Ongoing observations are folded and a terminal
|
|
1706
|
+
observation is born OPEN; a terminal state with no newly publishable body still
|
|
1707
|
+
produces one conclusion row. Every READ then obeys {§body-projection} and
|
|
1708
|
+
therefore renders its selected result complete. A stream that closes before a
|
|
1709
|
+
same-turn wait remains pending until this terminal READ crosses the next packet
|
|
1710
|
+
boundary. The EXEC row separately records the authored invocation.
|
|
1711
|
+
|
|
1712
|
+
`## KILL0 (<runtime>:///<loop>/<turn>/<seq>)` cancels an active subprocess via
|
|
1520
1713
|
the subscription registry's stored controller. A terminal stream is immutable:
|
|
1521
1714
|
499 returns 410 (already killed), every other terminal status returns an RFC
|
|
1522
1715
|
9457 409 Problem carrying `terminalStatus`, and an unknown address returns 404.
|
|
@@ -1524,8 +1717,8 @@ The runtime scheme participates in the durable lookup; a completed `sh:///`
|
|
|
1524
1717
|
stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
1525
1718
|
|
|
1526
1719
|
§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.
|
|
1527
|
-
- §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
|
|
1528
|
-
- §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
|
|
1720
|
+
- §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.
|
|
1721
|
+
- §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, `tokens` = the content's count, 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.
|
|
1529
1722
|
|
|
1530
1723
|
### §proposal The proposal lifecycle
|
|
1531
1724
|
|
|
@@ -1541,7 +1734,7 @@ stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
|
1541
1734
|
|
|
1542
1735
|
| decision | state | `status_rx` | default outcome | effect |
|
|
1543
1736
|
|---------------------------------|---|---|---|---|
|
|
1544
|
-
| §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 `
|
|
1737
|
+
| §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. |
|
|
1545
1738
|
| §proposal-reject-fails reject | `failed` | 400 | `rejected` | none — the action did not occur. |
|
|
1546
1739
|
| §proposal-cancel-aborts cancel | `cancelled` | 499 | `loop_aborted` | none — the loop is abandoning. |
|
|
1547
1740
|
|
|
@@ -1589,7 +1782,7 @@ Thus `auto` wins the otherwise nonsensical `auto + noProposals` combination, but
|
|
|
1589
1782
|
|
|
1590
1783
|
### §subscriptions Subscriptions
|
|
1591
1784
|
|
|
1592
|
-
§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. `
|
|
1785
|
+
§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.
|
|
1593
1786
|
|
|
1594
1787
|
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.
|
|
1595
1788
|
|
|
@@ -1612,12 +1805,13 @@ Model sees lifecycle events in the `log` section per turn.
|
|
|
1612
1805
|
|
|
1613
1806
|
### §deep-slices Deep slices on demand
|
|
1614
1807
|
|
|
1615
|
-
|
|
1808
|
+
`## 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.
|
|
1616
1809
|
|
|
1617
|
-
### §stream-control
|
|
1810
|
+
### §stream-control Stream control and writes
|
|
1618
1811
|
|
|
1619
|
-
- **Cancel:**
|
|
1620
|
-
- **
|
|
1812
|
+
- **Cancel:** `## SEND0 [499] (https://feed.example/x)` — the service invokes the handle registered by `subscriptions.open()` and aborts the composed subscription signal.
|
|
1813
|
+
- **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.
|
|
1814
|
+
- **Other stream write:** `## SEND0 [200] (…)` remains scheme-defined, including exec stdin.
|
|
1621
1815
|
|
|
1622
1816
|
### §stream-constraints Engine constraints
|
|
1623
1817
|
|
|
@@ -1658,14 +1852,14 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
1658
1852
|
**Lives in SQL:**
|
|
1659
1853
|
- Render queries — log assembly + the manifest catalog.
|
|
1660
1854
|
- Cross-scope path collision (CHECK/trigger → 409).
|
|
1661
|
-
-
|
|
1855
|
+
- Logical model-call identity plus cardinal physical provider-request lifecycle and immutable settlement constraints.
|
|
1662
1856
|
- Sequence number issuance (1-based per grammar).
|
|
1663
1857
|
- Entry-vs-log integrity.
|
|
1664
1858
|
|
|
1665
1859
|
**Lives in TS:**
|
|
1666
1860
|
- Status-bubble rules (`turn.status` → `loop.status` → `worker.status` → `workspace.status`). Engine UPDATEs explicitly; CHECK constraints enforce; triggers fight branching state machines.
|
|
1667
1861
|
- Tokenization (provider-bound; hot-swap re-tokenizes per {§tokenomics}).
|
|
1668
|
-
- Provider dispatch
|
|
1862
|
+
- Provider dispatch, request-accounting validation, and exact-decimal aggregate projection through the shared contracts-owned path.
|
|
1669
1863
|
- Scheme-handler invocation (connections, subprocesses, fetch).
|
|
1670
1864
|
- Plugin loading ({§plugin-discovery}).
|
|
1671
1865
|
- Stream AbortController lifecycle.
|
|
@@ -1749,7 +1943,7 @@ scheme. External schemes are discovered through
|
|
|
1749
1943
|
dispatcher contract.
|
|
1750
1944
|
|
|
1751
1945
|
The executor registry discovers installed runtimes, probes availability, and
|
|
1752
|
-
routes
|
|
1946
|
+
routes `## EXEC0 [<runtime>]`; core contributes orchestration and the output-scheme
|
|
1753
1947
|
adapter, not runtime implementations. Optional and third-party leaves extend
|
|
1754
1948
|
each family by installation and discovery; they never require a framework or
|
|
1755
1949
|
service manifest edit.
|
|
@@ -1771,8 +1965,8 @@ service manifest edit.
|
|
|
1771
1965
|
|
|
1772
1966
|
- Channel state (`static`/`active`/`closed`/`errored`) — persisted channel metadata owned by core and exposed through the schemes capability contract ({§channel-state}).
|
|
1773
1967
|
- Backpressure caps — none ({§stream-constraints}).
|
|
1774
|
-
- Stream cancel — `
|
|
1775
|
-
- Delete — `KILL` (entry-KILL, the canonical delete, {§move}); `
|
|
1968
|
+
- Stream cancel — SEND signal `499` ({§stream-control}).
|
|
1969
|
+
- Delete — `KILL` (entry-KILL, the canonical delete, {§move}); SEND signal `410` also deletes as a side-effect ({§send-dispatch}).
|
|
1776
1970
|
- §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.
|
|
1777
1971
|
- Default-channel wire rendering — {§channel-selection}.
|
|
1778
1972
|
|
|
@@ -1796,7 +1990,7 @@ Node's pre-script env-file form and the executable's post-script form share the
|
|
|
1796
1990
|
|
|
1797
1991
|
§operator-config-env-defaults **Every package owns its knobs — `.env.defaults` is the standard.** Each package in the daemon's ecosystem — internal or third-party — ships a `.env.defaults` at its package root declaring its own knobs; the file is the package's configuration reference, traveling in the tarball and changing with the code that reads it. At boot the daemon assembles every installed member's file into one floor (membership = the `@plurnk/*` scope or a `plurnk` package.json field, gated by `PLURNK_PLUGINS_TRUSTED_ONLY` with discover()'s exact semantics), applies it set-if-unset under every operator source, and renders the assembled catalog to `~/.plurnk/.env.defaults`. The catalog is machine-owned, regenerated each boot, and never read back as configuration. A key claimed by two packages fails boot naming both. With the reader-declares discipline, each key has one implementation and one defaults owner.
|
|
1798
1992
|
|
|
1799
|
-
Model selection: separate alias cascade in `ProviderRegistry` ({§provider-instantiation}). `PLURNK_MODEL_<alias>=<provider>/<model-id>` declares; `PLURNK_MODEL=<alias>` selects. Aliases live in `.env`, not `.env.defaults` (operator-specific).
|
|
1993
|
+
Model selection: separate alias cascade in `ProviderRegistry` ({§provider-instantiation}). `PLURNK_MODEL_<alias>=<provider>/<model-id>` declares; `PLURNK_MODEL=<alias>` selects. Optional `PLURNK_MODEL_CHILD=<alias>` selects the default child provider; unset means inherit the spawning loop's provider. Aliases and selections live in `.env`, not `.env.defaults` (operator-specific).
|
|
1800
1994
|
|
|
1801
1995
|
| Var | Default | Purpose |
|
|
1802
1996
|
|-------------------------------------------------------------|---------|---------|
|
|
@@ -1807,23 +2001,26 @@ Model selection: separate alias cascade in `ProviderRegistry` ({§provider-insta
|
|
|
1807
2001
|
| `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. |
|
|
1808
2002
|
| `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). |
|
|
1809
2003
|
| §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}). |
|
|
1810
|
-
| `PLURNK_SERVICE_MAX_STRIKES` | `
|
|
1811
|
-
| `PLURNK_SERVICE_EMISSION_ATTEMPTS` | `3` | Completed provider responses allowed beneath one engine turn before an untrustworthy model-turn frame
|
|
2004
|
+
| `PLURNK_SERVICE_MAX_STRIKES` | `6` | Consecutive admitted-turn strike threshold ({§engine-rails}). |
|
|
2005
|
+
| `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. |
|
|
1812
2006
|
| `PLURNK_SERVICE_PREVIEW_LINES` | `16` | Maximum lines in an ordinary bounded log-body projection ({§body-projection}). |
|
|
1813
|
-
| `PLURNK_SERVICE_PREVIEW_CHARS` | `
|
|
2007
|
+
| `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}). |
|
|
2008
|
+
| `PLURNK_SERVICE_PROMPT_PROJECTION` | `25%` | Aggregate ruler-weight share of the enforced packet budget available to automatic prompt-body projection ({§prompt-projection}); alias-scoped overrides are supported. |
|
|
2009
|
+
| `PLURNK_SERVICE_LINE_ANCHOR_CONTEXT_LINES` | `2` | Complete neighboring lines hashed on each side of a model-facing line anchor ({§line-anchors}). |
|
|
2010
|
+
| `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES` | `2` | Surrounding and landed lines shown at each EDIT result boundary ({§edit-result-receipt-projection}). |
|
|
1814
2011
|
| `PLURNK_SERVICE_MIN_CYCLES` | `3` | Min repetitions before cycle detection fires ({§engine-rails}). |
|
|
1815
2012
|
| `PLURNK_SERVICE_MAX_CYCLE_PERIOD` | `4` | Max period length cycle detection examines ({§engine-rails}). |
|
|
1816
2013
|
| `PLURNK_SERVICE_REQUIEM_MAX_TOKENS` | `16384` | Initial forensic witness output allowance ({§digest-requiem}). |
|
|
1817
2014
|
| `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS` | `32768` | Retry allowance; must be at least the initial requiem allowance ({§digest-requiem}). |
|
|
1818
2015
|
| `PLURNK_SERVICE_MD_<ALIAS>` | (unset) | Operator reference doc: materializes `<path>` as `worker://plurnk/<ALIAS>.md`, auto-READ into every model worker's turn 0 ({§actor-boundary-doc-injection}). `~` expands to home. |
|
|
1819
|
-
| `PLURNK_SERVICE_FILES_ITEMS` | `-1` | Turn-0 catalog preview. Folder-capable schemes render a
|
|
2016
|
+
| `PLURNK_SERVICE_FILES_ITEMS` | `-1` | Turn-0 catalog preview. Folder-capable schemes render a one-level `*` map with `dir/**` rollups; kernel docs remain recursive and explicitly complete. `-1` = markerless first pages; positive `N` explicitly caps only file-map rows; `0` / unset = off ({§actor-boundary-catalog-preview}). |
|
|
1820
2017
|
| `PLURNK_SERVICE_PROPOSAL_TIMEOUT_MS` | (empty — waits indefinitely) | Finite positive milliseconds before cancellation with outcome `timeout`; empty waits, and every other explicit value fails ({§proposal-timeout-cancels}). |
|
|
1821
2018
|
|
|
1822
2019
|
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.
|
|
1823
2020
|
|
|
1824
2021
|
**Two override semantics — ceiling vs default.** Which kind a var is determines what "override" means across the cascade:
|
|
1825
2022
|
- **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.
|
|
1826
|
-
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `runLoop({alias})` request overrides it)
|
|
2023
|
+
- **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`).
|
|
1827
2024
|
|
|
1828
2025
|
§operator-config-shipped-defaults **The shipped `.env.defaults` is itself under
|
|
1829
2026
|
test.** It has no active `PLURNK_SERVICE_MD_*` doc alias because policy is a
|
|
@@ -1838,14 +2035,14 @@ template both ways: every `PLURNK_SERVICE_*` the service reads has a
|
|
|
1838
2035
|
declared `PLURNK_SERVICE_*` is read. A half-landed rename therefore fails a test
|
|
1839
2036
|
instead of a user's boot, and a dead knob cannot ship.
|
|
1840
2037
|
|
|
1841
|
-
§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:
|
|
2038
|
+
§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:
|
|
1842
2039
|
|
|
1843
2040
|
| Owner | Configuration |
|
|
1844
2041
|
|---|---|
|
|
1845
|
-
| `.env.test` |
|
|
2042
|
+
| `.env.test` | Safe default model selection and universal real-model gate posture; no alias declarations, routes, secrets, model tuning, or cost/sandbox ceilings. |
|
|
1846
2043
|
| Live/demo scripts | The repository personality path and runner topology. |
|
|
1847
2044
|
| Benchlets | Their snapshotted policy, workspace restrictions, and task-specific exceptions; direct env wins over the profile. |
|
|
1848
|
-
| Operator env/shell | Model selection, provider capability such as GBNF, endpoints, credentials, tuning, and deliberate ceiling overrides. |
|
|
2045
|
+
| Operator env/shell | Model alias declarations and explicit selection overrides, provider capability such as GBNF, endpoints, credentials, tuning, and deliberate ceiling overrides. |
|
|
1849
2046
|
| `test/setup.ts` | Mock-only alias, envelope, resource, storage, and isolation fixtures; unit/integration never consume the real-model profile. |
|
|
1850
2047
|
|
|
1851
2048
|
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.
|
|
@@ -1890,7 +2087,7 @@ The composition families remain distinct so one setting's semantics never
|
|
|
1890
2087
|
leak into another.
|
|
1891
2088
|
|
|
1892
2089
|
*Defaults — explicit-wins (the client replaces/merges freely):*
|
|
1893
|
-
- §operator-config-workspace-files-items `settings.filesItems` (number) **replaces** `PLURNK_SERVICE_FILES_ITEMS` for the workspace: a one-shot opens clean (`0`, no preview),
|
|
2090
|
+
- §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.
|
|
1894
2091
|
- §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.
|
|
1895
2092
|
|
|
1896
2093
|
*Ceilings — most-restrictive-wins (the client may only narrow, never widen):*
|
|
@@ -1968,7 +2165,7 @@ Its function names are transport-neutral library calls, not public wire names.
|
|
|
1968
2165
|
| §methods-event-subscribe Events | `subscribeToEvents(handler) -> unsubscribe` | Subscribes to the raw event source in {§notifications}. A subscriber failure is logged and cannot re-enter engine control flow. |
|
|
1969
2166
|
| §proposal-list Proposals | `pendingProposals(workspaceId)` | Intersects durable proposed rows with the lifecycle owner's live resolution waiters, then returns their validated {§proposal-projection}; persistence alone cannot advertise an unresolvable client interrupt. |
|
|
1970
2167
|
| §methods-proposal-resolve Proposals | `resolveProposal(logEntryId, resolution)` | Validates and delivers one accept, reject, or cancel decision to the engine. An unknown or already-resolved id fails; the client protocol owns how the decision arrived. |
|
|
1971
|
-
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, maxTurns?, flags?, openPaths?, alias?, model? })` | Validates a model worker and provider
|
|
2168
|
+
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, maxTurns?, flags?, openPaths?, alias?, model?, childAlias?, childModel? })` | Validates a model worker and provider policy, persists it with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. The exact terminal result arrives only through `loop/terminated`; parking and resuming do not replace the loop. |
|
|
1972
2169
|
| §methods-loop-cancel Loops | `cancelDrain(workerId, reason?)` | Begins durable structured cancellation of the worker tree and reaps its process-local scopes. The boolean reports whether process-local work existed when called; queued or parked durable work is still terminalized when it is `false`. |
|
|
1973
2170
|
| §methods-op-mirror Client dispatch | `dispatchClientAction({ workspaceId, workerId, statements })` | Dispatches already-parsed grammar statements as one client action and one journal segment. Every statement is an ordered turn, and every committed `log/entry` is emitted before the action promise resolves; a proposal may keep that promise and segment open until resolution. Core exposes no per-op method family. |
|
|
1974
2171
|
| 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}). |
|
|
@@ -2041,6 +2238,16 @@ loop with a conflicting selection fails before work is accepted. Provider
|
|
|
2041
2238
|
instances are cached; no resume path substitutes a boot default for missing or
|
|
2042
2239
|
malformed durable selection.
|
|
2043
2240
|
|
|
2241
|
+
§methods-loop-run-child-provider **Child-provider selection is one durable
|
|
2242
|
+
subcall policy.** Optional `childModel` (client-resolved `<provider>/<model>`, wins) or
|
|
2243
|
+
`childAlias` selects the provider for every WORK/FORK descendant and BARE inference; omitted uses
|
|
2244
|
+
`PLURNK_MODEL_CHILD`, while explicit `childAlias: null` means inherit. Core
|
|
2245
|
+
persists the resolved policy on each loop. A child runs on that provider and
|
|
2246
|
+
carries the same policy deeper; inherit uses the spawning loop's provider and
|
|
2247
|
+
remains inherit. BARE consumes the selection without spawning a child. Packet admission is unchanged: a smaller WORK is valid when
|
|
2248
|
+
its packet fits, and an oversized inherited FORK terminates through the ordinary
|
|
2249
|
+
child-loop result without preflight assembly or provider fallback.
|
|
2250
|
+
|
|
2044
2251
|
§methods-log-coordinate **Log coordinate.** Every `LogEntry` returned by
|
|
2045
2252
|
`readLog` or emitted through `log/entry` carries `loop_seq` and `turn_seq`
|
|
2046
2253
|
beside database ids, so a client can render and resolve the logical `L/T/S`
|
|
@@ -2048,26 +2255,28 @@ coordinate without fetching all rows and matching locally.
|
|
|
2048
2255
|
|
|
2049
2256
|
§methods-log-entry-wire **Log entry wire fidelity.** `readLog` and `log/entry`
|
|
2050
2257
|
preserve causal `source` and parse the row's JSON `attrs` into structured data;
|
|
2051
|
-
|
|
2258
|
+
they also project the row's complete sorted `tags` classification. Client
|
|
2259
|
+
interfaces do not reconstruct these fields from operation or origin.
|
|
2052
2260
|
|
|
2053
2261
|
§op-look **LOOK ownership.** A client-interface module owns the public LOOK
|
|
2054
2262
|
spelling and grammar parsing. It rewrites a valid LOOK statement to READ and
|
|
2055
2263
|
hands the AST to core's `look`; core owns the full resolver and the no-log
|
|
2056
2264
|
invariant. The internal closed, rowless observation segment supplies an honest
|
|
2057
2265
|
numeric loop coordinate for relative `log:///` addressing without leaving
|
|
2058
|
-
active lifecycle behind.
|
|
2266
|
+
active lifecycle behind. LOOK text anchors resolve through the same
|
|
2267
|
+
{§line-anchors} path as READ.
|
|
2059
2268
|
|
|
2060
2269
|
### §notifications Core events
|
|
2061
2270
|
|
|
2062
2271
|
| Event | Payload | When fired |
|
|
2063
2272
|
|--------------------------------------------------------------|---------|------------|
|
|
2064
2273
|
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A `log_entries` row is committed. |
|
|
2065
|
-
| §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
|
|
2066
|
-
| `loop/proposal`
|
|
2067
|
-
| `workspace/created`
|
|
2068
|
-
| `workspace/branch-batch`
|
|
2069
|
-
| §notifications-stream-event-on-channel-change `stream/event` | `{ entryId, target, channel, state, contentLength, mimetype?, loop_seq?, turn_seq?, sequence? }` | Channel content grows or channel state transitions. `
|
|
2070
|
-
| §notifications-stream-concluded `stream/concluded` | `{ entryId, target, subscriptionId, scheme, result, summary, wakeAction, wakeLoopId?, loop_seq?, turn_seq?, sequence? }` | A subscription closes. `target` is
|
|
2274
|
+
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, contextTokens, promptBudget, 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}; `contextTokens` and `promptBudget` are the latest-turn gauge pair, and `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. |
|
|
2275
|
+
| §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}. |
|
|
2276
|
+
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
2277
|
+
| §notifications-workspace-branch-batch `workspace/branch-batch` | Branch-batch lifecycle payload | A branch batch enters queued, running, completed, failed, or recovery-required state. |
|
|
2278
|
+
| §notifications-stream-event-on-channel-change `stream/event` | `{ entryId, workerId, target, channel, state, contentLength, mimetype?, loop_seq?, turn_seq?, sequence? }` | Channel content grows or channel state transitions. `workerId` is the entry owner and read perspective; `target` is its canonical URI. The optional coordinate is copied from schemes whose addresses carry one. Core-managed channel writes include the current stored `mimetype`, which may change per call ({§channel-mimetype}); the generic plugin notification capability does not require it. It carries metadata, not content; consumers read bytes from the stated worker perspective. |
|
|
2279
|
+
| §notifications-stream-concluded `stream/concluded` | `{ entryId, workerId, target, subscriptionId, scheme, result, summary, wakeAction, wakeLoopId?, loop_seq?, turn_seq?, sequence? }` | A subscription closes. `workerId` identifies the entry owner; `target` is its canonical URI. The optional coordinate is copied from schemes whose addresses carry one, so clients never parse it back out of `target`. Exact result truth is preserved; `wakeAction` records whether core resumed a parked loop, folded into an active loop, skipped an aborted/cancelled worker, or found no loop. |
|
|
2071
2280
|
| §notifications-notice-event `notice/event` | `{ loopId, notice: Notice }` | A transient observation or progress notice occurs. It cannot alter durable history, scheduling, recovery, or model-visible failure truth. |
|
|
2072
2281
|
|
|
2073
2282
|
§notifications-stream-event-failure-isolation The plugin-facing
|
|
@@ -2136,12 +2345,12 @@ Conditional absence never reorders the surviving default sections.
|
|
|
2136
2345
|
| Order | Slot | Section | Wire contract |
|
|
2137
2346
|
|------:|:-------|:----------------------|:--------------|
|
|
2138
2347
|
| 1 | system | `definition` | Framework definition; leads the most stable prefix. |
|
|
2139
|
-
| 2 | system | `
|
|
2140
|
-
| 3 | system | `
|
|
2141
|
-
| 4 | system | `
|
|
2142
|
-
| 5 | system | `
|
|
2143
|
-
| 6 | system | `
|
|
2144
|
-
| 7 | system | `
|
|
2348
|
+
| 2 | system | `system-policy` | Operator policy; empty content is omitted on the wire. |
|
|
2349
|
+
| 3 | system | `project-policy` | Project policy; empty content is omitted on the wire. |
|
|
2350
|
+
| 4 | system | `tools` | Executable capability sheet for this loop. |
|
|
2351
|
+
| 5 | system | `optional-operations` | Present only when optional operations are enabled. |
|
|
2352
|
+
| 6 | system | `schemes` | Active resource catalogue. |
|
|
2353
|
+
| 7 | system | `inject` | Present only when operator notes are configured. |
|
|
2145
2354
|
| 8 | user | `log` | Append-mostly model-visible history. |
|
|
2146
2355
|
| 9 | user | `child-streams` | Per-turn status; empty content is omitted. |
|
|
2147
2356
|
| 10 | user | `child-workers` | Per-turn status; empty content is omitted. |
|
|
@@ -2150,16 +2359,14 @@ Conditional absence never reorders the surviving default sections.
|
|
|
2150
2359
|
| 13 | user | `git` | Per-turn workspace status; empty content is omitted. |
|
|
2151
2360
|
| 14 | user | `budget` | Model-facing packet pressure; omitted when capacity is unknown. |
|
|
2152
2361
|
| 15 | user | `prompt` | Current prompt-entry pointers. |
|
|
2153
|
-
| 16 | user | `requirements` | Syntax recap deliberately nearest generation. |
|
|
2154
2362
|
|
|
2155
2363
|
The order favors prefix-cache locality where semantics permit: the definition
|
|
2156
|
-
|
|
2157
|
-
volatile user-status clump. It does **not** claim that every system byte is
|
|
2364
|
+
and privileged policy lead loop-dependent capabilities, while the append-mostly
|
|
2365
|
+
log leads the volatile user-status clump. It does **not** claim that every system byte is
|
|
2158
2366
|
immutable or that the complete packet is globally monotone in volatility:
|
|
2159
|
-
capabilities, operator notes, and policies can change
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
stays in the user slot.
|
|
2367
|
+
capabilities, operator notes, and policies can change. Trust is a separate
|
|
2368
|
+
admission rule. The system slot contains trusted control-plane material;
|
|
2369
|
+
attacker-reachable content stays in the user slot.
|
|
2163
2370
|
|
|
2164
2371
|
#### §packet-plugin-transform Trusted whole-list extension seam
|
|
2165
2372
|
|
|
@@ -2182,48 +2389,58 @@ grinding and folding remain closed engine concerns.
|
|
|
2182
2389
|
Token accounting distinguishes the artifact being measured, the unit, and the
|
|
2183
2390
|
time of measurement.
|
|
2184
2391
|
|
|
2185
|
-
| Quantity
|
|
2186
|
-
|
|
2187
|
-
| Provider usage
|
|
2188
|
-
| Packet render-weight
|
|
2189
|
-
| Stored content-depth
|
|
2190
|
-
| §tokenomics-
|
|
2392
|
+
| Quantity | Source and unit | When | Contract |
|
|
2393
|
+
|:--------------------------------------------------------------------|:------------------------------------------------------------------|:------------------------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
2394
|
+
| Provider usage | Provider-reported input/output tokens and cache/reasoning details | After each physical request | Durable monetary and transport forensics under {§provider-usage}; never the pre-call packet budget. |
|
|
2395
|
+
| Packet render-weight | `rulerCount = ceil(chars/2)` over rendered slots | Every packet build | Drives the Budget readout and grinder. |
|
|
2396
|
+
| Stored content-depth | The same ruler over entry/log content | When content is written | Weights catalog and log rows without per-model workspace state. |
|
|
2397
|
+
| §tokenomics-context-envelope-admission Hard context-envelope admission | Provider measurement of the complete `PacketWire` message request | After the grinder leaves negative ruler pressure | Exact counts and proven upper bounds may admit the request within the effective total envelope. Estimates and unknown capacity fail closed. Never model-facing. |
|
|
2191
2398
|
|
|
2192
2399
|
- §tokenomics-tokens-stored-at-write **Ruler tokens, stored at write.** `entry_channels.tokens` and `log_entries.tokens` are populated with the model-independent `rulerCount` when their content is written. The stored number is a stable content-depth measurement, not a provider-specific prediction.
|
|
2193
2400
|
- §tokenomics-render-weight-budget **Render-weight budget.** The budget headline — `ceiling`, `tokenUsage`, `tokensFree` — is the ruler measurement of the *assembled packet* after section transforms and budget substitution. Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns the final values into those same widths; final substitution is length-invariant and displayed usage equals the stored request weight. A `SUM` of stored content-depth would measure different artifacts and cannot substitute for packet render-weight.
|
|
2194
2401
|
- §tokenomics-context-percent **Prompt-budget percent.** The headline carries usage as a percent of the enforced model-facing budget — `usage Y (P%)` — beside the absolutes. It reads the ceiling already in hand; no extra provider call.
|
|
2195
|
-
- §tokenomics-window-partition **
|
|
2196
|
-
separate.** The provider owns
|
|
2197
|
-
|
|
2402
|
+
- §tokenomics-window-partition **Hard capacity and virtual pressure are
|
|
2403
|
+
separate.** The provider owns the effective total context envelope and
|
|
2404
|
+
generation settings. The envelope is already the minimum of model capacity
|
|
2405
|
+
and any stricter operator context cap ({§model-fact-resolution}); that cap is
|
|
2406
|
+
final. Core derives the natural prompt gauge from the envelope and its packing-safety
|
|
2198
2407
|
margin. `PLURNK_SERVICE_PROMPT_BUDGET` is an optional alias-scoped virtual
|
|
2199
2408
|
ceiling:
|
|
2200
2409
|
`promptBudget = min(configuredPromptBudget, naturalPromptCapacity)`. Unset
|
|
2201
2410
|
uses the natural capacity. The virtual ceiling controls only the packet gauge
|
|
2202
|
-
and grinder; it never changes
|
|
2411
|
+
and grinder; it never changes the hard context envelope, reasoning, completion, or
|
|
2203
2412
|
`maxTokens`.
|
|
2413
|
+
- §tokenomics-prompt-projection-share **Prompt projection is stable packet policy.**
|
|
2414
|
+
`PLURNK_SERVICE_PROMPT_PROJECTION` is a required alias-scoped percentage in
|
|
2415
|
+
`(0, 100)`. It allocates that share of the enforced `promptBudget` to the
|
|
2416
|
+
aggregate automatic prompt-body projection. It does not bound stored prompt
|
|
2417
|
+
size, provider capacity, an explicit READ/FIND result, or the complete packet.
|
|
2418
|
+
Basing the share on the stable ceiling rather than current free weight keeps
|
|
2419
|
+
one prompt's projection byte-stable as the worker log evolves.
|
|
2204
2420
|
- §tokenomics-window-unpollable-deliberate **Unknown provider capacity stays
|
|
2205
2421
|
unknown.** Without a configured virtual ceiling, an unknown provider window
|
|
2206
2422
|
leaves the prompt uncapped and omits denominator-dependent gauge telemetry. A
|
|
2207
|
-
configured virtual ceiling still
|
|
2208
|
-
to describe
|
|
2209
|
-
|
|
2423
|
+
configured virtual ceiling still supplies PLURNK's curation gauge without
|
|
2424
|
+
pretending to describe hard capacity. A negative-gauge request cannot be
|
|
2425
|
+
admitted without a known effective context and generation envelope.
|
|
2210
2426
|
|
|
2211
2427
|
§tokenomics-client-gauge **The client gauge pairs current occupancy with its
|
|
2212
2428
|
effective ceiling.** A model switch changes both latest-turn values together;
|
|
2213
|
-
the loop
|
|
2429
|
+
the loop's aggregate provider accounting remains monetary evidence, not a gauge
|
|
2430
|
+
input.
|
|
2214
2431
|
|
|
2215
|
-
| Surface | `contextTokens`
|
|
2216
|
-
| -------------------------- |
|
|
2217
|
-
| `loop/terminated.usage` |
|
|
2218
|
-
| `providers.list` alias row | not present
|
|
2432
|
+
| Surface | `contextTokens` | `promptBudget` |
|
|
2433
|
+
| -------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
2434
|
+
| `loop/terminated.usage` | `inputTokens` from the latest settled physical request on the latest turn; `null` when absent or unknown | Latest turn's enforced packet allowance; `null` when uncapped or unknown |
|
|
2435
|
+
| `providers.list` alias row | not present | Active alias's current enforced packet allowance; `null` when not established |
|
|
2219
2436
|
|
|
2220
2437
|
- **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}
|
|
2221
2438
|
- §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.
|
|
2222
|
-
- §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. The complete model-facing ledger therefore uses `rulerCount = ceil(chars/2)`: one content has one number regardless of which model reads it, with no per-model workspace state or recount pass.
|
|
2223
|
-
- §tokenomics-neutral-telemetry **Budget telemetry is state,
|
|
2439
|
+
- §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. The complete model-facing ledger therefore uses `rulerCount = ceil(chars/2)`: one content has one number regardless of which model reads it, with no per-model workspace state or recount pass. Nonnegative-gauge packets rely on that ruler. Only a negative-gauge packet invokes the shipping provider's request-shaped hard-envelope measurement.
|
|
2440
|
+
- §tokenomics-neutral-telemetry **Budget telemetry is state, with one pressure alarm.** The model-facing Budget ordinarily has exactly one line: token ceiling, current usage and percentage, and free tokens. When free tokens are negative, exactly one second line reads `Context Token Budget Panic: YOU MUST FOLD or KILL enough less-relevant log items to restore free tokens.` Per-entry weights remain on log rows where they describe the entries themselves. Packet-level composition, rankings, and visualizations are absent. OPEN/FOLD/KILL remain the ordinary curation capabilities; no deterministic recovery process is prescribed.
|
|
2224
2441
|
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as a stable per-content identity. `tokens` is stored in ruler units beside that content and is never keyed or recomputed by model.
|
|
2225
|
-
- §tokenomics-provider-usage **Provider
|
|
2226
|
-
- §tokenomics-
|
|
2442
|
+
- §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.
|
|
2443
|
+
- §tokenomics-negative-pressure **Negative ruler pressure is honest and nonterminal.** The readout describes the packet the model actually receives: usage and percent may exceed the virtual ceiling, and free tokens equal `ceiling - usage` without flooring. Crossing the ceiling records {§grinder-overflow-problem}, then the grinder ({§grinder}) folds only the newest boundary. If the rebuilt packet remains negative but exact or proven-bounded request evidence fits the effective hard envelope, generation proceeds under the ordinary operation contract. Overflow creates no strike or one-turn quota; only failed hard-envelope admission terminally rejects generation.
|
|
2227
2444
|
|
|
2228
2445
|
### §membership Workspace identity, membership, disk co-location
|
|
2229
2446
|
|
|
@@ -2264,15 +2481,15 @@ query is the absolute identity ({§scheme-address-network}); the sanitized
|
|
|
2264
2481
|
readable projection is the fragmentless default, while faithful DOM, origin
|
|
2265
2482
|
media type, and projection identity remain explicit auxiliary evidence. A
|
|
2266
2483
|
normal
|
|
2267
|
-
|
|
2484
|
+
`## READ0 (https://host/path?query)` therefore publishes only the sanitized body
|
|
2268
2485
|
under that exact URL—never raw HTML, response headers, or a channel-selection
|
|
2269
|
-
lesson. FIND
|
|
2486
|
+
lesson. FIND and embeddings consume the same stored readable
|
|
2270
2487
|
projection and never re-fetch each match. Because the search family is in
|
|
2271
2488
|
`PLURNK_SERVICE_EXEC_HOLD`, the cycle holds until acquisition concludes
|
|
2272
2489
|
({§exec-hold-until-concluded}), so the next packet contains final
|
|
2273
2490
|
materialization verdicts and folded ambient rows for every acquired page.
|
|
2274
2491
|
|
|
2275
|
-
§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
|
|
2492
|
+
§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.
|
|
2276
2493
|
|
|
2277
2494
|
**Git is the substrate and the repository is the boundary:**
|
|
2278
2495
|
|
|
@@ -2296,7 +2513,8 @@ materialization verdicts and folded ambient rows for every acquired page.
|
|
|
2296
2513
|
and directs the operator back to the default. The isomorphic untracked scan
|
|
2297
2514
|
remains differential-gated against native
|
|
2298
2515
|
`ls-files --others --exclude-standard`.
|
|
2299
|
-
- §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
|
|
2516
|
+
- §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.
|
|
2517
|
+
- §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.
|
|
2300
2518
|
|
|
2301
2519
|
**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.
|
|
2302
2520
|
- §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.
|
|
@@ -2318,7 +2536,13 @@ identity, and terminal disposition without exposing raw bytes or a base64 lane.
|
|
|
2318
2536
|
| Binary with readable projection | Derived Unicode as `text/markdown` | READ uses the projection; source-aware EDIT remains 415. |
|
|
2319
2537
|
| Binary without projection/over cap | Empty marker under the source binary mimetype | READ and EDIT return 415; private metadata distinguishes unavailable from limit. |
|
|
2320
2538
|
|
|
2321
|
-
§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
|
|
2539
|
+
§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`.
|
|
2540
|
+
|
|
2541
|
+
The artifact also retains a positive `{§mimetype-parse-issues}` count when the
|
|
2542
|
+
exact parsed body reported one. It remains advisory alongside a normally
|
|
2543
|
+
completed semantic disposition; zero and unavailable evidence persist as
|
|
2544
|
+
absence. Catalog projection attaches it only to that parsed body channel, never
|
|
2545
|
+
to sibling channels whose content the artifact does not describe.
|
|
2322
2546
|
|
|
2323
2547
|
Every completed artifact records one terminal disposition: `vector`, `lexical`
|
|
2324
2548
|
(only no embedder or an operator size ceiling), `excluded` (the configured
|
|
@@ -2338,11 +2562,11 @@ Lossless chunk admission requires either the embedder's own counter or an exact
|
|
|
2338
2562
|
|
|
2339
2563
|
§semantic-max-embed-size **Embedding has an optional size posture.** `PLURNK_SERVICE_MAX_EMBED_SIZE` is an operator-set maximum UTF-8 byte size eligible for vectors; `0` is unlimited and is the shipped default. The measured value is the exact default body READ exposes and the embedder receives. Exhaustive embedding therefore remains the normal posture. When a nonzero ceiling rejects an oversized body, the entry remains directly readable with full graph and lexical indexing; only vectors are absent. The setting is folded into the deep derivation signature, so changing it honestly re-derives affected entries. Client notices report compact aggregate progress; the digest records every non-vector pathname, terminal disposition, and reason for forensic inspection.
|
|
2340
2564
|
|
|
2341
|
-
§membership-change-gated-sync **Sync is idempotent and change-gated.** Per turn, membership materializes every member's model-readable snapshot into its entry. Text with an unchanged disk signature is a stat-only no-op. Binary sources additionally compare the cached per-mimetype projection identity; unchanged bytes are never reacquired, while changed reader behavior rematerializes without fabricating a filesystem-divergence event. Coverage is exhaustive across the project repository while work is proportional to source or projection change. After a pass every member carries the current representation defined by {§membership-source-projection}.
|
|
2565
|
+
§membership-change-gated-sync **Sync is idempotent and change-gated.** Per turn, membership materializes every member's model-readable snapshot into its entry. Text with an unchanged disk signature is a stat-only no-op. The version token is either the observed `mtime:size` or the explicit `absent` state; an observed deletion removes the stale readable channels, and a later reappearance is therefore a new divergence rather than a first-sight materialization. Binary sources additionally compare the cached per-mimetype projection identity; unchanged bytes are never reacquired, while changed reader behavior rematerializes without fabricating a filesystem-divergence event. Coverage is exhaustive across the project repository while work is proportional to source or projection change. After a pass every member carries the current representation defined by {§membership-source-projection}.
|
|
2342
2566
|
|
|
2343
2567
|
§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.
|
|
2344
2568
|
|
|
2345
|
-
§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
|
|
2569
|
+
§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.
|
|
2346
2570
|
|
|
2347
2571
|
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.
|
|
2348
2572
|
|
|
@@ -2352,63 +2576,51 @@ The CAS is the **hard backstop**, at the moment of writing, on every accept path
|
|
|
2352
2576
|
|
|
2353
2577
|
**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.
|
|
2354
2578
|
|
|
2355
|
-
**Schema.** The version-1 baseline stores
|
|
2579
|
+
**Schema.** The version-1 baseline stores logical model calls beneath turns,
|
|
2580
|
+
emission admission as their specialization, and cardinal physical requests
|
|
2581
|
+
beneath each call. Its constraints distinguish pending calls, response
|
|
2582
|
+
evidence, and response-less errors while monetary classification remains
|
|
2583
|
+
explicit.
|
|
2356
2584
|
|
|
2357
2585
|
### §grinder Budget enforcement: the grinder
|
|
2358
2586
|
|
|
2359
|
-
The grinder is the one pre-provider
|
|
2360
|
-
|
|
2587
|
+
The grinder is the one pre-provider curation path for the model-facing prompt
|
|
2588
|
+
gauge. Hard admission remains a separate predicate over the effective total
|
|
2589
|
+
context envelope:
|
|
2361
2590
|
|
|
2362
2591
|
```mermaid
|
|
2363
2592
|
flowchart TD
|
|
2364
|
-
assemble["Assemble and measure<br/>request packet"] --> policy{"
|
|
2593
|
+
assemble["Assemble and measure<br/>request packet"] --> policy{"Nonnegative<br/>ruler gauge?"}
|
|
2365
2594
|
policy -->|yes| unchanged["Provider generate<br/>packet unchanged"]
|
|
2366
|
-
policy -->|no|
|
|
2595
|
+
policy -->|no| problem["Record nonterminal 413<br/>Token Budget Overflow"]
|
|
2596
|
+
problem --> fold["FOLD newest boundary<br/>and its OPEN effects<br/>tag overflow"]
|
|
2367
2597
|
fold --> rebuild["Rebuild and remeasure"]
|
|
2368
|
-
rebuild --> folded{"
|
|
2369
|
-
folded -->|yes|
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
finalPolicy -->|no| candidate
|
|
2374
|
-
candidate --> physical{"Candidate physically sendable?"}
|
|
2375
|
-
physical -->|no| stop["413 hard stop<br/>no provider call"]
|
|
2376
|
-
physical -->|yes| grant{"Recovery grant unused?"}
|
|
2377
|
-
grant -->|no| stop
|
|
2378
|
-
grant -->|yes| steer["Record recovery Problem<br/>allow PLAN / FOLD / KILL / SEND<br/>rebuild"]
|
|
2379
|
-
steer --> finalPhysical{"Final packet physically sendable?"}
|
|
2380
|
-
finalPhysical -->|yes| recovery["One constrained<br/>provider call"]
|
|
2381
|
-
finalPhysical -->|no| stop
|
|
2598
|
+
rebuild --> folded{"Nonnegative<br/>ruler gauge?"}
|
|
2599
|
+
folded -->|yes| recovered["Provider generate<br/>with folded packet"]
|
|
2600
|
+
folded -->|no| admission{"Exact or bounded request<br/>fits hard envelope?"}
|
|
2601
|
+
admission -->|yes| pressured["Provider generate<br/>with negative Budget alarm"]
|
|
2602
|
+
admission -->|no / unknown / estimate| stop["413 hard stop<br/>no provider call"]
|
|
2382
2603
|
```
|
|
2383
2604
|
|
|
2384
2605
|
§grinder-overflow-only **The grinder fires only on actual overflow.** In
|
|
2385
2606
|
`Engine.runTurn`, after `PacketBuilder.buildRequestPacket` assembles the request
|
|
2386
2607
|
and before `provider.generate`, it compares the packet's render-weight
|
|
2387
|
-
({§tokenomics}) with the
|
|
2388
|
-
ships untouched
|
|
2608
|
+
({§tokenomics}) with the virtual ceiling. At or under the ceiling, the packet
|
|
2609
|
+
ships untouched. The grinder never trims
|
|
2389
2610
|
speculatively or "helpfully."
|
|
2390
2611
|
|
|
2391
|
-
- §grinder-
|
|
2392
|
-
- §grinder-
|
|
2393
|
-
- §grinder-
|
|
2394
|
-
- §grinder-hard-413
|
|
2395
|
-
|
|
2396
|
-
- §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 the allowed operation set. No ambient packet text prescribes an ordering strategy.
|
|
2612
|
+
- §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.
|
|
2613
|
+
- §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.
|
|
2614
|
+
- §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.
|
|
2615
|
+
- §grinder-hard-413 **The terminal hard stop belongs only to effective-envelope admission.** If the rebuilt packet remains ruler-negative, Core measures the complete `PacketWire` request and reserves the configured generation envelope. Exact or proven-bounded evidence within `provider.contextWindow` admits an ordinary turn regardless of ruler debt. Unknown capacity, an estimate, or evidence exceeding the effective prompt capacity abandons the loop immediately at **413 Content Too Large** without calling `provider.generate`. The terminal request stores one `engine/context/context-envelope-admission-failed` Problem with ruler pressure and admission evidence, distinct from the preceding nonterminal overflow 413; it is failure forensics, not a model recovery turn.
|
|
2397
2616
|
|
|
2398
|
-
- §
|
|
2617
|
+
- §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 ceiling, 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 ruler debt follows {§tokenomics-negative-pressure}.
|
|
2399
2618
|
|
|
2400
|
-
§
|
|
2401
|
-
per-turn rail verdict, so an overflow contributes one strike under
|
|
2402
|
-
{§engine-rails}. If cycle detection also fires on that turn, the turn still
|
|
2403
|
-
contributes only one strike and a threshold crossing is classified as 508.
|
|
2619
|
+
- §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` effective context-envelope admission failed · `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.
|
|
2404
2620
|
|
|
2405
|
-
§grinder-
|
|
2406
|
-
is no soft exemption. Folded rows still cost their coordinate lines, so
|
|
2407
|
-
repeated overflow can legitimately reach the strike threshold.
|
|
2621
|
+
§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 ruler pressure remains negative but hard admission succeeds, the Budget section also reports the negative free-token value and its one panic line ({§tokenomics-neutral-telemetry}). The 413 diagnoses the overflow without striking, terminalizing the turn, or changing the ordinary operation contract.
|
|
2408
2622
|
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
The model controls its context; the engine enforces packet physics without
|
|
2623
|
+
The model controls its context; the engine enforces the hard envelope without
|
|
2412
2624
|
choosing what older history matters. The same boundary applies on turn 1 and
|
|
2413
2625
|
turn 101. The grinder folds reversibly, never deletes, and never performs
|
|
2414
2626
|
speculative or non-overflow trimming.
|
|
@@ -2435,7 +2647,8 @@ flowchart LR
|
|
|
2435
2647
|
pre-turn, a worker materializes every other actor's event on shared state after
|
|
2436
2648
|
its last completed observation boundary into its own log. The set is
|
|
2437
2649
|
exhaustive, unranked, and exactly once; the engine makes no relevance decision.
|
|
2438
|
-
Each copied event retains its effect, cause,
|
|
2650
|
+
Each copied event retains its effect, cause, typed attributes, and initial
|
|
2651
|
+
log classifications ({§log-item-tags}). Every
|
|
2439
2652
|
producer appends to one workspace-scoped occurrence journal with a monotonic
|
|
2440
2653
|
identity. A pull captures one closed `(worker cursor, high-water]` interval,
|
|
2441
2654
|
materializes each identity idempotently, then advances the cursor only after the
|
|
@@ -2462,7 +2675,7 @@ cross this door, while an ancestry-authorized explicit READ remains available.
|
|
|
2462
2675
|
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
2463
2676
|
| §env-delta-sibling-edit Sibling commons mutation | The sibling's successful resolved `EDIT` row and its receipt. | One folded `EDIT` retaining the exact effect and typed attributes. |
|
|
2464
2677
|
| §env-delta-kernel-entry-edit Kernel-published mutation | The reserved `plurnk` worker's successful resolved `EDIT` row on `worker://plurnk/...`. | One folded `EDIT` retaining the published authority, exact effect, and typed attributes. |
|
|
2465
|
-
| §env-delta-filesystem-narration Project-file divergence | The reserved `plurnk` worker records one `source=file` EDIT-shaped event during pre-turn membership reconciliation. | One folded `EDIT` naming the file and carrying the net changed span. No model operation is fabricated as having run.
|
|
2678
|
+
| §env-delta-filesystem-narration Project-file divergence | The reserved `plurnk` worker records one `source=file` EDIT-shaped event during pre-turn membership reconciliation. | One folded `EDIT` naming the file and carrying the net changed span plus the exact two-coordinate Git porcelain status in `git` metadata when Git reports that path. No model operation is fabricated as having run. |
|
|
2466
2679
|
| §env-delta-entry-materialization Executor `entry()` sink | The reserved `plurnk` worker records a typed `EDIT` event with `kind="entry_materialized"` and the calling worker as cause. | One folded system `READ` projection advertising newly readable state; the durable event remains an EDIT for replay and forensics ({§exec-entry-sink}). |
|
|
2467
2680
|
|
|
2468
2681
|
§env-delta-attribution **Ownership, authorship, and cause are independent.**
|
|
@@ -2521,16 +2734,22 @@ the aggregate remains dispatch coordination state.
|
|
|
2521
2734
|
| -------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
2522
2735
|
| 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. |
|
|
2523
2736
|
| `unit`, `before`, `after` | `extent` | Whole-line batches use line counts. A batch containing any exact four-coordinate edit uses Unicode code-point counts. |
|
|
2737
|
+
| `parseIssues` | `parseIssues` | Positive parser-recovery count for the complete landed revision; clean, unsupported, and unavailable evidence is omitted. |
|
|
2524
2738
|
| `effect.requested`, `source`, `result` | `range` | The admitted marker and its normalized mapping from the common source snapshot into the landed body. |
|
|
2525
2739
|
| `effect.removed`, `inserted` | `change` | Removed and inserted counts in the receipt unit. |
|
|
2526
|
-
| `effect.context` | Canonical row body | Numbered physical lines
|
|
2740
|
+
| `effect.context` | Canonical row body | Numbered physical lines at each landed boundary, bounded symmetrically by `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES`. |
|
|
2527
2741
|
| `disposition`, `requested` | `disposition`, `requested` | A reviewer-replaced batch preserves the authored marker while stating that its attributed effect was superseded. |
|
|
2528
2742
|
| `replacement` | `replacement`, `change`, canonical proposal-owner body | The one whole-resource effect actually applied by the reviewer replacement; never duplicated across authored rows. |
|
|
2529
2743
|
|
|
2530
2744
|
§edit-result-receipt-truth **Receipts describe committed state.** Every row in
|
|
2531
|
-
one resource-channel EDIT batch carries the same landed revision and
|
|
2745
|
+
one resource-channel EDIT batch carries the same landed revision, extent, and
|
|
2746
|
+
optional positive `parseIssues` count for that complete revision.
|
|
2532
2747
|
When the proposed batch lands unchanged, each row also carries its own requested
|
|
2533
|
-
marker, source/result mapping, counts, and context.
|
|
2748
|
+
marker, source/result mapping, counts, and context. For configured count `C`,
|
|
2749
|
+
the context contains up to `C` surrounding lines and the first and last `C`
|
|
2750
|
+
landed lines at the result boundaries. Overlapping windows coalesce; coordinate
|
|
2751
|
+
jumps expose an omitted middle. A deletion instead shows up to `C` lines on
|
|
2752
|
+
each side of its join.
|
|
2534
2753
|
|
|
2535
2754
|
§edit-result-reviewer-replacement **A resolver replacement is one effect, not a
|
|
2536
2755
|
guess at authorship.** An arbitrary accepted body replaces the batch's proposed
|
|
@@ -2576,7 +2795,10 @@ landed.
|
|
|
2576
2795
|
| `304`, rejection, or cancellation with no landed mutation | `effects` omitted. |
|
|
2577
2796
|
| Cross-selection MOVE source failure after destination success | The failure retains every destination effect that landed. |
|
|
2578
2797
|
|
|
2579
|
-
Core validates the complete ordered array before exposing it.
|
|
2798
|
+
Core validates the complete ordered array before exposing it. Parser-recovery
|
|
2799
|
+
inspection is advisory and occurs against complete resulting text after
|
|
2800
|
+
successful application. A handler or parser failure emits a Notice, omits
|
|
2801
|
+
`parseIssues`, and never changes the mutation outcome.
|
|
2580
2802
|
|
|
2581
2803
|
### §proposal-ownership Loop auto and client YOLO
|
|
2582
2804
|
|
|
@@ -2606,7 +2828,7 @@ when an emission is admitted, its response.** Core assembles and measures the
|
|
|
2606
2828
|
request under {§packet-assembly}. An admitted response extends that same record
|
|
2607
2829
|
before the turn closes; a failed provider call or exhausted invalid emission
|
|
2608
2830
|
leaves the request-only record, while rejected exchanges remain in
|
|
2609
|
-
`turn_attempts`.
|
|
2831
|
+
`model_calls` with their classification in `turn_attempts`.
|
|
2610
2832
|
|
|
2611
2833
|
| Turn state | `turns.packet` |
|
|
2612
2834
|
| ----------------------------- | ----------------------------------------------- |
|
|
@@ -2654,7 +2876,7 @@ by service policy.
|
|
|
2654
2876
|
|--------------------------------|----------------|
|
|
2655
2877
|
| `worker` / `loop` / `turn` | The process hierarchy in {§lifecycle-terms}; unqualified `run` names no internal entity. |
|
|
2656
2878
|
| `packet` | The assembled address space, a kernel concept rather than merely a provider request. |
|
|
2657
|
-
| `costUsd` | No standard cost field exists; the explicit currency avoids implied units. |
|
|
2879
|
+
| `costUsd` | No standard cost field exists; the explicit currency avoids implied units, and the value remains an exact decimal string. |
|
|
2658
2880
|
| `PLURNK_SERVICE_SAFETY` | The ruler's packing margin is a service fact no provider owns. |
|
|
2659
2881
|
| the `chars/2` ruler | Model-agnostic by design ({§tokenomics-agnostic-ruler}). |
|
|
2660
2882
|
|
|
@@ -2662,26 +2884,31 @@ Retired terms stay retired: the lexicon guard rejects `thinking`, the unqualifie
|
|
|
2662
2884
|
|
|
2663
2885
|
§encrypted-reasoning-carrier **Encrypted reasoning is opaque client evidence.**
|
|
2664
2886
|
When a provider returns encrypted reasoning items, core attaches that list to
|
|
2665
|
-
the admitted model
|
|
2887
|
+
the admitted model-emission row's `attrs.reasoning`. `log/entry` and `readLog`
|
|
2666
2888
|
carry it to AG-UI, which may project correlated standard reasoning entities.
|
|
2667
2889
|
Core never decodes the blobs or renders them into a model packet; readable
|
|
2668
2890
|
reasoning text remains separate in `assistant.reasoning`. The provider-detail
|
|
2669
2891
|
identity and derived classification retain their exact provider-normalized
|
|
2670
2892
|
meaning from {§provider-encrypted-reasoning}; core never reinterprets either as
|
|
2671
|
-
a client entity. The mirror row and
|
|
2893
|
+
a client entity. The mirror row and logical model call remain the lossless
|
|
2672
2894
|
evidence when a downstream standard cannot represent the complete list.
|
|
2673
2895
|
|
|
2674
2896
|
§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:
|
|
2675
2897
|
|
|
2676
2898
|
| row producer | ordinary OPEN projection |
|
|
2677
2899
|
|---|---|
|
|
2678
|
-
|
|
|
2900
|
+
| any `READ` or `FIND` | complete selected operation result |
|
|
2901
|
+
| any `PLAN` | complete authored working memory |
|
|
2902
|
+
| actionless lowercase `prompt` | budgeted head under {§prompt-projection} |
|
|
2903
|
+
| structured `EDIT` receipt or textual `COPY`/`MOVE` effects | complete receipt-owned join context |
|
|
2679
2904
|
| every other nonempty body | head bounded independently by `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS` |
|
|
2680
2905
|
| bodyless row | `"display":"none","body":""` |
|
|
2681
2906
|
|
|
2682
|
-
|
|
2907
|
+
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.
|
|
2908
|
+
|
|
2909
|
+
§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.
|
|
2683
2910
|
|
|
2684
|
-
§prompt-
|
|
2911
|
+
§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 enforced `promptBudget` and the alias-resolved percentage from `PLURNK_SERVICE_PROMPT_PROJECTION` derive one aggregate ruler-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. Without an enforced packet ceiling 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.
|
|
2685
2912
|
|
|
2686
2913
|
§prompt-self-only The frame is self-only and owner-keyed:
|
|
2687
2914
|
`entries.owner_id` carries worker identity while the address carries only the
|
|
@@ -2701,13 +2928,13 @@ contract.
|
|
|
2701
2928
|
§packet-catalog **Catalogs are query results, not packet state.** The packet
|
|
2702
2929
|
stores no materialized manifest. Complete and one-level entry directories,
|
|
2703
2930
|
their row shape, and their ordering are ordinary FIND projections owned by
|
|
2704
|
-
{§find-result-
|
|
2931
|
+
{§find-result-projection}; persistent search derivation is a separate index.
|
|
2705
2932
|
|
|
2706
2933
|
### §operation-results Model-facing failures and notices
|
|
2707
2934
|
|
|
2708
2935
|
The model's runtime alert surface has two distinct kinds of information:
|
|
2709
2936
|
|
|
2710
|
-
- **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
|
|
2937
|
+
- **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}.
|
|
2711
2938
|
- **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.
|
|
2712
2939
|
|
|
2713
2940
|
The `log` is durable product truth. The `errors` section points at its failures
|
|
@@ -2744,7 +2971,7 @@ retain distinct contracts and lifetimes.
|
|
|
2744
2971
|
| failure | row | status |
|
|
2745
2972
|
|---|---|---|
|
|
2746
2973
|
| action failure | the failed op's own row; the owning scheme supplies Problem Details | 4xx/5xx |
|
|
2747
|
-
|
|
|
2974
|
+
| context-envelope admission | `op='error'`, origin `plurnk`, source `engine`; `engine/context/context-envelope-admission-failed` Problem Details | 413 |
|
|
2748
2975
|
| max commands exceeded | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/max-commands-exceeded` Problem Details | 429 |
|
|
2749
2976
|
| idle turn | `op='error'`, origin `plurnk`, source `rail`; `engine/rail/idle-turn` Problem Details | 409 |
|
|
2750
2977
|
|
|
@@ -2757,7 +2984,7 @@ retain distinct contracts and lifetimes.
|
|
|
2757
2984
|
|
|
2758
2985
|
§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.
|
|
2759
2986
|
|
|
2760
|
-
§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
|
|
2987
|
+
§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.
|
|
2761
2988
|
|
|
2762
2989
|
§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.
|
|
2763
2990
|
|
|
@@ -2768,10 +2995,10 @@ retain distinct contracts and lifetimes.
|
|
|
2768
2995
|
| 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. |
|
|
2769
2996
|
| `run({ dbPath })` | Reads the required database and writes a complete digest to `./test/digest` relative to the caller's working directory. |
|
|
2770
2997
|
| `digestDir` | Selects the output directory. `run` removes and recreates it so stale packet artifacts cannot survive; concurrent callers use distinct directories. |
|
|
2771
|
-
| `workerId` | Narrows workers and every dependent loop, turn, attempt,
|
|
2998
|
+
| `workerId` | Narrows workers and every dependent loop, turn, logical model call, emission attempt, physical request, and log row to that one worker. |
|
|
2772
2999
|
| `workspaceId` | Narrows workers and dependent evidence to one workspace; when both selectors are present they intersect. |
|
|
2773
3000
|
|
|
2774
|
-
§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,
|
|
3001
|
+
§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.
|
|
2775
3002
|
|
|
2776
3003
|
§digest-requiem **A requiem is an out-of-band forensic interview, not a worker
|
|
2777
3004
|
turn.** It cannot execute operations or alter the audited history.
|
|
@@ -2779,11 +3006,11 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
2779
3006
|
| Aspect | Contract |
|
|
2780
3007
|
|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
2781
3008
|
| Scope | One interview for each worker with model-bearing turns; journal-only workers are omitted. |
|
|
2782
|
-
| Evidence | The worker's final packet plus every
|
|
3009
|
+
| Evidence | The worker's final packet plus every attempt's exact normalized response and admission evidence; opaque raw transport remains in durable forensic artifacts. |
|
|
2783
3010
|
| Witness | An explicitly supplied provider or the active configured provider; absence fails hard. |
|
|
2784
|
-
| Identity | The worker's durable
|
|
3011
|
+
| 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. |
|
|
2785
3012
|
| 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`. |
|
|
2786
|
-
| Artifacts | `requiem.md` carries testimony and
|
|
3013
|
+
| 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. |
|
|
2787
3014
|
|
|
2788
3015
|
§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}).
|
|
2789
3016
|
|
|
@@ -2791,28 +3018,28 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
2791
3018
|
|
|
2792
3019
|
### §tools user.tools — the capability sheet
|
|
2793
3020
|
|
|
2794
|
-
§tools-capability-sheet The
|
|
3021
|
+
§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.
|
|
2795
3022
|
|
|
2796
3023
|
§tools-loop-affinity **The capability sheet describes the current loop.** The
|
|
2797
3024
|
sheet filters registered capabilities through the same
|
|
2798
3025
|
`SchemeRegistry.resolveForLoop(flags)` predicate the dispatcher enforces. When
|
|
2799
|
-
registered executors exist but EXEC is inactive, their
|
|
3026
|
+
registered executors exist but EXEC is inactive, their table is replaced by
|
|
2800
3027
|
an explicit disabled notice rather than silent absence. The dispatch 403 remains
|
|
2801
3028
|
the backstop and names the non-retryable loop restriction.
|
|
2802
3029
|
|
|
2803
|
-
**Contributors: the wired executor tags.**
|
|
3030
|
+
**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.
|
|
2804
3031
|
|
|
2805
|
-
### §schemes user.schemes — the
|
|
3032
|
+
### §schemes user.schemes — the resource directory
|
|
2806
3033
|
|
|
2807
|
-
§schemes-directory A `##
|
|
3034
|
+
§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.
|
|
2808
3035
|
|
|
2809
3036
|
### §inject system.inject — the operator injection
|
|
2810
3037
|
|
|
2811
|
-
§packet-inject When `PLURNK_SERVICE_PACKET_INJECT` names a readable markdown file, its content renders as an `## Operator Notes` section in the system slot
|
|
3038
|
+
§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.
|
|
2812
3039
|
|
|
2813
3040
|
### §policy system.policy — the client's policy injection
|
|
2814
3041
|
|
|
2815
|
-
§policy-sections Two sections ride the system slot **
|
|
3042
|
+
§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}).
|
|
2816
3043
|
|
|
2817
3044
|
On first run, and only when `~/.plurnk` itself is absent, the service seeds
|
|
2818
3045
|
`AGENTS.md` from `@plurnk/plurnk-meta/PLURNK_PERSONALITY.md` ({§teaching-corpus}).
|
|
@@ -2821,27 +3048,29 @@ surfaces with its cause and leaves no apparently initialized home.
|
|
|
2821
3048
|
After that bootstrap the file is user-owned: edits and deletion persist, and a
|
|
2822
3049
|
later boot never refreshes or recreates it.
|
|
2823
3050
|
|
|
2824
|
-
§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
|
|
2825
|
-
|
|
2826
|
-
### §requirements The requirements section — static per-turn rules
|
|
3051
|
+
§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.
|
|
2827
3052
|
|
|
2828
|
-
|
|
2829
|
-
under `## Recap`, closest to the assistant turn so the contract the model has
|
|
2830
|
-
to honor is the most recent text it sees.
|
|
2831
|
-
- §requirements-requirements-omitted-when-empty The header is omitted entirely
|
|
2832
|
-
when the requirements string is empty.
|
|
3053
|
+
### §packet-git-status The Git status section — compact repository state
|
|
2833
3054
|
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
3055
|
+
When Git is admitted for the workspace, `## Git Status` reports the current
|
|
3056
|
+
branch, upstream ahead/behind counts, and staged/unstaged/untracked totals. The
|
|
3057
|
+
active direct child of a running branch batch additionally receives its assigned
|
|
3058
|
+
branch and the requirement to commit any project changes and leave the checkout
|
|
3059
|
+
clean before concluding ({§worker-branch-batch-return}); no other worker receives
|
|
3060
|
+
that instruction. The section never repeats an unbounded path list. Per-path state belongs to
|
|
3061
|
+
the durable causal observation: a `source=file` environment-delta row carries
|
|
3062
|
+
the exact two-character porcelain `XY` value as `git` metadata when the status
|
|
3063
|
+
snapshot names that path. The engine takes one snapshot after membership
|
|
3064
|
+
reconciliation and uses it for both projections; no per-file Git process exists.
|
|
2839
3065
|
|
|
2840
|
-
|
|
3066
|
+
### §requirements Recap footer
|
|
2841
3067
|
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
3068
|
+
The user slot ends with `## Recap`, a compact recency-biased reminder of selected
|
|
3069
|
+
operational law already owned by `plurnk.md`. A non-empty `runLoop` / `runTurn`
|
|
3070
|
+
`requirements` value overrides the default; otherwise core reads
|
|
3071
|
+
`PLURNK_SERVICE_REQUIREMENTS` or the required meta-owned `requirements.md` source
|
|
3072
|
+
for every packet. A failed read fails packet assembly with its cause. The footer
|
|
3073
|
+
is one projection path and one authored source, not a second language contract.
|
|
2845
3074
|
|
|
2846
3075
|
## §matcher Matcher selection and text regions
|
|
2847
3076
|
|
|
@@ -2867,7 +3096,7 @@ stored a resource.
|
|
|
2867
3096
|
§graph-relations **Graph matching is one-hop, kind-agnostic name matching.**
|
|
2868
3097
|
Source definitions resolve over the complete relationship universe (the
|
|
2869
3098
|
workspace for entry FIND; the worker's complete log for log FIND), while the
|
|
2870
|
-
authored target
|
|
3099
|
+
authored target still constrains every resource returned. Outgoing
|
|
2871
3100
|
references belong to a definition through the handler-reported fully qualified
|
|
2872
3101
|
container identity.
|
|
2873
3102
|
|
|
@@ -2902,12 +3131,11 @@ Glob anchoring (`TODO*` starts-with, `*TODO*` contains, `*.log` ends-with,
|
|
|
2902
3131
|
|
|
2903
3132
|
§matcher-result-resource-selection **A matcher selects resources; it never extracts a value or chooses a retrieval
|
|
2904
3133
|
window.** Every dialect answers whether a resource matches and may return
|
|
2905
|
-
`MatchEvidence {
|
|
3134
|
+
`MatchEvidence { locator?, region? }` ({§matcher-selection-signal}). `locator` is a
|
|
2906
3135
|
canonical structural locator. `region` is a complete `TextRegion` in the exact
|
|
2907
3136
|
text the model can READ and may be exact or the smallest honest enclosing
|
|
2908
|
-
region. A matcher miss is 204. FIND
|
|
2909
|
-
|
|
2910
|
-
scope.
|
|
3137
|
+
region. A matcher miss is 204. FIND's target shape projects the selected
|
|
3138
|
+
resources according to {§find-result-projection}.
|
|
2911
3139
|
|
|
2912
3140
|
| Dialect | Selects | Natural use |
|
|
2913
3141
|
|---|---|---|
|
|
@@ -2918,15 +3146,12 @@ scope.
|
|
|
2918
3146
|
| `~`semantic `~q` | resources ranked by indexed chunks | chunk text region when available |
|
|
2919
3147
|
| `@`graph `@<sym` | resources with matching symbol relations | symbol text region when available |
|
|
2920
3148
|
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
guessing which occurrence or context window it wants. A locator-only or
|
|
2928
|
-
coordinate-less valid result still selects the resource; the service never
|
|
2929
|
-
fabricates coordinates. {§read-selection-projection}
|
|
3149
|
+
Match evidence is navigation evidence, never an implicit body projection. The
|
|
3150
|
+
model uses broad FIND to select and page resources, exact FIND to page that
|
|
3151
|
+
resource's locations, then explicit exact READs—parallel in one turn when
|
|
3152
|
+
useful—to retrieve chosen bodies or regions. A locator-only or coordinate-less
|
|
3153
|
+
valid result still selects the resource; the service never fabricates
|
|
3154
|
+
coordinates. {§read-exact-target} {§read-selection-projection}
|
|
2930
3155
|
|
|
2931
3156
|
### §text-scope-runtime Text-scope runtime projection
|
|
2932
3157
|
|
|
@@ -2953,7 +3178,7 @@ coordinates, inverted regions, out-of-range coordinates, and other arities are
|
|
|
2953
3178
|
416.
|
|
2954
3179
|
|
|
2955
3180
|
Every successful scoped READ carries its complete resolved `region` in the
|
|
2956
|
-
operation result and packet metadata. The body remains
|
|
3181
|
+
operation result and packet metadata. The body remains coordinate-prefixed from
|
|
2957
3182
|
`startLine`; the region preserves columns that line numbering cannot express.
|
|
2958
3183
|
|
|
2959
3184
|
Every same-resource mutation resolves its replacement offsets against one
|
|
@@ -2963,7 +3188,7 @@ SARIF region/replacement algebra for exact spans and same-snapshot ordering, not
|
|
|
2963
3188
|
adoption of the SARIF interchange envelope.
|
|
2964
3189
|
|
|
2965
3190
|
§slice-semantics-compose-pattern **Compose from evidence.** A match region already uses the four-coordinate
|
|
2966
|
-
scope shape. A follow-up
|
|
3191
|
+
scope shape. A follow-up `## READ0 (resource) <SL,SC,EL,EC>` retrieves that exact
|
|
2967
3192
|
region. JSONPath/XPath remain locators and matchers; they do not introduce a
|
|
2968
3193
|
second structural scope or structural EDIT language.
|
|
2969
3194
|
|
|
@@ -2984,15 +3209,18 @@ projection, and binary handling. Text scope meaning does not vary by mimetype.
|
|
|
2984
3209
|
|
|
2985
3210
|
### §render-rule Render rule
|
|
2986
3211
|
|
|
2987
|
-
§render-rule-line-navigable-prefix Every textual content body with a source
|
|
2988
|
-
prefix on each physical line, independent
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3212
|
+
§render-rule-line-navigable-prefix Every textual content body with a source
|
|
3213
|
+
`startLine` renders with a coordinate prefix on each physical line, independent
|
|
3214
|
+
of mimetype. A successful exact READ whose active scheme declares
|
|
3215
|
+
`textEditScopes: true` and model write authority supplies `@hash N:` with one
|
|
3216
|
+
or more ASCII spaces before `N` under
|
|
3217
|
+
{§line-anchors}; every other body renders `N:`. JSON, XML, and HTML are therefore just as
|
|
3218
|
+
line-addressable as markdown and source code. The prefix is a packet
|
|
3219
|
+
presentation aid, never part of canonical content; matchers and mutations
|
|
3220
|
+
consume canonical bytes before rendering. A producer may set `startLine: null`
|
|
3221
|
+
only when its content is already source-numbered, such as an effect receipt.
|
|
2994
3222
|
|
|
2995
|
-
§render-rule-find-renders-result A log row's canonical full body is resolved once by `LogBody`: READ/FIND
|
|
3223
|
+
§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.
|
|
2996
3224
|
|
|
2997
3225
|
An `EDIT` log row renders its bounded effect receipt (`rx.receipt`) as row
|
|
2998
3226
|
metadata and join context, not its input statement. Proposal-gated file EDITs
|
|
@@ -3003,7 +3231,8 @@ any scoped textual receipt contexts under their `log:///` address, never under
|
|
|
3003
3231
|
one operand's resource address. All generated bodies remain under
|
|
3004
3232
|
{§body-projection}. {§edit-result-render}
|
|
3005
3233
|
|
|
3006
|
-
|
|
3234
|
+
Numeric and anchored coordinate prefixes are presentation/reference per
|
|
3235
|
+
plurnk.md ("not part of the source"); matchers operate on canonical content.
|
|
3007
3236
|
|
|
3008
3237
|
### §markdown-primitive Mimetype primitive: text/markdown
|
|
3009
3238
|
|
|
@@ -3023,10 +3252,14 @@ Carried from the contract walk; durable.
|
|
|
3023
3252
|
- **Binary markers** → 415 for text operations. A readable binary source is durably represented as projected `text/markdown` under {§membership-source-projection}; source-aware File EDIT remains 415.
|
|
3024
3253
|
- **EDIT `<L>` on non-existent entry** → body becomes content; `<L>` is positional-only on existing content.
|
|
3025
3254
|
- §copy-l-source-range **COPY/MOVE source scope** selects only the addressed source channel and
|
|
3026
|
-
|
|
3255
|
+
first resolves and, when required, prepares the same canonical
|
|
3256
|
+
owner-addressed representation as exact READ/FIND. It transfers canonical
|
|
3257
|
+
text without the packet's coordinate prefix. A MOVE removes
|
|
3027
3258
|
that same selected region; an unscoped MOVE removes only the selected
|
|
3028
3259
|
channel, deleting the entry only when no channels remain. A binary marker
|
|
3029
|
-
is not transferable; a readable binary projection is already a textual
|
|
3260
|
+
is not transferable; a readable binary projection is already a textual
|
|
3261
|
+
channel. A selected producer failure aborts before destination mutation;
|
|
3262
|
+
successful non-`200` content remains transferable.
|
|
3030
3263
|
|
|
3031
3264
|
- **COPY/MOVE destination scope** is independent of the source scope and lowers
|
|
3032
3265
|
through the destination scheme's `editBatch`.
|
|
@@ -3035,10 +3268,11 @@ Carried from the contract walk; durable.
|
|
|
3035
3268
|
Any scoped textual transfer materializes create/update receipts; whole-channel
|
|
3036
3269
|
changes do not. Operand selections remain independently visible per
|
|
3037
3270
|
{§copy-move-observation}.
|
|
3038
|
-
- **READ rx** prefixes every textual line
|
|
3039
|
-
|
|
3271
|
+
- **READ rx** prefixes every textual line under {§render-rule}; eligible
|
|
3272
|
+
editable resources carry `@hash N:`, and all others carry `N:`.
|
|
3273
|
+
- **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}).
|
|
3040
3274
|
- **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.
|
|
3041
|
-
- **SEND
|
|
3275
|
+
- **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}).
|
|
3042
3276
|
- **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.
|
|
3043
3277
|
|
|
3044
3278
|
### §send-status-policy Directed-SEND status code policy
|