@plurnk/plurnk-service 1.10.0 → 1.11.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 +41 -17
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/SPEC.md +487 -233
- package/digest-sql/curation/curation.sql +1 -0
- package/dist/Paths.d.ts +3 -3
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +9 -9
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/content/edit-collision.js +3 -3
- package/dist/content/edit-collision.js.map +1 -1
- package/dist/content/edit-receipt.d.ts +6 -5
- package/dist/content/edit-receipt.d.ts.map +1 -1
- package/dist/content/edit-receipt.js +13 -2
- package/dist/content/edit-receipt.js.map +1 -1
- package/dist/content/index.d.ts +2 -2
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +1 -1
- package/dist/content/index.js.map +1 -1
- package/dist/content/line-anchors.d.ts +2 -0
- package/dist/content/line-anchors.d.ts.map +1 -1
- package/dist/content/line-anchors.js +2 -0
- package/dist/content/line-anchors.js.map +1 -1
- package/dist/content/matcher.d.ts.map +1 -1
- package/dist/content/matcher.js +3 -2
- package/dist/content/matcher.js.map +1 -1
- package/dist/content/read-projector.js +3 -3
- package/dist/content/read-projector.js.map +1 -1
- package/dist/core/BudgetReadout.d.ts +6 -1
- package/dist/core/BudgetReadout.d.ts.map +1 -1
- package/dist/core/BudgetReadout.js +38 -2
- package/dist/core/BudgetReadout.js.map +1 -1
- package/dist/core/CapabilityPolicies.d.ts +15 -0
- package/dist/core/CapabilityPolicies.d.ts.map +1 -0
- package/dist/core/CapabilityPolicies.js +60 -0
- package/dist/core/CapabilityPolicies.js.map +1 -0
- package/dist/core/CapabilityResolver.d.ts +24 -0
- package/dist/core/CapabilityResolver.d.ts.map +1 -0
- package/dist/core/CapabilityResolver.js +180 -0
- package/dist/core/CapabilityResolver.js.map +1 -0
- package/dist/core/ChannelWrite.d.ts +4 -3
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.d.ts +1 -1
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +1 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +3 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +148 -160
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/DurableStatement.d.ts.map +1 -1
- package/dist/core/DurableStatement.js +19 -15
- package/dist/core/DurableStatement.js.map +1 -1
- package/dist/core/Engine.d.ts +6 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +18 -7
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +30 -25
- package/dist/core/ExecutorRegistry.d.ts +3 -3
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +10 -6
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/GitBranch.d.ts +4 -1
- package/dist/core/GitBranch.d.ts.map +1 -1
- package/dist/core/GitBranch.js +12 -2
- package/dist/core/GitBranch.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts +1 -0
- package/dist/core/LogEntryProjection.d.ts.map +1 -1
- package/dist/core/LogEntryProjection.js +12 -4
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LoopPolicyReader.d.ts +7 -0
- package/dist/core/LoopPolicyReader.d.ts.map +1 -0
- package/dist/core/LoopPolicyReader.js +33 -0
- package/dist/core/LoopPolicyReader.js.map +1 -0
- package/dist/core/ModelCall.d.ts +1 -0
- package/dist/core/ModelCall.d.ts.map +1 -1
- package/dist/core/ModelCall.js +3 -0
- package/dist/core/ModelCall.js.map +1 -1
- package/dist/core/NoticeChannel.d.ts +2 -2
- package/dist/core/NoticeChannel.d.ts.map +1 -1
- package/dist/core/NoticeChannel.js +18 -9
- package/dist/core/NoticeChannel.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +4 -0
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +4 -3
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/Owner.js +5 -5
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +2 -2
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +53 -35
- 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 +16 -13
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +1 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -1
- package/dist/core/ResourceMutations.d.ts +5 -4
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +71 -48
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +6 -3
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +14 -17
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ServiceTeardown.d.ts +1 -1
- package/dist/core/ServiceTeardown.d.ts.map +1 -1
- package/dist/core/ServiceTeardown.js +4 -8
- package/dist/core/ServiceTeardown.js.map +1 -1
- package/dist/core/StrikeRail.js +1 -1
- package/dist/core/StrikeRail.js.map +1 -1
- package/dist/core/ToolResources.d.ts +2 -2
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +18 -8
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +22 -8
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +7 -3
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +315 -71
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerControlAddress.d.ts.map +1 -1
- package/dist/core/WorkerControlAddress.js +1 -2
- package/dist/core/WorkerControlAddress.js.map +1 -1
- package/dist/core/WorkerName.d.ts +2 -0
- package/dist/core/WorkerName.d.ts.map +1 -1
- package/dist/core/WorkerName.js +3 -2
- package/dist/core/WorkerName.js.map +1 -1
- package/dist/core/WorkerName.sql +2 -1
- package/dist/core/caps/DbProjectionCaps.d.ts +2 -1
- package/dist/core/caps/DbProjectionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbProjectionCaps.js +12 -1
- package/dist/core/caps/DbProjectionCaps.js.map +1 -1
- package/dist/core/file-materialization.d.ts +22 -0
- package/dist/core/file-materialization.d.ts.map +1 -0
- package/dist/core/file-materialization.js +77 -0
- package/dist/core/file-materialization.js.map +1 -0
- package/dist/core/fork.d.ts +2 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +13 -4
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +27 -11
- package/dist/core/git-membership.d.ts +25 -14
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +260 -164
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/git-state.d.ts +2 -0
- package/dist/core/git-state.d.ts.map +1 -1
- package/dist/core/git-state.js +27 -1
- package/dist/core/git-state.js.map +1 -1
- package/dist/core/operation-target-groups.d.ts.map +1 -1
- package/dist/core/operation-target-groups.js +8 -7
- package/dist/core/operation-target-groups.js.map +1 -1
- package/dist/core/owner.sql +7 -10
- package/dist/core/packet-wire.d.ts +10 -0
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +78 -29
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/scheme-types.d.ts +2 -2
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/scheme-types.js +1 -1
- package/dist/core/scheme-types.js.map +1 -1
- package/dist/core/types.d.ts +3 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-settings.d.ts +4 -3
- package/dist/core/worker-settings.d.ts.map +1 -1
- package/dist/core/worker-settings.js +40 -19
- package/dist/core/worker-settings.js.map +1 -1
- package/dist/core/workspace-settings.d.ts +3 -1
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +6 -2
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +93 -26
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +13 -3
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/schemes/Exec.d.ts +3 -5
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +98 -94
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +24 -6
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/ExecScheduler.d.ts +15 -0
- package/dist/schemes/ExecScheduler.d.ts.map +1 -0
- package/dist/schemes/ExecScheduler.js +95 -0
- package/dist/schemes/ExecScheduler.js.map +1 -0
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +16 -15
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts +6 -4
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +131 -38
- package/dist/schemes/Log.js.map +1 -1
- package/dist/schemes/Log.sql +23 -18
- package/dist/schemes/QuestionTool.d.ts +17 -4
- package/dist/schemes/QuestionTool.d.ts.map +1 -1
- package/dist/schemes/QuestionTool.js +2 -4
- package/dist/schemes/QuestionTool.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +20 -20
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-chunk.js +1 -1
- package/dist/schemes/_entry-chunk.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +18 -17
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +7 -9
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-graph.js +12 -12
- package/dist/schemes/_entry-graph.js.map +1 -1
- package/dist/schemes/_entry-graph.sql +5 -5
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +3 -3
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-semantic.js +1 -1
- package/dist/schemes/_entry-semantic.js.map +1 -1
- package/dist/server/BranchBatches.d.ts +3 -3
- package/dist/server/BranchBatches.d.ts.map +1 -1
- package/dist/server/BranchBatches.js +9 -2
- package/dist/server/BranchBatches.js.map +1 -1
- package/dist/server/Daemon.d.ts +12 -41
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +59 -93
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +10 -1
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +6 -5
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +14 -11
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts +2 -2
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +8 -5
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.d.ts +13 -1
- package/dist/server/FunctionalityManager.d.ts.map +1 -1
- package/dist/server/FunctionalityManager.js +82 -17
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts +56 -0
- package/dist/server/MembersFunctionality.d.ts.map +1 -0
- package/dist/server/MembersFunctionality.js +350 -0
- package/dist/server/MembersFunctionality.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +12 -1
- package/dist/server/SkillsFunctionality.d.ts.map +1 -1
- package/dist/server/SkillsFunctionality.js +6 -1
- package/dist/server/SkillsFunctionality.js.map +1 -1
- package/dist/server/client-input.d.ts +5 -8
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +49 -108
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +3 -0
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +4 -4
- package/dist/server/envelope.d.ts +1 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +11 -10
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +1 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +12 -5
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/seam-proposal-list.sql +2 -2
- package/dist/server/worker-capabilities.sql +9 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +16 -21
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +217 -64
- package/package.json +30 -32
- package/dist/core/LoopFlagsReader.d.ts +0 -7
- package/dist/core/LoopFlagsReader.d.ts.map +0 -1
- package/dist/core/LoopFlagsReader.js +0 -33
- package/dist/core/LoopFlagsReader.js.map +0 -1
- package/dist/core/resolveForLoop.d.ts +0 -5
- package/dist/core/resolveForLoop.d.ts.map +0 -1
- package/dist/core/resolveForLoop.js +0 -12
- package/dist/core/resolveForLoop.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -50,7 +50,7 @@ their absence never makes a client, plugin, or `_plurnk` turn exceptional.
|
|
|
50
50
|
| `kind` | Required purpose: `inference`, `initialization`, `overflow`, `operation`, or `maintenance`. Model iff inference; initialization, overflow, and maintenance require `_plurnk`. A maintenance turn's successful rows are packet-suppressed — a receipt answers an asker, and maintenance has none ({§actor-boundary-doc-injection}). |
|
|
51
51
|
| `status`, `completed_at` | A new turn is open at status 102 with `completed_at=NULL`. Completion records the exact terminal SEND/operation disposition and timestamp; a completed 102 is therefore distinct from an open 102. |
|
|
52
52
|
| Operations | Ordered by `(turn_id, sequence)` on one exact worker/loop/turn chain. Each row's `origin` is the turn producer or `_plurnk` making a system observation; the observation does not impersonate the producer. |
|
|
53
|
-
| `turnOps` | Every admitted source-backed turn preserves its exact PLAN…SEND program as one
|
|
53
|
+
| `turnOps` | Every admitted source-backed turn preserves its exact PLAN…SEND program as one actionless `/ops` log item under {§turn-ops-entry}. The item supplements rather than replaces the executed operation rows. |
|
|
54
54
|
| Inference evidence | Model calls, `packet`, model, finish reason, and provider metadata belong only to model/inference turns. Turn fields are nullable until recorded and remain NULL for every other kind. |
|
|
55
55
|
|
|
56
56
|
One lifecycle owner opens, optionally records inference evidence, and completes
|
|
@@ -117,6 +117,25 @@ These are the complete strike sources:
|
|
|
117
117
|
executor error is not a PLURNK contract violation. Cycle and terminal steering
|
|
118
118
|
remain independent strike sources.
|
|
119
119
|
|
|
120
|
+
§provider-recovery **A recoverable provider failure never ends a loop.** When a model
|
|
121
|
+
call fails with a network failure, rate limit, deadline, or interrupted resource after
|
|
122
|
+
the provider's own retries, the turn records the exact Problem as a `_plurnk` row,
|
|
123
|
+
notices the client (`engine:provider` / `provider_unavailable`), waits with
|
|
124
|
+
exponential backoff (`PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF`, doubling, capped at
|
|
125
|
+
twelve times itself), and re-issues the same call against the exact frozen model
|
|
126
|
+
messages whose response is still outstanding. Each reissue remains a distinct logical
|
|
127
|
+
model call with complete physical-request accounting, but the active turn's newly
|
|
128
|
+
recorded provider Problems do not recursively enter that request; they surface normally
|
|
129
|
+
only in a later genuinely new packet. No emission attempt is consumed and no strike is
|
|
130
|
+
scored. Every recovery checkpoint broadcasts live, while the model-facing Notice buffer
|
|
131
|
+
retains only the current provider state; the next completed exchange notices
|
|
132
|
+
`provider_recovered`. Recovery is bounded by `PLURNK_SERVICE_PROVIDER_RECOVERY`; when it
|
|
133
|
+
is spent the turn completes as `202` and the loop parks exactly like a
|
|
134
|
+
`## SEND0 [202]` wait ({§worker-lifecycle-wake-requeue-not-terminal}), resuming on the
|
|
135
|
+
next prompt or wake with its log intact. Only a client cancel, the loop deadline
|
|
136
|
+
({§operator-config-loop-timeout}), or a non-recoverable provider Problem (refusal,
|
|
137
|
+
authorization, quota, an invalid response) settles a loop on a provider failure.
|
|
138
|
+
|
|
120
139
|
A struck turn increments the consecutive streak once; a clean admitted turn
|
|
121
140
|
resets it to zero. Reaching `MAX_STRIKES` terminates at **508 Loop Detected**
|
|
122
141
|
when the crossing turn is cycle-detected, otherwise **500**. Rejected emission
|
|
@@ -130,11 +149,11 @@ shown. The current streak may ride first-party provider metadata
|
|
|
130
149
|
|------------------------------|---|
|
|
131
150
|
| **verdict** | The end-of-turn ruling computed inline in `Engine.runLoop` from the strike rail and independent loop terminals. No filter chain. |
|
|
132
151
|
| **strike** | One admitted turn matching at least one source above. |
|
|
133
|
-
| **emission attempt** | One completed provider exchange beneath an engine turn. ANTLR admits it when
|
|
152
|
+
| **emission attempt** | One completed provider exchange beneath an engine turn. ANTLR admits it when at least one source operation has a trustworthy effective envelope and no boundary-destroying tail. A hard error inside that envelope becomes a failed operation in the admitted turn; a rejected attempt is forensic evidence, not another turn or an engine strike. |
|
|
134
153
|
| **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}). |
|
|
135
154
|
| **cycle** | A repeated turn fingerprint across consecutive turns. Detection strikes silently under the rule above. |
|
|
136
|
-
|
|
|
137
|
-
| **
|
|
155
|
+
| **capability policy** | A purely subtractive `only`/`deny` selector layer over routed operation demands. Service, workspace, inherited delegation bound, worker, and loop layers compose without granting authority. |
|
|
156
|
+
| **loop policy** | One immutable loop snapshot containing capability attenuation and the independent `review`, `accept`, or `reject` proposal disposition. |
|
|
138
157
|
| **proposal** | A deferred side-effecting action. State machine: `proposed → resolved` (accept), `→ failed` (reject), or `→ cancelled` (cancel). Its core-owned disposition says whether the client or loop owns resolution ({§proposal-disposition}). |
|
|
139
158
|
| **resolution** | A client decision delivered through a standard resume entry. Proposal resolutions accept, reject, or cancel ({§methods-proposal-resolve}); client-interaction resolutions return a payload or cancel ({§methods-client-interaction-resolve}, {§agui-proposal-resolve}). |
|
|
140
159
|
|
|
@@ -282,18 +301,28 @@ an explicitly specified subpath, not in the frozen root barrel.
|
|
|
282
301
|
|
|
283
302
|
```mermaid
|
|
284
303
|
flowchart LR
|
|
285
|
-
|
|
304
|
+
LISTENER["Bind client listener<br/>unready: HTTP 503"] --> DB["Acquire daemon lock<br/>and admit SQLite schema"]
|
|
305
|
+
DB --> PROVIDER["Resolve and verify<br/>selected provider"]
|
|
286
306
|
PROVIDER --> DAEMON["Construct and start<br/>daemon composition"]
|
|
287
|
-
DAEMON --> CLIENT["
|
|
288
|
-
|
|
307
|
+
DAEMON --> CLIENT["Activate client transport"]
|
|
308
|
+
LISTENER -. failure .-> FAIL["Fail startup<br/>durable state untouched"]
|
|
309
|
+
DB -. failure .-> CLOSE_LISTENER["Close listener"] --> FAIL
|
|
289
310
|
PROVIDER -. failure .-> CLOSE["Close database<br/>and release lock"] --> FAIL
|
|
290
311
|
DAEMON -. failure .-> TEARDOWN["Close every started owner"] --> FAIL
|
|
291
312
|
```
|
|
292
313
|
|
|
293
|
-
§startup-admission
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
314
|
+
§startup-listener-admission The production service binds its sole client
|
|
315
|
+
listener before creating, opening, replacing, rotating, migrating, or otherwise
|
|
316
|
+
mutating anything in the durable data directory. A process that loses the
|
|
317
|
+
listener race fails with the originating address error and byte-identical
|
|
318
|
+
durable storage. The client-interface module owns the socket continuously; it
|
|
319
|
+
answers 503 until activated, so early ownership introduces neither traffic nor a
|
|
320
|
+
close/rebind race.
|
|
321
|
+
|
|
322
|
+
§startup-admission-order After listener ownership, database admission completes
|
|
323
|
+
before provider or capability initialization can perform external work. Every
|
|
324
|
+
later startup failure closes resources in reverse ownership order while
|
|
325
|
+
preserving the originating failure: daemon, observability, database, listener.
|
|
297
326
|
|
|
298
327
|
### §actor-boundary The actor boundary: isolation by worker, two doors, self-hosting
|
|
299
328
|
|
|
@@ -321,8 +350,8 @@ filter a row.
|
|
|
321
350
|
attached to.** A client connection is attached to one conversation Worker; its
|
|
322
351
|
management commands mutate that Worker's Functionality ({§module-worker-capabilities}),
|
|
323
352
|
and its operations execute in that Worker's environment — executable families,
|
|
324
|
-
runtime schemes,
|
|
325
|
-
|
|
353
|
+
runtime schemes, tools, and capability policy resolve through the attached
|
|
354
|
+
Worker — while the operation
|
|
326
355
|
journals in the client's own worker ({§connection-lifecycle}) and any entry it
|
|
327
356
|
writes binds its principal through that client worker. Every dispatch therefore
|
|
328
357
|
carries two coordinates: `workerId`, the journaling and entry principal, and
|
|
@@ -358,7 +387,7 @@ plus a broadcast duplicate. Ordinary project files, private worker entries,
|
|
|
358
387
|
and remote resources do not acquire ambient attention merely because they are
|
|
359
388
|
workspace-addressable.
|
|
360
389
|
|
|
361
|
-
§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}
|
|
390
|
+
§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} overlay, a workspace policy, bounds every worker's visible surface uniformly — {§machine-processes}); stale writes reject at their anchor or compare-and-swap boundary rather than being prevented by a lock ({§line-anchors}, {§membership-edit-write-cas}). 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.
|
|
362
391
|
|
|
363
392
|
§actor-boundary-passive-wake **Passive wake follows ownership.** A directed
|
|
364
393
|
voice wakes an idle worker. A parked continuation resumes when an obligation it
|
|
@@ -384,8 +413,8 @@ lineage activity and explicit commons mutations cross the environment door.
|
|
|
384
413
|
| Search derivation and catalog render | Kernel. | They are indexes and read-only projections, not entry operations. |
|
|
385
414
|
| Packet assembly and budget rails | Kernel. | They are the execution substrate on which actor operations depend. |
|
|
386
415
|
|
|
387
|
-
Git membership
|
|
388
|
-
|
|
416
|
+
Git membership is the repository's tracked files and nothing else ({§membership-baseline});
|
|
417
|
+
Plurnk never stages a file or runs `git add`.
|
|
389
418
|
|
|
390
419
|
§turn0-agents-stunt **The project AGENTS.md is a turn-0 stunt.** When
|
|
391
420
|
`<projectRoot>/AGENTS.md` exists, LoopDocs materializes it as the current worker's private
|
|
@@ -404,14 +433,22 @@ neither a hidden database write nor a kernel-owned mirror.
|
|
|
404
433
|
|
|
405
434
|
§actor-boundary-catalog-preview **Catalog preview.** `PLURNK_SERVICE_FILES_ITEMS`
|
|
406
435
|
foists turn-0 discovery into the worker's first turn, so a worker opens with a
|
|
407
|
-
navigable map instead of blank. An enabled preview executes exactly
|
|
408
|
-
bodyless FIND surveys in order:
|
|
409
|
-
(worker://~/_plurnk/skills/*.md) <1,-1>`),
|
|
410
|
-
(`## FIND0 [+init,+
|
|
411
|
-
|
|
412
|
-
enabled
|
|
413
|
-
<1,-1>`, {§a2a-agents-catalog}),
|
|
414
|
-
(
|
|
436
|
+
navigable map instead of blank. An enabled preview executes exactly eight baseline
|
|
437
|
+
bodyless FIND surveys in order: Agent Skills (`## FIND0
|
|
438
|
+
[+init,+skills] (worker://~/_plurnk/skills/*.md) <1,-1>`), plurnk references — the
|
|
439
|
+
executors, schemes, and family managers (`## FIND0 [+init,+plurnk]
|
|
440
|
+
(worker://~/_plurnk/plurnk/*.md) <1,-1>`), enabled tools (`## FIND0 [+init,+tools]
|
|
441
|
+
(worker://~/_plurnk/tools/*.md) <1,-1>`), enabled agents (`## FIND0 [+init,+agents]
|
|
442
|
+
(worker://~/_plurnk/agents/*.md) <1,-1>`, {§a2a-agents-catalog}), enabled members
|
|
443
|
+
(`## FIND0 [+init,+members] (worker://~/_plurnk/members/*.md) <1,-1>`,
|
|
444
|
+
{§members-projection}), workspace files (`## FIND0 [+init] (*) <!-- workspace files -->`),
|
|
445
|
+
workspace entries (`## FIND0 [+init] (worker:///*) <!-- workspace entries -->`), and
|
|
446
|
+
private worker entries (`## FIND0 [+init] (worker://~/*) <!-- private worker entries -->`).
|
|
447
|
+
Only those three namespace surveys carry annotations because the bare targets do not
|
|
448
|
+
name their surface; generated paths and classification tags already name every other
|
|
449
|
+
survey. Naming `~` private prevents a worker from offering its own `~` address to
|
|
450
|
+
another worker.
|
|
451
|
+
The word `skills` names Agent Skills and nothing else.
|
|
415
452
|
The catalogs select every direct document independently of its authored body;
|
|
416
453
|
ordinary READ supplies its examples and complete instructions on demand. Their
|
|
417
454
|
log classifications make the opening discovery one `init` set while retaining
|
|
@@ -430,7 +467,7 @@ direct-entry-plus-directory count; `-1` enables the ordinary markerless page;
|
|
|
430
467
|
unset / `0` disables previews. `log://` is absent because the current worker's
|
|
431
468
|
log already renders in present mode.
|
|
432
469
|
|
|
433
|
-
§worker-initialization-entry **Model-worker initialization is a real `_plurnk` turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. It preserves one OPEN exact `turnOps` item and dispatches the same source into ordinary PLAN, one archiving COPY, orienting READ/FIND, and terminal `SEND0 [102]` rows (authored in their {§op-mode-phases} execution order). Every orienting row is structurally classified `_plurnk` and `init`; the archiving `COPY (prompt:///<loop>/1)` onto `worker://~/prompts.md <-1>` is classified `_plurnk` and `backup` — the worked COPY specimen, showing the private space as scratch, emitted whenever the loop publishes a prompt ({§prompt-entry}). The PLAN is the canonical {§plan-value} with
|
|
470
|
+
§worker-initialization-entry **Model-worker initialization is a real `_plurnk` turn.** A model worker's first loop begins with one packetless `{ producer="_plurnk", kind="initialization" }` turn submitted through {§turn-ops-admission-path}. It preserves one OPEN exact `turnOps` item and dispatches the same source into ordinary PLAN, one archiving COPY, orienting READ/FIND, and terminal `SEND0 [102]` rows (authored in their {§op-mode-phases} execution order). Every orienting row is structurally classified `_plurnk` and `init`; the archiving `COPY (prompt:///<loop>/1)` onto `worker://~/prompts.md <-1>` is classified `_plurnk` and `backup` — the worked COPY specimen, showing the private space as scratch, emitted whenever the loop publishes a prompt ({§prompt-entry}). The PLAN is the canonical {§plan-value} with two entries in order: `Persist Determinations and Decisions` is `memory`, then `Discover the tooling available and survey the workspace file root.` is `in_progress`; SEND hands off with `Next: Address the prompt.` The first model request occupies the following turn and therefore begins at database/log turn sequence 2; “turn zero” is the initialization phase's model-facing label, not a zero-based database coordinate. Client and `_plurnk` administrative workers execute operation turns and do not receive model initialization.
|
|
434
471
|
|
|
435
472
|
### §machine-processes The machine and its processes: workspace, worker, fork
|
|
436
473
|
|
|
@@ -466,7 +503,7 @@ terminal history.**
|
|
|
466
503
|
| Project files ({§machine-processes-one-filesystem}) | Workspace | Shared live; a fork does not create another checkout. |
|
|
467
504
|
| Shared worker entries (`worker:///...`) | Workspace commons | Shared live. |
|
|
468
505
|
| Membership overlay ({§machine-processes-one-overlay}) | Workspace | Shared unchanged; divergent membership requires another workspace. |
|
|
469
|
-
| Log items ({§machine-processes-fork-copies-the-log}) | Worker |
|
|
506
|
+
| Log items ({§machine-processes-fork-copies-the-log}) | Worker | Durable events, curation effects, tags, current active/folded projection, and the matching observation cursor are copied as terminal history. Parent-audience occurrences still pending at the fork boundary belong to the snapshot; later sibling activity does not. |
|
|
470
507
|
| §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. |
|
|
471
508
|
| §machine-processes-entry-inheritance **Worker-owned entries** | Worker | The scheme's mandatory `{§manifest-entry-inheritance}` decides: `snapshot` copies only entries whose channels are all quiescent and remaps ownership; `rederive` copies no bytes and lets the child materializer rebuild them from inherited Functionality; `none` carries nothing. Within a `snapshot` scheme the Worker scheme's generated subtree is always rederived ({§worker-generated-subtree}). Parent and child then diverge. |
|
|
472
509
|
| Active loops, turns, and cancellation | Worker | Never copied as live work; inherited structure is terminal history, then a new loop starts. |
|
|
@@ -529,7 +566,7 @@ continues to decompose other authorities without treating them as mintable.
|
|
|
529
566
|
| Any other spelling | Refused as `name-invalid` before lookup, insertion, or child startup. |
|
|
530
567
|
| Automatic name | Generated, then admitted through the same predicate. |
|
|
531
568
|
|
|
532
|
-
§worker-read-scope **Named spaces are
|
|
569
|
+
§worker-read-scope **Named spaces are workspace-wide reads**: any worker of the workspace reads `worker://<name>/…` for any other — a parent its child's `result`, a child its parent's, a sibling its sister's. The parent designs the topology by what it names to whom; the engine imposes none (operator ruling 2026-08-26, #394). An unknown name resolves 404. Reserved runtime workers obey the same rule; there is no unnamed world-readable space, and writability is untouched ({§worker-write-scoping}).
|
|
533
570
|
|
|
534
571
|
§worker-write-scoping **Writes are own-space-and-commons only**: a model writes `worker://~/` and `worker:///` — every ancestry-readable named authority is read-only to it (403), while an unreadable name remains 404 under {§worker-read-scope}. `owner_id` is engine-stamped from the dispatch context, never model-set. Nothing worker-authored can land under another principal. The entry-copy seam (COPY/MOVE) is pathname-keyed and addresses the commons; a space's content moves via READ + EDIT. The one exception inside a writable space is the generated subtree below.
|
|
535
572
|
|
|
@@ -579,7 +616,7 @@ literal `workers.name` value.
|
|
|
579
616
|
remapped (source → branch) — so the branch opens with the parent's notes and
|
|
580
617
|
diverges on its own edits: *fork = everything-in-common-but-name*.
|
|
581
618
|
- **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.
|
|
582
|
-
- §worker-delegation-inherits-
|
|
619
|
+
- §worker-delegation-inherits-policy **Delegation cannot widen authority.** WORK and FORK copy the delegating actor's complete effective capability policy into the child's immutable `capability_bound`; later widening of any parent layer cannot enlarge that child. Every fresh delegated loop carries that complete effective attenuation and the delegating loop's proposal disposition, including a loop created by SEND to an idle Worker. SEND into an active or parked loop leaves that loop's immutable policy untouched. The bound is delegation authority captured by value, not a client binding or a live parent-policy link.
|
|
583
620
|
- §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.
|
|
584
621
|
|
|
585
622
|
Untagged worker control rides the daemon's inject seam (active→fold, idle→enqueue+drain), so the handler creates/branches the worker and hands off; the daemon owns provider + system prompt. Tagged WORK/FORK instead enqueue a fresh loop without starting its drain; {§worker-branch-batch} becomes its sole starter. FORK/WORK carry the seed task in the body and are their own ops, dispatched to worker control — never the entry-copy path.
|
|
@@ -589,6 +626,15 @@ Untagged worker control rides the daemon's inject seam (active→fold, idle→en
|
|
|
589
626
|
Branch-tagged WORK/FORK serializes ordinary Git branches over the one project
|
|
590
627
|
checkout. It creates no worktrees, alternate roots, hidden merges, or stashes.
|
|
591
628
|
|
|
629
|
+
§branch-delegation-disabled **Off the surface until specified (#396).** The form is
|
|
630
|
+
not offered to the model: the teaching carries no `[branch]` slot, and unless the
|
|
631
|
+
operator sets `PLURNK_SERVICE_BRANCH_DELEGATION=1` a WORK/FORK signal is refused up
|
|
632
|
+
front as `501 branch-delegation-disabled`, naming the signal-less form. Twelve
|
|
633
|
+
branch delegations across the benchmarks produced no organic success; the
|
|
634
|
+
preconditions below are invisible to the model until the refusal, and any label on
|
|
635
|
+
WORK/FORK reads as a branch order. The batches below remain the implementation behind
|
|
636
|
+
that knob and keep their witnesses.
|
|
637
|
+
|
|
592
638
|
§worker-branch-batch-exclusive **Stop the workspace; serialize
|
|
593
639
|
ordinary branches.** A branch signal on WORK/FORK creates a durable batch keyed
|
|
594
640
|
to the parent turn. The batch queues one exclusive workspace gate before that
|
|
@@ -647,8 +693,9 @@ The remaining worker surfaces are:
|
|
|
647
693
|
**2xx deliverable is born OPEN** (its body
|
|
648
694
|
materialized into the parent's packet, not hidden behind a fold): a child's
|
|
649
695
|
success must reach the parent open and awakening, never a bodyless row. An
|
|
650
|
-
non-2xx result surfaces folded; a failure retains its exact status and Problem. Every death-path is stamped uniformly
|
|
651
|
-
|
|
696
|
+
non-2xx result surfaces folded; a failure retains its exact status and Problem. Every death-path is stamped uniformly —
|
|
697
|
+
including a spawn that dies before its first turn, such as a branch batch refused at
|
|
698
|
+
git-preflight — so no child termination is silent to its owner; collection is lineage
|
|
652
699
|
supervision, never a
|
|
653
700
|
verb. The **pull** side mirrors the push: a path-absent
|
|
654
701
|
`## READ0 (worker://<name>)` collects that same result on demand for a
|
|
@@ -848,22 +895,29 @@ Three current entry points:
|
|
|
848
895
|
|
|
849
896
|
### §emission-admission Provider emission admission
|
|
850
897
|
|
|
851
|
-
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
|
|
898
|
+
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 requires at least one parsed source operation, no `unparsedTail`, and a trustworthy effective envelope. Canonical source begins with PLAN and ends with a terminal SEND. If no valid leading PLAN or terminal SEND was parsed, the parser supplies an empty PLAN or bodyless `SEND [102]`, records its exact hard diagnostic, and Core admits the useful operations instead of resampling. Both diagnostics participate in one ordinary struck turn, never one strike apiece. An authored PLAN or terminal SEND remains a real boundary, so an error outside either authored edge, post-terminal content, a boundary-destroying tail, or no source operation 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}). The accepted packet retains the provider's source bytes exactly in response evidence and `turnOps`; synthetic envelope statements exist only in the normalized operation program and its durable rows.
|
|
852
899
|
|
|
853
|
-
§safe-uri-target-groups After source and authored-command admission, Core tolerates one target group on READ, FOLD, or
|
|
900
|
+
§safe-uri-target-groups After source and authored-command admission, Core tolerates one target group on READ, FOLD, OPEN, or KILL only when splitting its raw target at top-level comma or whitespace separators produces at least two members and every member independently parses as an explicit `scheme://` URI. Request-metadata blocks are opaque to this split. Each member becomes one ordinary statement with an independent dispatch outcome and log row, in authored member order; scheduling may still move the complete operation class under {§op-mode-phases}. Otherwise the target remains exactly singular, including local filenames containing spaces or commas. The stored `turnOps` and authored command count remain unexpanded, and no other operation admits target groups.
|
|
854
901
|
|
|
855
902
|
Core retries a rejected emission against the exact same packet beneath the same engine turn, up to `PLURNK_SERVICE_EMISSION_ATTEMPTS`. Rejected bytes never dispatch or reach the engine strike rail. Before each `generate`, Core opens one durable logical `model_calls` row and its emission-specific `turn_attempts` admission row. A call that ends without response evidence leaves that admission row unclassified (`accepted IS NULL`) and does not consume the emission-attempt ceiling. Beneath the model call, every provider observer invocation opens one cardinal `provider_requests` occurrence immediately before physical I/O and settles it as response or error. Adapter retries and capacity failover append requests in issue order; a response-less failure therefore remains an accounted occurrence rather than disappearing. Normalized response evidence is durable before parser classification and does not duplicate the separately owned accounting. The accepted exchange alone extends `turns.packet` with response evidence; 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.
|
|
856
903
|
|
|
857
|
-
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 emission-attempt item under {§rejected-emission-entry} and carries one transient `invalid_emission` Notice: `
|
|
904
|
+
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 emission-attempt item under {§rejected-emission-entry} and carries one transient `invalid_emission` Notice: `Response rejected before dispatch; no operations were performed.` followed by `Parser: <the latest attempt's first diagnostic>` with its `content-offset` position — the model sees why, at which line, against its own projected text. The Notice states only observed admission facts; it does not classify the response as unrecoverable, infer why generation ended, or prescribe intent beyond the parser-owned diagnostic. Attempt count and rail state never become 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.
|
|
858
905
|
|
|
859
|
-
An admitted
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
906
|
+
An admitted program may contain bounded malformed statements or recovered
|
|
907
|
+
envelope defaults. Parsed operations still dispatch; each hard parser diagnostic
|
|
908
|
+
becomes one durable model-origin `error` row with the parser's exact detail under
|
|
909
|
+
{§parse-diagnostics} and status 400. These failures are committed before the
|
|
910
|
+
terminal disposition, participate in the ordinary strike rail, and prevent SEND
|
|
911
|
+
signal `200` or an already-drained signal `202` from concluding before the model
|
|
912
|
+
sees them in the next packet. This is operation recovery, not provider
|
|
913
|
+
resampling. A malformed statement's Problem records the factual
|
|
914
|
+
`siblingsRetained: true` extension; an envelope-default Problem states only the
|
|
915
|
+
observed boundary failure and exact default applied.
|
|
863
916
|
|
|
864
917
|
§invalid-emission-attempts Exhausting the emission-attempt budget opens the
|
|
865
918
|
single informed recovery turn above. Consecutive exhaustion of that turn
|
|
866
|
-
terminates the loop at 500 without spending an engine strike
|
|
919
|
+
terminates the loop at 500 without spending an engine strike, with detail `No
|
|
920
|
+
Plurnk turn was admitted after <N> emission attempts.`
|
|
867
921
|
|
|
868
922
|
§turn-never-blank An admitted turn whose operation fails — during parsing or
|
|
869
923
|
dispatch — is categorically different: its failed operation row enters
|
|
@@ -998,7 +1052,7 @@ meaning of an authored URI authority before any entry capability is exposed:
|
|
|
998
1052
|
host, non-default port, path, and serialized query are identity; query order,
|
|
999
1053
|
duplicates, and an explicit empty `?` survive. A fragment is a Plurnk channel
|
|
1000
1054
|
selector, not network identity or transport. URL userinfo is rejected and
|
|
1001
|
-
|
|
1055
|
+
scheme metadata never enters identity. Plain `http` routes through `https`,
|
|
1002
1056
|
just as `ws` routes through `wss`; those implementation aliases never alias
|
|
1003
1057
|
resources, and the secure face is the one taught — `http` stays supported
|
|
1004
1058
|
for the endpoint that requires it, never advertised as a peer. `SchemeCtx.entries` binds every cap to the addressed protocol.
|
|
@@ -1045,43 +1099,43 @@ render-time filtering.
|
|
|
1045
1099
|
|
|
1046
1100
|
§fs-canonical-name **One canonical name, storage ≡ wire: the git pathspec.** Member keys follow gitformat-index(5) verbatim (reference edition: git 2.47.3): relative to the workspace `project_root`, without leading slash, `/`-separated, no trailing slash or NUL. Directories are never entries and the root needs no name. When `project_root` is below the containing repository's top level, Git members above it naturally use the same `../`-prefixed CWD-relative names that `git ls-files` emits without `--full-name`; these are not outside-repository mounts. The database stores that root-relative key directly because workspace identity is rooted at the access point. Every model spelling canonicalizes before storage or comparison.
|
|
1047
1101
|
|
|
1048
|
-
§fs-visibility-grantors **File visibility has two represented grantors; Plurnk never invents a private third one.** A file member is admitted by the active Git substrate or by an ordinary `
|
|
1102
|
+
§fs-visibility-grantors **File visibility has two represented grantors; Plurnk never invents a private third one.** A file member is admitted by the active Git substrate or by an ordinary `include` row of the overlay. An `include` is either a projected `members` definition ({§members-projection}) or the exact, inspectable record of an accepted creation ({§fs-create-record}); both resolve to `constraint` membership. AGENTS.md remains auto-pulled as POLICY ({§policy-sections}), deliberately not a file member. A physically existing path that neither Git nor an `include` admits does not exist for the model and cannot be overwritten.
|
|
1049
1103
|
|
|
1050
|
-
§fs-write-surface **The write surface — one admission and incorporation path.** Existing writes remain membership-gated. An absent path additionally crosses the effective creation scope and the complete constraint/Git policy before a proposal is issued. EDIT, COPY destinations, and MOVE destinations use this same path regardless of whether the producer is a model, client, plugin, or `_plurnk`. A COPY or MOVE destination region on an absent entry
|
|
1104
|
+
§fs-write-surface **The write surface — one admission and incorporation path.** Existing writes remain membership-gated. An absent path additionally crosses the effective creation scope and the complete constraint/Git policy before a proposal is issued. EDIT, COPY destinations, and MOVE destinations use this same path regardless of whether the producer is a model, client, plugin, or `_plurnk`. A COPY or MOVE destination region on an absent entry creates the entry when it is `<-1>`, the whole-source form `<1,-1>`, or a whole-line range from line 1 whose final line exactly equals the selected source's resulting line count. These scopes all describe the complete new value; any other region on an absent entry is `destination-region-not-found`.
|
|
1051
1105
|
|
|
1052
1106
|
| Case | Required admission | Accepted result |
|
|
1053
1107
|
|------|--------------------|-----------------|
|
|
1054
1108
|
| §fs-create-disabled Absent path, effective scope `none` | None | Refuse without touching disk. |
|
|
1055
|
-
| §fs-create-root Absent path inside `project_root` | Effective scope `root` or `namespace`; no matching
|
|
1056
|
-
| §fs-create-namespace Absent canonical `../` path | Effective scope `namespace`; no matching
|
|
1057
|
-
| §fs-create-ignored Absent path ignored by active Git | Matching
|
|
1058
|
-
| §fs-create-git Absent in-root path admitted by active Git | Not ignored | Exclusive CREATE followed by
|
|
1059
|
-
| §fs-create-
|
|
1060
|
-
| §fs-write-member Existing in-root member | Git or
|
|
1061
|
-
| §fs-write-outside Existing canonical `../` member |
|
|
1109
|
+
| §fs-create-root Absent path inside `project_root` | Effective scope `root` or `namespace`; no matching exclusion | Exclusive CREATE (`open(O_CREAT\|O_EXCL)` semantics), then incorporation below. |
|
|
1110
|
+
| §fs-create-namespace Absent canonical `../` path | Effective scope `namespace`; no matching exclusion | Exclusive CREATE, then an exact creation record so the outside member is read-write. |
|
|
1111
|
+
| §fs-create-ignored Absent path ignored by active Git | Matching `members` definition | Exclusive CREATE through that definition; a creation record or model definition never overrides Git ignore. |
|
|
1112
|
+
| §fs-create-git Absent in-root path admitted by active Git | Not ignored | Exclusive CREATE followed by an exact creation record (`source: "create"`); never `git add` ({§membership-baseline}). |
|
|
1113
|
+
| §fs-create-definition Absent admitted path without Git incorporation | A projected `members` definition or automatic incorporation permitted | Exclusive CREATE followed by an exact creation record when no `members` definition already covers it. |
|
|
1114
|
+
| §fs-write-member Existing in-root member | Git or include membership | Proposal-gated EDIT. |
|
|
1115
|
+
| §fs-write-outside Existing canonical `../` member | Include membership | Proposal-gated EDIT. Git-only outside members are read-only. |
|
|
1062
1116
|
| §fs-write-nonmember Existing non-member | None | Refuse; reveal occupancy only, never content. |
|
|
1063
1117
|
|
|
1064
|
-
§fs-create-incorporation **Creation incorporation is durable workspace state, not a transient entry exception.** `workspace_constraints.source` distinguishes
|
|
1118
|
+
§fs-create-incorporation **Creation incorporation is durable workspace state, not a transient entry exception.** `workspace_constraints.source` distinguishes the engine's `create` record of a file Plurnk wrote from the `members` family's projected rows (`members`: human-authored, `model`: model-proposed — {§members-projection}). A projected row interprets `glob` as a pattern; a creation record is one exact canonical path, never reinterpreted as a pattern, and the family's projection never overwrites or retires it.
|
|
1065
1119
|
|
|
1066
|
-
| Event |
|
|
1120
|
+
| Event | Creation-record lifecycle |
|
|
1067
1121
|
|-------|--------------------------|
|
|
1068
|
-
| §fs-create-
|
|
1122
|
+
| §fs-create-record Successful creation not covered by a projected `members` definition | Insert exact `{ effect: "include", glob: canonicalPath, source: "create" }`. |
|
|
1069
1123
|
| §fs-create-copy COPY to a new path | Incorporate the destination independently; the source is unchanged. |
|
|
1070
|
-
| §fs-create-move MOVE to a new path | Incorporate the destination, then remove the source's
|
|
1071
|
-
| §fs-create-kill KILL or accepted whole-resource deletion | Remove the deleted path's
|
|
1072
|
-
| §fs-create-
|
|
1073
|
-
| §fs-create-masked A later
|
|
1074
|
-
| §fs-create-ambient-delete Reconciliation confirms a
|
|
1124
|
+
| §fs-create-move MOVE to a new path | Incorporate the destination, then remove the source's creation record after deleting the source. |
|
|
1125
|
+
| §fs-create-kill KILL or accepted whole-resource deletion | Remove the deleted path's creation record. |
|
|
1126
|
+
| §fs-create-definition-overlap A `members` definition projected at the same exact path | The creation record stays as it is; the definition keeps admitting the path after the record is retired. |
|
|
1127
|
+
| §fs-create-masked A later exclusion or active Git-ignore rule excludes a created member | Preserve the creation record as dormant provenance; removing the exclusion restores membership when the file still exists. |
|
|
1128
|
+
| §fs-create-ambient-delete Reconciliation confirms a created path disappeared outside Plurnk | Remove the creation record; projected definitions are untouched. |
|
|
1075
1129
|
|
|
1076
1130
|
The file-creation invariants are deliberately redundant with the matrices only
|
|
1077
1131
|
where the invariant closes an architectural failure mode:
|
|
1078
1132
|
|
|
1079
|
-
- §file-create-no-orphans A successful create always ends in
|
|
1133
|
+
- §file-create-no-orphans A successful create always ends in a projected definition or a creation record; no accepted file is orphaned from the workspace that created it.
|
|
1080
1134
|
- §file-create-no-clobber Creation is exclusive and an existing non-member remains unreadable and non-overwritable.
|
|
1081
|
-
- §file-create-exclusions-win
|
|
1135
|
+
- §file-create-exclusions-win An exclusion outranks all automatic creation; active Git ignore is overridden only by a `members` definition.
|
|
1082
1136
|
- §file-create-scope The effective creation scope is the minimum of the service ceiling and workspace setting; no call site or producer may widen it.
|
|
1083
1137
|
- §file-create-producer-neutral The file contract depends on the operation and target, never the producer identity.
|
|
1084
|
-
- §file-create-single-owner File membership owns prospective admission, incorporation choice, and
|
|
1138
|
+
- §file-create-single-owner File membership owns prospective admission, incorporation choice, and creation-record lifecycle; file operations consume that decision rather than re-deriving Git and constraint policy.
|
|
1085
1139
|
- §file-create-transaction Success requires both exclusive disk creation and durable incorporation. Approval re-resolves physical containment and policy, so a proposal-time parent cannot be swapped for an outside-pointing symlink. Incorporation failure removes the created entry and file; incomplete rollback is an explicit partial-failure Problem.
|
|
1086
1140
|
|
|
1087
1141
|
Refusing an occupied non-member follows the POSIX exclusive-create precedent:
|
|
@@ -1139,7 +1193,7 @@ Registration precedes loop affinity:
|
|
|
1139
1193
|
|
|
1140
1194
|
- §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.
|
|
1141
1195
|
|
|
1142
|
-
- §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.
|
|
1196
|
+
- §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. When validation identifies one malformed statement, that row receives its exact 4xx Problem while every otherwise-valid sibling receives 424 Failed Dependency without borrowing the malformed statement's coordinates. 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.
|
|
1143
1197
|
|
|
1144
1198
|
### §orchestration Cross-scheme orchestration
|
|
1145
1199
|
|
|
@@ -1174,7 +1228,7 @@ Directed SEND (non-null path) routes to scheme's `send`. Status = intent:
|
|
|
1174
1228
|
- `## SEND0 [200] (path)` — write body into resource (WS message, exec stdin).
|
|
1175
1229
|
- `## SEND0 [499] (path)` — cancel active subscription ({§stream}).
|
|
1176
1230
|
|
|
1177
|
-
- §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
|
|
1231
|
+
- §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.
|
|
1178
1232
|
- §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.
|
|
1179
1233
|
- §channel-selection-visibility **Channel selection is decision-time information, not a guess** — every multi-channel resource presents its channels with extents wherever FIND presents the resource: broad results list each channel's path, mimetype, tokens, and lines (default channel first), and matcher locations name the channel their line coordinates address. The packet never presents channels as equal and indistinguishable; extents derive from the stored channels by construction. Budget enforcement stays with {§overflow-turn} — this is information, not a second guard.
|
|
1180
1234
|
|
|
@@ -1217,7 +1271,7 @@ Engine → scheme guarantees:
|
|
|
1217
1271
|
- `ctx` is fresh per call. No mutation across calls.
|
|
1218
1272
|
- §universal-read-composition **Exact READ has one composition.** Core resolves
|
|
1219
1273
|
canonical identity and owner once, gives a data scheme its optional
|
|
1220
|
-
`prepareRepresentation({ target, authority, pathname })` opportunity, reads the complete
|
|
1274
|
+
`prepareRepresentation({ target, metadata, authority, pathname })` opportunity, reads the complete
|
|
1221
1275
|
canonical channels, selects the authored channel, applies binary and
|
|
1222
1276
|
text-coordinate rules, and finally composes that channel's durable producer
|
|
1223
1277
|
result. Preparation receives neither fragment nor `lineMarker`; finite work
|
|
@@ -1374,7 +1428,7 @@ flowchart LR
|
|
|
1374
1428
|
|
|
1375
1429
|
§derivation-exhaustive Identical projections attach the same immutable artifact regardless of their source table. Search primitives therefore consume only `{key, deepHash}` candidates and cannot depend on entry or log storage. Semantic and graph FIND require every selected channel candidate—and every channel in graph's relationship universe—to be attached. An incomplete set returns 503 with `problem.search = {state:"incomplete", indexed, total}`; it never silently searches a partial corpus. Explicit membership changes may warm eagerly; every model turn joins exhaustive derivation before dispatch. Passive workspace creation and attachment do not launch it. The incomplete response is therefore an interface invariant and diagnostic, not a lazy-search mode.
|
|
1376
1430
|
|
|
1377
|
-
The graph projection stores only addressable symbol names. A structured-data handler may legitimately emit an empty key into its symbols channel, but the
|
|
1431
|
+
The graph projection stores only addressable symbol names. A structured-data handler may legitimately emit an empty key into its symbols channel, but the `&graph` matcher cannot name an empty symbol; that one definition is omitted from graph storage without suppressing FTS, vectors, or the remaining definitions. Invalid references and other persistence violations still fail the resource derivation explicitly.
|
|
1378
1432
|
|
|
1379
1433
|
The pass tiles the exact readable text into token-budgeted fragment strings and
|
|
1380
1434
|
sends every tile for one resource through one ordered `mimetypes.embedBatch`
|
|
@@ -1486,7 +1540,11 @@ For READ/LOOK and COPY/MOVE source or destination selection, core resolves every
|
|
|
1486
1540
|
anchor against the addressed current complete content before applying the
|
|
1487
1541
|
ordinary numeric text-coordinate contract. Exactly one current match lowers to
|
|
1488
1542
|
its numeric line; zero or multiple matches return 409 `line-anchor-collision`,
|
|
1489
|
-
|
|
1543
|
+
with `retryable: false` because resolving the collision requires a new READ and
|
|
1544
|
+
different coordinates rather than automatic replay. An anchor in a column
|
|
1545
|
+
position returns 400 stating that four-coordinate
|
|
1546
|
+
column slots are numeric and that `<@start,@end>` is the whole-line anchor
|
|
1547
|
+
range. COPY/MOVE mutation owners retain
|
|
1490
1548
|
the resolved endpoint neighborhoods as compare-and-swap preconditions. There is
|
|
1491
1549
|
no revision sidecar or fuzzy relocation. A range authenticates both endpoint
|
|
1492
1550
|
neighborhoods, so every line of a range up to `2C + 2` lines is covered; a
|
|
@@ -1501,7 +1559,7 @@ AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
|
|
|
1501
1559
|
- §edit-null-clears Writes the body; `body: null` clears it.
|
|
1502
1560
|
- §edit-status-201-200 Returns `{ status: 201, entryId }` for a new entry and
|
|
1503
1561
|
`{ status: 200, entryId }` for a content update.
|
|
1504
|
-
- §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}).
|
|
1562
|
+
- §edit-noop-304 A write that changes nothing — identical content — returns `{ status: 304, entryId }`, mirroring OPEN/FOLD's idempotence ({§open-fold}). Its terse detail states the observed equality and the valid empty-body deletion shape; it never presumes that repetition or retrieval is the intended recovery. The operation's log classification remains independent ({§log-item-tags}).
|
|
1505
1563
|
- §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.
|
|
1506
1564
|
- §edit-line-anchors An anchored EDIT resolves under {§line-anchors} and carries
|
|
1507
1565
|
its endpoint checks as a core-private mutation precondition. Otherwise-valid
|
|
@@ -1518,9 +1576,11 @@ AST: `{ op: "EDIT", target, body: string | null, signal: tags | null, lineMarker
|
|
|
1518
1576
|
that changes before mutation, or a representation that changes in the final
|
|
1519
1577
|
check/write gap returns the same neutral **409 `edit-collision`** and preserves
|
|
1520
1578
|
the winner's content. Its public detail says only that EDIT collided with
|
|
1521
|
-
another change and directs the model to READ
|
|
1522
|
-
|
|
1523
|
-
|
|
1579
|
+
another change and directs the model to READ before selecting current
|
|
1580
|
+
coordinates; `retryable: false` forbids automatic replay of the identical
|
|
1581
|
+
stale request. It does not assign fault or reveal which detection layer won.
|
|
1582
|
+
Concurrent correct workers are an ordinary cause. Core resolves anchors,
|
|
1583
|
+
scheme handlers receive only numeric
|
|
1524
1584
|
coordinates, the shared entry mutation owner rechecks selected endpoint
|
|
1525
1585
|
neighborhoods against its exact snapshot, and atomic identity/channel claims
|
|
1526
1586
|
and storage predicates close the remaining races.
|
|
@@ -1557,18 +1617,30 @@ operation requires a target, matcher, or unsigned tag. Add and remove terms for
|
|
|
1557
1617
|
the same tag conflict. Successful visibility and classification changes land as
|
|
1558
1618
|
one curation event whose exact per-row deltas are durable. Engine policy may
|
|
1559
1619
|
apply its separately specified diagnostic classifications, such as `overflow`.
|
|
1560
|
-
Every classification lives once in `log_tags`,
|
|
1561
|
-
copied with log history on fork.
|
|
1620
|
+
Every classification lives once in `log_tags`, remains forensic evidence when
|
|
1621
|
+
KILL retires its row's projection, and is copied with log history on fork.
|
|
1622
|
+
|
|
1623
|
+
### §log-history-projection Durable history and active projection
|
|
1624
|
+
|
|
1625
|
+
| Layer | Owner | Curation contract |
|
|
1626
|
+
|---|---|---|
|
|
1627
|
+
| Durable event | `log_entries` | One chronological execution fact. Ordinary Plurnk operations never erase it; its original body and initial folded state remain available to the client journal, digest, and fork forensics. Containing turn, worker, or workspace teardown may cascade the history. |
|
|
1628
|
+
| Active projection | `log_entry_projections` | One current worker-facing state per event. OPEN/FOLD change folded body intervals while active. Log-KILL atomically changes active to inactive and cannot be reversed; inactive rows are absent from packet rendering, log READ/FIND, failure pointers, semantic discovery, token accounting, and later curation. |
|
|
1629
|
+
|
|
1630
|
+
The successful curation operation and every exact target transition are durable
|
|
1631
|
+
in the same commit. KILL against another scheme retains that scheme's ordinary
|
|
1632
|
+
resource or process semantics; this projection contract is specific to
|
|
1633
|
+
`log:///`.
|
|
1562
1634
|
|
|
1563
1635
|
### §open-fold OPEN / FOLD
|
|
1564
1636
|
|
|
1565
1637
|
AST: `{ op: "OPEN"|"FOLD", target, body: MatcherBody | null, signal: tags | null, lineMarker: TextLineMarker | null }`.
|
|
1566
1638
|
|
|
1567
|
-
OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surface ({§packet}). Without a scope, FOLD hides and OPEN reveals the whole canonical log body. With a one-line or inclusive two-line scope, each operation changes only that body's intersecting body-relative physical lines. An anchor may be one already published on that immutable body or one returned by READing its `log:///` identity; numeric lines outside a selected body and absent or ambiguous anchors are successful per-body no-ops. Both select rows by target, matcher, and the symmetric ALL-tags filter before independently applying the scope and tag changes to each selected row. Folded intervals are durable, sorted, disjoint, and compositional; `[]` is wholly open and `[[1,-1]]` wholly folded.
|
|
1639
|
+
OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surface ({§packet}). Without a scope, FOLD hides and OPEN reveals the whole canonical log body. With a one-line or inclusive two-line scope, each operation changes only that body's intersecting body-relative physical lines. An anchor may be one already published on that immutable body or one returned by READing its `log:///` identity; numeric lines outside a selected body and absent or ambiguous anchors are successful per-body no-ops. Both select rows by target, matcher, and the symmetric ALL-tags filter before independently applying the scope and tag changes to each selected row. Folded intervals are durable, sorted, disjoint, and compositional; `[]` is wholly open and `[[1,-1]]` wholly folded. An active row's canonical body remains complete through log READ/FIND regardless of folded visibility. Rows and bodies persist, and classification changes still land when visibility is a no-op. Malformed targets, unsupported coordinate arity, and nonexistent exact coordinates fail at their owning boundary. Entries carry no visibility ({§no-visibility}), so OPEN/FOLD against an entry scheme returns 501.
|
|
1568
1640
|
|
|
1569
1641
|
### §jsonplurnk The Log's wire format
|
|
1570
1642
|
|
|
1571
|
-
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 visible content line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, 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 three body states are self-describing through field presence alone: a `body` field means open, `tokensBody` without `body` means folded (the value prices the OPEN), and neither means no canonical body; no `display` label exists. Two defaults are likewise field absence: `origin` is omitted for the worker's own model authorship (exactly as `source` absence means the owning worker), and `status` is omitted for a routine 200 on
|
|
1643
|
+
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 visible content line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, 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 three body states are self-describing through field presence alone: a `body` field means open, `tokensBody` without `body` means folded (the value prices the OPEN), and neither means no canonical body; no `display` label exists. Two defaults are likewise field absence: `origin` is omitted for the worker's own model authorship (exactly as `source` absence means the owning worker), and `status` is omitted for a routine 200 on an ordinary row. SEND always carries its submit code, KILL keeps an explicit 200 so destructive completion is decisive, and every non-200 stays explicit. A partially hidden open row also carries `"folded":["<scope>",...]`; coordinate gaps in its body make the omission explicit without renumbering later lines. `path` is the complete model-facing log identity: when a projected operation exists it ends in `/OP`, and no separate `op` field duplicates it. It leads each entry object; the remaining members follow in stable alphabetical order. A present authored operation annotation appears as `annotation`; its absence omits the field. Nonempty `tags` is the row's complete deduplicated, sorted folksonomy; an untagged row omits it. When an automatic bounded projection differs from the visibility-selected 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. Every row's accounting: {§packet-token-accounting}.
|
|
1572
1644
|
|
|
1573
1645
|
- §packet-token-accounting Every row reports its real weight so the packet self-reconciles against the budget: `tokensBody` is the projected body's nonzero weight whenever a canonical body would render (never `0` — a priceless OPEN is field absence), and `tokensActive` is the complete row's weight in the packet right now. The metadata share is derivable (`tokensActive − tokensBody` when open; `tokensActive` otherwise) and is never serialized — it feeds no curation decision. Thus FOLD removes the rendered body's weight while KILL removes `tokensActive`; on a folded row `tokensBody` previews the body share an OPEN would activate. The completed row, including its accounting field and framing, is measured to a fixed point. 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 state). {§jsonplurnk} {§packet-jsonplurnk-exception}
|
|
1574
1646
|
|
|
@@ -1600,17 +1672,17 @@ ordinary bounded bodies expose their displayed and complete chunk extents there.
|
|
|
1600
1672
|
|
|
1601
1673
|
### §turn-ops-entry The admitted turn program
|
|
1602
1674
|
|
|
1603
|
-
§turn-ops-log-curation A source-backed turn preserves its **exact admitted Plurnk program** as an actionless log item in addition to the ordinary result row for every dispatched statement. `op` is null, `attrs.kind="turnOps"` identifies the
|
|
1675
|
+
§turn-ops-log-curation A source-backed turn preserves its **exact admitted Plurnk program** as an actionless log item in addition to the ordinary result row for every dispatched statement. `op` is null, `attrs.kind="turnOps"` identifies the durable type, `origin` is the turn producer, no target exists, `tx` is empty, and the source lives in `rx.content`, typed `text/vnd.plurnk`. Its canonical model-facing address appends the lowercase `/ops` leaf to its three-part coordinate. The packet does not duplicate that identity as `kind` metadata. It is line-numbered and READ/FIND/OPEN/FOLD/KILL-able like any active log body. The worker-initialization `turnOps` is born OPEN because it is the worked orientation example; every other `turnOps`, including model inference and overflow recovery, is born FOLDED and remains available on demand. Log-KILL clears the `writableBy` gate for a model-authored item and retires only its active projection under {§log-history-projection}; the exact program remains forensic history. Log's handler surface keeps every other mutating op at 501. The shared executor writes exactly one after every admitted source-backed turn.
|
|
1604
1676
|
|
|
1605
|
-
§rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program. The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, and the exact latest rejected response. It is born durably FOLDED and projected OPEN only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
1677
|
+
§rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program. The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, the canonical model-facing `/attempt` leaf, and the exact latest rejected response. The packet does not duplicate that identity as `kind` metadata. It is born durably FOLDED and projected OPEN only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
1606
1678
|
|
|
1607
|
-
- §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
|
|
1679
|
+
- §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 complete `[start-end]` segment in any numeric coordinate slot selects that inclusive decimal interval; brackets elsewhere retain ordinary path-glob meaning. Every rendered row appends one canonical model-facing leaf: `/OP` for an operation, `/ops` for an admitted turn program, or `/attempt` for a rejected emission. The leaf names identity rather than adding a resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive leaf is authoritative and a disagreement resolves 404. Typed entry materialization therefore resolves as `/READ` while retaining its durable `EDIT` event ({§exec-entry-sink}). `log:///1/2/*` still selects the turn's item rows, while `log:///**/READ`, `log:///**/ops`, and `log:///**/attempt` deliberately filter canonical leaves.
|
|
1608
1680
|
- §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.
|
|
1609
1681
|
- §log-curation-set-selection **Row selection and body scope are independent** — target/glob, optional body matcher, and optional ALL-tags filter compose by intersection into the affected row set. An optional `<L>` or `<SL,EL>` then intersects each selected canonical body; it never paginates or changes the selected set. Thus `## FOLD0 (log:///**/READ) <17,-1>` may change long READs and no-op on short ones while reporting every selected row in `matched`.
|
|
1610
1682
|
|
|
1611
|
-
§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 folded
|
|
1683
|
+
§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 active/folded state before and after, 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, projection changes, tag changes, and landed effects commit in one database statement with each before state as a collision guard. The authored program also survives verbatim in its `turnOps` item. 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.
|
|
1612
1684
|
|
|
1613
|
-
§kill-log-receipt-suppressed **A successful KILL of
|
|
1685
|
+
§kill-log-receipt-suppressed **A successful KILL of an active log item is suppressed from the render too.** It retires the selected rows from the worker's active projection under {§log-history-projection}; it does not delete their execution history. The KILL event, exact active/folded transition for every target, and authored `turnOps` remain durable, while none creates replacement packet weight. The suppression is **scoped to log targets**: a `KILL` of a `worker://` note, an `sh://` stream, or another stored artifact retains its scheme-owned world or process semantics and stays visible. A killed exact coordinate resolves 404 in ordinary log operations; a well-formed broad selection with no active matches remains the 204 no-op of {§log-curation-folder-idiom}. Failed KILL renders like every operation error.
|
|
1614
1686
|
|
|
1615
1687
|
### §log-sensitive-request-evidence Durable request evidence
|
|
1616
1688
|
|
|
@@ -1620,10 +1692,11 @@ secret detection.
|
|
|
1620
1692
|
|
|
1621
1693
|
| Surface | Durable rule |
|
|
1622
1694
|
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1623
|
-
| Operation target | Each non-null URL username or password becomes `__redacted__`;
|
|
1624
|
-
|
|
|
1695
|
+
| Operation target | Each non-null URL username or password becomes `__redacted__`; `raw` is rebuilt from that pure projected target. |
|
|
1696
|
+
| Scheme metadata modifier | Every present block becomes `__redacted__` wholesale; only ordered block count and represented-slot presence survive ({§scheme-metadata-modifier}). |
|
|
1697
|
+
| COPY/MOVE destination | The nested destination target receives the identical URL-credential projection. URI component columns derive from the projected primary target, so columns and `tx` cannot disagree. |
|
|
1625
1698
|
| Query and authored body | Preserved exactly; they are authored content and URI identity, not structurally identifiable credential slots. |
|
|
1626
|
-
| Parser failure | Preserves the structural diagnosis and source position without quoting
|
|
1699
|
+
| Parser failure | Preserves the structural diagnosis and source position without quoting scheme-metadata contents ({§scheme-metadata-modifier}). |
|
|
1627
1700
|
| Client, fork, packet, and digest | Consume the stored projection; none owns a second redaction policy. |
|
|
1628
1701
|
| Model-call evidence and source artifacts | `model_calls.response` under {§emission-admission}, `turnOps` under {§turn-ops-log-curation}, and `emissionAttempt` under {§rejected-emission-entry} remain exact forensic evidence and are the explicit exception. |
|
|
1629
1702
|
|
|
@@ -1640,8 +1713,9 @@ body: ResourceSelection (destination), signal: tags | null }`.
|
|
|
1640
1713
|
2. Resolve destination path, channel, and optional text scope. Source and
|
|
1641
1714
|
destination mimetypes must agree or the result is 415. Destination anchors
|
|
1642
1715
|
resolve independently under {§line-anchors}.
|
|
1643
|
-
3. A scoped destination must already exist and is mutated through the
|
|
1644
|
-
destination scheme's `editBatch`.
|
|
1716
|
+
3. A partial scoped destination must already exist and is mutated through the
|
|
1717
|
+
destination scheme's `editBatch`. A complete-value destination scope on an
|
|
1718
|
+
absent resource is creation under {§fs-write-surface}.
|
|
1645
1719
|
4. An unscoped destination writes only its selected channel. Existing other
|
|
1646
1720
|
channels survive.
|
|
1647
1721
|
- §copy-conflict-409 Different content in that channel is 409.
|
|
@@ -1795,11 +1869,11 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1795
1869
|
addresses a worker (`## SEND0 (worker://<name>)`), an outbound agent (`a2a://`),
|
|
1796
1870
|
or a scheme that implements SEND (an `https://` POST); with `[410]` it names a
|
|
1797
1871
|
resource to delete. A SEND to a scheme the model may not write (the prompt, the
|
|
1798
|
-
log) is refused 400 `send-target-not-a-recipient
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
answers 501
|
|
1872
|
+
log) is refused 400 `send-target-not-a-recipient`, never the unrelated writer
|
|
1873
|
+
rule. The detail states only that the addressed scheme is not a recipient;
|
|
1874
|
+
neutral recovery distinguishes targetless replies from directed SEND without
|
|
1875
|
+
guessing which one was intended. A scheme that does not implement SEND
|
|
1876
|
+
answers its ordinary factual 501 without grafting a guessed recovery onto it.
|
|
1803
1877
|
- §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.
|
|
1804
1878
|
- §send-premature-terminate **Premature terminate — the pending set.**
|
|
1805
1879
|
A model's completion claim is gated by one rule: *nothing pending may be silently
|
|
@@ -1813,7 +1887,10 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1813
1887
|
retrieval members are unchanged. The set is judged at the disposition's own dispatch, after
|
|
1814
1888
|
earlier operations in the emission. `[200]` over any member is refused 409
|
|
1815
1889
|
and the loop continues; every refusal strikes uniformly, including a
|
|
1816
|
-
retrieval-only refusal.
|
|
1890
|
+
retrieval-only refusal. Its Problem reports only the bounded pending kinds
|
|
1891
|
+
`streams`, `workers`, `receipts`, `failed-stream-results`, and
|
|
1892
|
+
`worker-results`; it never embeds commands, stream handles, result bodies, or
|
|
1893
|
+
a presumed recovery. The pending kind changes the factual Problem class, not
|
|
1817
1894
|
rail accounting. `[499]` deliberately abandons regardless.
|
|
1818
1895
|
- §send-administrative-terminal **An administrative terminal closes its own
|
|
1819
1896
|
transaction.** A client, plugin, or `_plurnk` operation program runs in its
|
|
@@ -1842,12 +1919,15 @@ a relative target against the directory the command would run in — the project
|
|
|
1842
1919
|
root, or the shell's own cwd when the workspace has none — and inspects it
|
|
1843
1920
|
before anything spawns: a directory becomes the working directory, a file is the
|
|
1844
1921
|
script; anything else is refused `400 target-not-found`, naming that directory
|
|
1845
|
-
and
|
|
1922
|
+
and giving the applicable accepted form without inferring what the model meant.
|
|
1923
|
+
When the target is a registered tool of another runtime, recovery gives that
|
|
1924
|
+
tool's exact runtime-qualified invocation; otherwise it distinguishes an existing
|
|
1925
|
+
directory/script target from a targetless shell-command body. The started receipt always
|
|
1846
1926
|
names the working directory. The EXEC `(path)` is one of cwd, script, or tool
|
|
1847
1927
|
name — the runtime's declaration decides which (interpreters: cwd or script;
|
|
1848
1928
|
tool families: tool name) — and a command is never a target. The default shell
|
|
1849
|
-
is taught as
|
|
1850
|
-
|
|
1929
|
+
is taught as targetless bare `EXEC`; `[sh]` remains the explicit form, and an
|
|
1930
|
+
authored directory target remains an optional cwd override.
|
|
1851
1931
|
|
|
1852
1932
|
| Declared target kind | Authored target | Canonical effect target | Executor realization |
|
|
1853
1933
|
| -------------------- | --------------------------------------- | ----------------------- | --------------------------------------------------------- |
|
|
@@ -1899,7 +1979,7 @@ Loop-flag authority follows the selected runtime's declaration:
|
|
|
1899
1979
|
| Absent, `literal`, local `path`, or local `resource` | `exec` |
|
|
1900
1980
|
| Non-file `resource` | `exec` and the addressed source scheme |
|
|
1901
1981
|
|
|
1902
|
-
Worker and runtime-stream authorities, query, fragment,
|
|
1982
|
+
Worker and runtime-stream authorities, query, fragment, the scheme metadata modifier, and
|
|
1903
1983
|
every other component of a `resource` address retain their owning READ
|
|
1904
1984
|
semantics. A failed source READ is preserved as the proposal-application
|
|
1905
1985
|
failure. A successful READ with no string representation is refused 422; an
|
|
@@ -1918,9 +1998,10 @@ Worker Functionality providers may atomically overlay additional names under
|
|
|
1918
1998
|
{§module-worker-capabilities}; a name has one owner within a worker, while
|
|
1919
1999
|
independent workers may use the same name. An absent or empty tag selects
|
|
1920
2000
|
`sh`; a non-empty tag selects exactly that registered executable tool. Unknown
|
|
1921
|
-
tags are refused 501 with
|
|
1922
|
-
|
|
1923
|
-
|
|
2001
|
+
tags are refused 501 with the advertised catalogue and are never reinterpreted
|
|
2002
|
+
as shell command words. The common mistaken `[shell]` alias is narrowly told to
|
|
2003
|
+
omit that signal for the default shell; arbitrary unknown tags receive no guessed
|
|
2004
|
+
alternative intent. An unavailable runtime is also 501 and carries the probe
|
|
1924
2005
|
`detail`.
|
|
1925
2006
|
|
|
1926
2007
|
For a family runtime, `ExecutorRegistry.toolRegistry(tag, workerId)`
|
|
@@ -1932,8 +2013,9 @@ catalogue.
|
|
|
1932
2013
|
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}
|
|
1933
2014
|
|
|
1934
2015
|
**Timeout and poll — `<T,P>` on the `<L>` slot (grammar 0.74.20).** EXEC
|
|
1935
|
-
repurposes the line-marker slot as `<timeout, poll>` in **
|
|
1936
|
-
|
|
2016
|
+
repurposes the line-marker slot as `<timeout, poll>` in **minutes** — agentic
|
|
2017
|
+
latencies make a sub-minute horizon a trap — converted at the parse boundary to the
|
|
2018
|
+
catalog's internal `stream.seconds`. The SEND `[202] <T>` wait horizon is minutes too.
|
|
1937
2019
|
|
|
1938
2020
|
§exec-timeout `T` (`mark[0]`) caps the spawn's lifetime. At `T>0` the service
|
|
1939
2021
|
aborts it — a bounded reap, polite signal then SIGKILL after
|
|
@@ -1947,7 +2029,7 @@ its terminal output surfaces born-OPEN like any close ({§exec-stream}).
|
|
|
1947
2029
|
§exec-poll `P` (`mark[1]`) is the **poll cadence**, stored on the subscription.
|
|
1948
2030
|
While the loop is blocked on a SEND signal `202` wait for that stream, the daemon arms
|
|
1949
2031
|
a per-worker timer for the tightest open poll cadence and resumes the blocked
|
|
1950
|
-
loop every P
|
|
2032
|
+
loop every P minutes, floored by `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS` so it cannot tick
|
|
1951
2033
|
faster than the optimistic settlement scale, to inspect progress. It does **nothing while the
|
|
1952
2034
|
loop is active** because ambient stream deltas already surface progress. An
|
|
1953
2035
|
open stream without `P` uses exponential backoff
|
|
@@ -1978,10 +2060,12 @@ may use a semantic Worker name there, but the private numeric id never appears
|
|
|
1978
2060
|
in a URI or packet. `plurnk` and `commons` are reserved Workers, and `~` is the
|
|
1979
2061
|
current-Worker sigil; none can be minted by a spawn or client.
|
|
1980
2062
|
|
|
1981
|
-
§stream-owner-scoped **Capability streams are owner-scoped.** Concurrent workers' stream coordinates are loop-relative and IDENTICAL (every worker's first loop is sequence 1), so the entry identity keys on the owner and identical coordinates across workers are distinct rows. The address's authority names the owner: **empty = the calling worker** — your own streams need no qualifier, so a fan-out sibling's output can never surface under your READ — and a **named authority** reaches that worker's streams
|
|
2063
|
+
§stream-owner-scoped **Capability streams are owner-scoped.** Concurrent workers' stream coordinates are loop-relative and IDENTICAL (every worker's first loop is sequence 1), so the entry identity keys on the owner and identical coordinates across workers are distinct rows. The address's authority names the owner: **empty = the calling worker** — your own streams need no qualifier, so a fan-out sibling's output can never surface under your READ — and a **named authority** reaches that worker's streams for any worker of the workspace (the parent designs the topology by what it names to whom; the engine imposes none, #394; an unknown name resolves 404). A child is told its parent's name in its packet (`parent-worker`). KILL stays self-only — a parent controls a child through the worker lifecycle, never by reaching into its streams. The storage pathname stays the bare loop coordinate; the owner rides the column, so nothing model-facing carries a worker id. A stream 404 never discloses existence, but it names the address space: the coordinate shape, the unqualified self, the descendant-by-name form, and that a tool's own ids are arguments, not addresses.
|
|
1982
2064
|
|
|
1983
2065
|
§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.
|
|
1984
2066
|
|
|
2067
|
+
§workspace-auto-name **Workspace auto-names are five anchor-alphabet characters.** A `workspace.create` without a name draws five characters from the {§line-anchors} alphabet `0-9A-Za-z` (uniformly, from a cryptographic source), redrawing on the astronomically rare collision. No prefix, timestamp, or origin marker: a name never encodes where a workspace came from, so nothing can grow load-bearing on it.
|
|
2068
|
+
|
|
1985
2069
|
§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.
|
|
1986
2070
|
|
|
1987
2071
|
§effect-policy-tunable **Effect admission is deployment-tunable.** The default
|
|
@@ -2000,7 +2084,22 @@ two states and no others:
|
|
|
2000
2084
|
| state | what the model receives |
|
|
2001
2085
|
|---|---|
|
|
2002
2086
|
| active | nothing in the Log. The `## Child Streams` pointer names the stream with each channel's size and its growth since the last packet ({§child-orientation}); the model READs any range it wants. |
|
|
2003
|
-
| terminal | ONE `origin=_plurnk` READ at `<runtime>:///<coord>#<channel>`, born OPEN, that is exactly a markerless READ of the channel — its first page ({§read-selection-projection}: lines 1–16, the whole channel when it fits, the channel's own mimetype), the `range` extent, and
|
|
2087
|
+
| terminal | ONE `origin=_plurnk` READ at `<runtime>:///<coord>#<channel>`, born OPEN, that is exactly a markerless READ of the channel — its first page ({§read-selection-projection}: lines 1–16, the whole channel when it fits, the channel's own mimetype), the `range` extent, terminal status and Problem, `terminal: true`, any producer-supplied integer `exitCode`, and `source: log:///<coord>/EXEC` linking the causal invocation. |
|
|
2088
|
+
|
|
2089
|
+
§exec-concurrency **Bounded admission per workspace (#389).** At most
|
|
2090
|
+
`PLURNK_SERVICE_EXEC_CONCURRENCY` executions run at once in one workspace (shipped `12`;
|
|
2091
|
+
`-1` unbounded); the scope is the workspace, so neither delegation nor later turns
|
|
2092
|
+
bypass it and no other workspace can starve it. Every admitted EXEC still creates its
|
|
2093
|
+
entry, channels, and open subscription before its receipt returns, so queued work is
|
|
2094
|
+
cancellable, restart-reconcilable, completion-gated, and observed through the ordinary
|
|
2095
|
+
stream mechanics ({§exec-stream}). The receipt tells the truth once and never rewrites
|
|
2096
|
+
it: an immediate slot is `200 { outcome: "started" }`; delayed work is
|
|
2097
|
+
`202 { outcome: "queued", executionsAhead, concurrency }`, and the channel stays
|
|
2098
|
+
`active` in the existing live sense — output growth and terminal settlement are the
|
|
2099
|
+
current truth. Admission is FIFO within the workspace; queue residence does not consume
|
|
2100
|
+
the execution timeout; a KILL while queued never invokes the executor and closes the
|
|
2101
|
+
stream through the normal 499 path. The scheduler is the EXEC scheme's; the knob is the
|
|
2102
|
+
service's ({§operator-config}), fail-hard on any other value.
|
|
2004
2103
|
|
|
2005
2104
|
§exec-stream-page **An unrequested delivery never exceeds the retrieval page.** The
|
|
2006
2105
|
terminal observation is the same page a markerless READ returns, whatever the
|
|
@@ -2015,7 +2114,8 @@ to the model — by the Child Streams pointer while active, by the terminal
|
|
|
2015
2114
|
observation at close — so the pointer can state growth and no partial document
|
|
2016
2115
|
or record ever reaches the model. The terminal observation and its cursor
|
|
2017
2116
|
transition commit atomically; a terminal state with an empty channel still
|
|
2018
|
-
produces one bodyless conclusion row
|
|
2117
|
+
produces one bodyless conclusion row whose terminal fact, causal EXEC link, and
|
|
2118
|
+
available exit code make completion explicit without invented narration. OPEN, FOLD, or KILL may curate
|
|
2019
2119
|
that log row without rewinding the cursor or publishing the terminal result
|
|
2020
2120
|
again; the exact terminal result and channel content remain READable at the
|
|
2021
2121
|
stream address. Every READ then obeys {§body-projection} and therefore renders
|
|
@@ -2055,7 +2155,7 @@ stream cannot fall through an internal `exec`-only query. {§stream-control}
|
|
|
2055
2155
|
|
|
2056
2156
|
§proposal-outcome-terse-error A caller-supplied `outcome` overrides the default. On an **accept** it stays forensics-only; a **non-accept** carries it as the `rx`'s terse `error` token (`write_failed` / `rejected` / `timeout` — one word, never prose), because "the action didn't occur" without the mechanical why leaves the model acting on a phantom success (the fan-out dead-park: an ENOENT apply rendered as a mute 400).
|
|
2057
2157
|
|
|
2058
|
-
§proposal-proposed-hidden **A proposed row is invisible until it resolves.** A `state='proposed'` / 202 row is withheld from
|
|
2158
|
+
§proposal-proposed-hidden **A proposed row is invisible until it resolves.** A `state='proposed'` / 202 row is withheld from both packet materialization and `log/entry`; it surfaces exactly once after resolution, carrying its terminal status — models and clients see outcomes, never pending proposals.
|
|
2059
2159
|
|
|
2060
2160
|
### §proposal-projection One durable proposal, one client projection
|
|
2061
2161
|
|
|
@@ -2067,7 +2167,7 @@ Core derives the contracts-owned `ProposalProjection` from the durable proposed
|
|
|
2067
2167
|
| `target` | canonical `{ scheme, authority, pathname }` from `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target |
|
|
2068
2168
|
| `body` | proposed operation result `rx.body`; absent means the empty review body |
|
|
2069
2169
|
| `attrs` | proposed log row `attrs` object |
|
|
2070
|
-
| `
|
|
2170
|
+
| `policy` | validated complete persisted loop policy |
|
|
2071
2171
|
| `disposition` | {§proposal-disposition}; the same value drives automatic settlement and client presentation |
|
|
2072
2172
|
|
|
2073
2173
|
Workspace scope remains the event envelope / seam argument ({§notifications-envelope-carries-workspaceid}); it is not forged into `ProposalProjection`. Malformed durable JSON, target metadata, result envelopes, loop policy, or final projection fails at core with its cause; after insertion, core terminalizes that row as a 500 `policy_failed` before propagating the internal failure, so no waiter or durable stopped world is orphaned.
|
|
@@ -2091,15 +2191,15 @@ removes ownerless rows without fabricating cancellation, payload, or replay.
|
|
|
2091
2191
|
|
|
2092
2192
|
### §proposal-disposition Settlement authority and precedence
|
|
2093
2193
|
|
|
2094
|
-
`ProposalDisposition` is either `{ owner: "client" }` or `{ owner: "loop", decision: "accept" | "reject", outcome? }`. The
|
|
2194
|
+
`ProposalDisposition` is either `{ owner: "client" }` or `{ owner: "loop", decision: "accept" | "reject", outcome? }`. The persisted loop policy determines it exactly:
|
|
2095
2195
|
|
|
2096
|
-
| `
|
|
2097
|
-
|
|
|
2098
|
-
|
|
|
2099
|
-
|
|
|
2100
|
-
|
|
|
2196
|
+
| `policy.proposals` | Disposition |
|
|
2197
|
+
| ------------------ | ---------------------------------------- |
|
|
2198
|
+
| `review` | client |
|
|
2199
|
+
| `accept` | loop accept |
|
|
2200
|
+
| `reject` | loop reject, outcome `no_review_channel` |
|
|
2101
2201
|
|
|
2102
|
-
|
|
2202
|
+
Capability admission precedes this decision, so proposal disposition cannot grant a denied capability. Loop-owned settlement occurs before observational notification; observer failures are diagnosed with their cause and cannot change disposition or leave an eligible automatic proposal pending.
|
|
2103
2203
|
|
|
2104
2204
|
---
|
|
2105
2205
|
|
|
@@ -2137,6 +2237,7 @@ Model sees lifecycle events in the `log` section per turn.
|
|
|
2137
2237
|
### §stream-control Stream control and writes
|
|
2138
2238
|
|
|
2139
2239
|
- **Cancel:** `## SEND0 [499] (https://feed.example/x)` — the service invokes the handle registered by `subscriptions.open()` and aborts the composed subscription signal.
|
|
2240
|
+
- **Kill:** `## KILL0 (sh:///1/2/3)` — the model terminates its own runtime stream. This is stream control, not a write: the output scheme's `writableBy` never gates it, `Exec.kill` scopes the address to the caller ({§stream-owner-scoped}), and a finished stream answers 410 under its own tag. A queued execution ({§exec-concurrency}) is cancelled the same way and never enters its executor.
|
|
2140
2241
|
- **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.
|
|
2141
2242
|
- **Other stream write:** `## SEND0 [200] (…)` remains scheme-defined, including exec stdin.
|
|
2142
2243
|
|
|
@@ -2258,7 +2359,7 @@ freshness remains the owning family's concern.
|
|
|
2258
2359
|
| Mimetypes | `@plurnk/plurnk-mimetypes` | `application-ipynb`, `application-json`, `application-jsonl`, and `application-xml` format leaves. |
|
|
2259
2360
|
| | | `text-csv`, `text-diff`, `text-dotenv`, `text-html`, `text-ini`, `text-markdown`, and `text-plain` format leaves. |
|
|
2260
2361
|
| | | Fixed `embeddings` artifact. All names use the `@plurnk/plurnk-mimetypes-*` prefix. |
|
|
2261
|
-
| Executors | `@plurnk/plurnk-execs` | `common`, `
|
|
2362
|
+
| Executors | `@plurnk/plurnk-execs` | `common`, `jq`, and `sqlite` leaves under the `@plurnk/plurnk-execs-*` prefix. |
|
|
2262
2363
|
|
|
2263
2364
|
The independently published `application-pdf` handler and `tokenizers`
|
|
2264
2365
|
artifact are opt-in leaves. Installing either beside the service admits it
|
|
@@ -2303,7 +2404,7 @@ service manifest edit.
|
|
|
2303
2404
|
- Backpressure caps — none ({§stream-constraints}).
|
|
2304
2405
|
- Stream cancel — SEND signal `499` ({§stream-control}).
|
|
2305
2406
|
- Delete — `KILL` (entry-KILL, the canonical delete, {§move}); SEND signal `410` also deletes as a side-effect ({§send-dispatch}).
|
|
2306
|
-
- §loop-
|
|
2407
|
+
- §loop-policy-effective-read Per-loop policy — `loops.policy` persists one complete immutable `LoopPolicy`; every runtime policy read validates that snapshot 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.
|
|
2307
2408
|
- Default-channel wire rendering — {§channel-selection}.
|
|
2308
2409
|
|
|
2309
2410
|
---
|
|
@@ -2362,12 +2463,15 @@ Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-
|
|
|
2362
2463
|
|-------------------------------------------------------------|---------|---------|
|
|
2363
2464
|
| `PLURNK_SERVICE_DB_PATH` | `$XDG_DATA_HOME/plurnk/plurnk.db` | SQLite file path; an explicit non-empty value overrides the derived default. |
|
|
2364
2465
|
| `PLURNK_HOST` | `127.0.0.1` | Bind address for the listener. Local-only by default. |
|
|
2365
|
-
| `PLURNK_PORT` | `
|
|
2366
|
-
| §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | `1` | Hard service ceiling: only `1` admits Git membership, status, branch batching
|
|
2466
|
+
| `PLURNK_PORT` | `1066` | TCP port for THE client surface — the AG-UI+ listener (the plurnk-agui plugin module binds it at boot). Production is single-listener. |
|
|
2467
|
+
| §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | `1` | Hard service ceiling: only `1` admits Git membership, status, and branch batching; every other value denies them. |
|
|
2367
2468
|
| §operator-config-file-create-scope `PLURNK_SERVICE_FILE_CREATE_SCOPE` | `root` | Hard file-creation ceiling: `none < root < namespace`. `none` denies new filesystem files, `root` admits only paths inside `project_root`, and `namespace` also admits canonical outside-root paths. Existing-member writes are unaffected. |
|
|
2469
|
+
| `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` | `104857600` | Byte ceiling in `1..104857600` for one workspace-file snapshot ({§membership-materialization-limit}). |
|
|
2368
2470
|
| `PLURNK_SERVICE_MAX_TURNS` | `-1` | Operator inference-turn **ceiling** — `-1` = no cap; a positive value clamps `runLoop({maxTurns})`. The effective value is persisted on the durable loop and counts completed model/inference turns cumulatively across every `202` park/resume; `_plurnk`, client, and plugin turns remain chronology but consume none of this allowance. |
|
|
2369
2471
|
| `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). |
|
|
2370
2472
|
| §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}). |
|
|
2473
|
+
| `PLURNK_SERVICE_PROVIDER_RECOVERY` | `900000` | ms a turn keeps re-issuing its provider call after a recoverable provider failure before the loop parks ({§provider-recovery}); `0` parks at once. |
|
|
2474
|
+
| `PLURNK_SERVICE_PROVIDER_RECOVERY_BACKOFF` | `5000` | First recovery delay (ms); doubles per failure, capped at twelve times itself ({§provider-recovery}). |
|
|
2371
2475
|
| `PLURNK_SERVICE_MAX_STRIKES` | `6` | Consecutive admitted-turn strike threshold ({§engine-rails}). |
|
|
2372
2476
|
| `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. |
|
|
2373
2477
|
| `PLURNK_SERVICE_PREVIEW_LINES` | `16` | Maximum lines in an ordinary bounded log-body projection ({§body-projection}). |
|
|
@@ -2380,6 +2484,8 @@ Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-
|
|
|
2380
2484
|
| `PLURNK_SERVICE_REQUIEM_MAX_TOKENS` | `16384` | Initial forensic witness output allowance ({§digest-requiem}). |
|
|
2381
2485
|
| `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS` | `32768` | Retry allowance; must be at least the initial requiem allowance ({§digest-requiem}). |
|
|
2382
2486
|
| `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}). |
|
|
2487
|
+
| `PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` | `none` | Ceiling for a model's `members` definitions in the lattice `none < root < namespace`; `none` refuses every model definition ({§members-model-scope}). |
|
|
2488
|
+
| `PLURNK_SERVICE_EXEC_CONCURRENCY` | `12` | Executions admitted at once per workspace; the rest queue FIFO with `202 queued` receipts; `-1` unbounded ({§exec-concurrency}). |
|
|
2383
2489
|
| `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}). |
|
|
2384
2490
|
| §operator-config-worker-warm `PLURNK_SERVICE_WORKER_WARM_MS` | `900000` | Milliseconds a lease-free worker Functionality snapshot remains warm; `0` cools without grace and `-1` disables time-based cooling ({§module-worker-residency}). |
|
|
2385
2491
|
| `PLURNK_SERVICE_WORKER_WARM_MAX` | `2` | Maximum lease-free worker Functionality snapshots retained process-wide; `0` retains none and `-1` disables the idle-LRU bound ({§module-worker-residency}). |
|
|
@@ -2408,7 +2514,7 @@ instead of a user's boot, and a dead knob cannot ship.
|
|
|
2408
2514
|
| Owner | Configuration |
|
|
2409
2515
|
|---|---|
|
|
2410
2516
|
| `.env.test` | Safe default model selection and universal real-model gate posture; no alias declarations, routes, secrets, model tuning, or cost/sandbox ceilings. |
|
|
2411
|
-
| Live/demo scripts | The repository
|
|
2517
|
+
| Live/demo scripts | The repository policy path and runner topology. |
|
|
2412
2518
|
| Benchlets | Their snapshotted policy, workspace restrictions, and task-specific exceptions; direct env wins over the profile. |
|
|
2413
2519
|
| Operator env/shell | Model alias declarations and explicit selection overrides, provider capability such as GBNF, endpoints, credentials, tuning, and deliberate ceiling overrides. |
|
|
2414
2520
|
| `test/setup.ts` | Mock-only alias, envelope, resource, storage, and isolation fixtures; unit/integration never consume the real-model profile. |
|
|
@@ -2447,7 +2553,7 @@ boundary. Operator-arcane knobs stay environment-only.
|
|
|
2447
2553
|
| `settings.git` | Boolean | Tightening denial {§operator-config-workspace-git} |
|
|
2448
2554
|
| `settings.fileCreateScope` | `none`, `root`, or `namespace` | Tightening ceiling {§operator-config-workspace-file-create-scope} |
|
|
2449
2555
|
| `settings.client` | Nonempty string | Stable self-identification {§client-metadata} |
|
|
2450
|
-
| `settings.
|
|
2556
|
+
| `settings.capabilities` | `CapabilityPolicy` | Subtractive capability layer {§operator-config-workspace-capabilities} |
|
|
2451
2557
|
|
|
2452
2558
|
The composition families remain distinct so one setting's semantics never
|
|
2453
2559
|
leak into another.
|
|
@@ -2462,22 +2568,17 @@ leak into another.
|
|
|
2462
2568
|
workspace: a client tightens the runaway-op guard and never raises it past
|
|
2463
2569
|
the operator's.
|
|
2464
2570
|
- §operator-config-workspace-max-commands-floor The cap bounds *actions* only.
|
|
2465
|
-
PLAN
|
|
2571
|
+
PLAN and the final disposition `SEND` (`102`, `200`, `202`,
|
|
2466
2572
|
`300`, or `499`) are never counted and always dispatch, so `0` is a valid
|
|
2467
2573
|
floor — the tightest — admitting a plan and disposition with zero actions.
|
|
2468
2574
|
- §operator-config-workspace-git `settings.git` (`false`) **denies** git for the workspace (`PLURNK_SERVICE_GIT_ALLOWED` AND workspace) — the client opts its workspace out of git membership and working-tree status; it can never re-enable git past the operator's service-wide lockout.
|
|
2469
2575
|
- §operator-config-workspace-file-create-scope `settings.fileCreateScope` narrows `PLURNK_SERVICE_FILE_CREATE_SCOPE` by the ordered lattice `none < root < namespace`; a workspace may disable creation or confine a namespace-enabled service to its root, but never widen the operator's ceiling. Unknown service values fail configuration validation and unknown workspace values fail `workspace.create`.
|
|
2470
|
-
- §operator-config-workspace-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
intersects it: settings cannot register or re-enable a runtime. A canonical
|
|
2477
|
-
key for a currently absent tag is accepted as inert policy and applies if a
|
|
2478
|
-
worker Functionality provider later publishes that tag. Dispatch and
|
|
2479
|
-
model-facing tool-resource materialization use the same registered-set intersection and policy
|
|
2480
|
-
predicate, so a workspace-disabled tag is neither executable nor taught.
|
|
2576
|
+
- §operator-config-workspace-members-model-scope `settings.membersModelScope` narrows `PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` by the same lattice; a workspace may refuse the model's definitions entirely under a permissive service ({§members-model-scope}).
|
|
2577
|
+
- §operator-config-workspace-capabilities `settings.capabilities` is one
|
|
2578
|
+
workspace-stable `CapabilityPolicy` layer in {§capability-admission}. It may
|
|
2579
|
+
narrow any registered operation, scheme, runtime, tool, access class, or
|
|
2580
|
+
trait through the canonical `only`/`deny` selectors; it cannot register a
|
|
2581
|
+
capability or restore one removed by the service layer.
|
|
2481
2582
|
|
|
2482
2583
|
Feature-flag bools use `process.env.X === "1"` exactly — never `=== "true"`.
|
|
2483
2584
|
|
|
@@ -2499,12 +2600,13 @@ names, request validation, discovery result, and event projection.
|
|
|
2499
2600
|
|
|
2500
2601
|
```mermaid
|
|
2501
2602
|
flowchart LR
|
|
2603
|
+
bind["Host may pre-bind client-interface listener<br/>unready"] --> register
|
|
2502
2604
|
register["Daemon.registerModule"] --> setup["module.setup(ModuleSetupSeam)"]
|
|
2503
2605
|
setup --> capabilities["Register static capabilities,<br/>workspace activators, and actions"]
|
|
2504
2606
|
capabilities --> ready["Process-wide schemes ready"]
|
|
2505
2607
|
ready --> recovery["Reconcile durable lifecycle"]
|
|
2506
2608
|
recovery --> start["module.start(ApplicationPort)"]
|
|
2507
|
-
start --> interface["Module-owned
|
|
2609
|
+
start --> interface["Module-owned client protocol<br/>ready"]
|
|
2508
2610
|
recovery -->|durable workspace work| demand["First workspace demand"]
|
|
2509
2611
|
interface -->|client workspace work| demand
|
|
2510
2612
|
demand --> lease["Acquire capability residency"]
|
|
@@ -2522,14 +2624,19 @@ flowchart LR
|
|
|
2522
2624
|
|
|
2523
2625
|
Every registered module's `setup` runs in registration order before any
|
|
2524
2626
|
module's `start`. Core then readies process-wide schemes, reconciles durable
|
|
2525
|
-
lifecycle, and starts modules in registration order.
|
|
2627
|
+
lifecycle, and starts modules in registration order. The production
|
|
2628
|
+
client-interface module may already own its socket under
|
|
2629
|
+
{§startup-listener-admission}; its `start` activates request handling without
|
|
2630
|
+
rebinding. Persisted workspaces with
|
|
2526
2631
|
no durable work stay passive until first demand; activation publishes their
|
|
2527
2632
|
complete capabilities and documentation before the demanding operation
|
|
2528
2633
|
proceeds. `setup` is the readiness boundary for every capability registered
|
|
2529
|
-
with Core: recovery may demand a workspace provider before `start`.
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2634
|
+
with Core: recovery may demand a workspace provider before `start`. For a
|
|
2635
|
+
pre-bound client interface, requests remain unavailable until `start`; every
|
|
2636
|
+
other module opens its module-owned exterior ingress only after recovery. No
|
|
2637
|
+
registered capability may depend on exterior ingress. Shutdown begins started
|
|
2638
|
+
and self-closing module closure in reverse order and surfaces aggregated close
|
|
2639
|
+
failures.
|
|
2533
2640
|
|
|
2534
2641
|
§module-discovery **Third-party daemon-module composition is manifest
|
|
2535
2642
|
discovery.** A package declares `plurnk: { kind: "module", module:
|
|
@@ -2650,6 +2757,11 @@ accepted model proposal converge on the same coordinator method; no family
|
|
|
2650
2757
|
invents a third management grammar, configuration path, proposal policy, or
|
|
2651
2758
|
hotload mechanism.
|
|
2652
2759
|
|
|
2760
|
+
Problem retryability is never inferred from numeric status. A coordinator
|
|
2761
|
+
failure names `retryable` only when its owning condition establishes whether an
|
|
2762
|
+
identical automatic replay is valid; in particular, absent Worker residency
|
|
2763
|
+
requires activation and is non-retryable as submitted.
|
|
2764
|
+
|
|
2653
2765
|
| Verb | Common contract |
|
|
2654
2766
|
|---|---|
|
|
2655
2767
|
| `list` | Project every definition with its origin, desired enabledness, and current state — `disabled`, `active`, `unavailable` with its exact Problem, or `authorization-required` — without exposing credentials. |
|
|
@@ -2663,7 +2775,9 @@ hotload mechanism.
|
|
|
2663
2775
|
declares its family (the action segment and EXEC tag), its one namespace owner,
|
|
2664
2776
|
the exact definition schema one `add` accepts, its service-contributed
|
|
2665
2777
|
definitions with their default enabledness, inert discovery, admission of an
|
|
2666
|
-
authored definition
|
|
2778
|
+
authored definition — told whether a client action or a model operation authored it, so a
|
|
2779
|
+
family may bound the model's authority ({§members-model-scope}) — two-phase preparation of
|
|
2780
|
+
the enabled set, and teardown.
|
|
2667
2781
|
Preparation returns the family's runtimes, its generated documents, one outcome
|
|
2668
2782
|
per enabled alias, and a snapshot with `commit`/`abort`; the coordinator
|
|
2669
2783
|
never tears down a previous snapshot behind the adapter — it commits after a
|
|
@@ -2713,8 +2827,12 @@ family per Worker.** Every activated Worker publishes, for each registered
|
|
|
2713
2827
|
family, one executor tagged with the family name whose registered targets are
|
|
2714
2828
|
exactly the six verbs; its documents render through
|
|
2715
2829
|
{§tools-resource-materialization} like every family, so the model learns the
|
|
2716
|
-
manager from `_plurnk/
|
|
2717
|
-
teaching.
|
|
2830
|
+
manager from `_plurnk/plurnk/<family>.md` and never from hand-written
|
|
2831
|
+
teaching. That document lists the six verbs in lifecycle order and teaches the
|
|
2832
|
+
definition from the family's own schema — one exact `add` example and a table of
|
|
2833
|
+
every field with its type, requirement, and meaning — and carries the family's own
|
|
2834
|
+
`discover` contract when the generic one does not fit; the model composes an `add`
|
|
2835
|
+
from the document alone, without a probe. `list` and `discover` are `read` effects and run ungated; `add`,
|
|
2718
2836
|
`enable`, `disable`, and `remove` are `host` effects and propose through
|
|
2719
2837
|
the ordinary Exec proposal lifecycle. A verb's JSON outcome streams into the
|
|
2720
2838
|
family's output entry. `ExecArgs` carries no Worker identity, which is why
|
|
@@ -2745,7 +2863,7 @@ Core's behavior behind them.
|
|
|
2745
2863
|
| §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. |
|
|
2746
2864
|
| §client-interaction-list Client interactions | `pendingClientInteractions(workspaceId)` | Intersects durable interaction rows with their live operation waiters and returns the contracts-owned projection; a row alone is not a resumable interaction. |
|
|
2747
2865
|
| §methods-client-interaction-resolve Client interactions | `resolveClientInteraction(interactionId, resolution)` | Validates and delivers one resolved payload or cancellation. Unknown, ownerless, and already-resolved identities fail before affecting an operation. |
|
|
2748
|
-
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, source?, maxTurns?,
|
|
2866
|
+
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, source?, maxTurns?, policy?, openPaths?, selector?, childSelector? })` | Validates a model worker and complete loop policy, persists it with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. A trusted adapter may identify the prompt's causal actor with one canonical `source`; ordinary clients cannot author it through their protocol surface. The exact terminal result arrives only through `loop/terminated`; parking and resuming do not replace the loop. |
|
|
2749
2867
|
| §methods-loop-cancel Loops | `cancelDrain(workerId, reason?)`; `cancelWorker({ workspaceId, workerId, reason? })` | `cancelDrain` begins durable structured cancellation and reports whether process-local work existed when called; queued or parked durable work is still terminalized when it is `false`. The ownership-bounded `cancelWorker` awaits that same tree cancellation and stream reap, so an exterior protocol can project the settled durable result without polling or fabricating state. |
|
|
2750
2868
|
| §methods-op-mirror Client dispatch | `dispatchClientAction({ workspaceId, workerId, functionalityWorkerId, statements })` | Dispatches already-parsed grammar statements as one client action in one administrative loop in the client worker, executing in the attached Worker's Functionality ({§actor-boundary-attached-functionality}). Every statement is an ordered client/operation turn, and every committed `log/entry` is emitted before the action promise resolves; a proposal may keep its turn, loop, and action promise open until resolution. Core exposes no per-op method family. |
|
|
2751
2869
|
| Client observation | `look({ workspaceId, workerId, functionalityWorkerId, statement })` | Runs an already-parsed READ through the full resolver in the attached Worker's Functionality without a log row. A non-READ statement is rejected ({§op-look}). |
|
|
@@ -2754,13 +2872,12 @@ Core's behavior behind them.
|
|
|
2754
2872
|
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
|
|
2755
2873
|
| Model catalog | `listModels(query)` | Returns one validated bounded {§model-catalog-wire} page under {§model-catalog}; performs no provider request or selection. |
|
|
2756
2874
|
| Client capabilities | `listClientDisplayCapabilities()` | Composes sorted scheme declarations ({§manifest-client-display}) followed by sorted MIME declarations ({§mimetype-client-display}) into the validated shared wire ({§client-display-capabilities}). The internal `exec` operation handler is excluded; its addressable runtime-tag scheme faces remain included. |
|
|
2757
|
-
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings
|
|
2875
|
+
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope, and emits global `workspace/created`. Creation and attachment are passive: neither starts derivation nor activates worker Functionality. `projectRoot` is established here or the workspace remains headless. |
|
|
2758
2876
|
| §methods-workspace-attach Workspace lifecycle | `attachWorkspace({ workspaceId, workerId?, workerName? })` | Validates ownership and returns a client envelope for an existing world. It does not retain caller or transport binding state in core. |
|
|
2759
2877
|
| §methods-model-worker Workspace lifecycle | `ensureModelWorker(workspaceId)` | Returns the workspace's stable default model worker, creating it on first use. A durable default-conversation role identifies it independently of worker name and root creation order. Repeated and concurrent calls return the same root; fresh conversations and forks do not replace it. |
|
|
2760
2878
|
| §methods-conversation-worker Workspace lifecycle | `createConversationWorker({ workspaceId, name? })` | Creates a distinct model-origin root worker with empty private history: a fresh conversation over the same world, not a fork or the stable default. |
|
|
2761
2879
|
| Workspace lifecycle | `forkWorker({ workspaceId, workerId, name? })` | Creates a child worker that branches the source worker's history while sharing workspace state. |
|
|
2762
2880
|
| §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the world's unique mutable name; workers, log, and membership remain intact. |
|
|
2763
|
-
| Workspace metadata | `constrain(...)`, `unconstrain(...)`, `listConstraints(...)`, `listMembers(...)` | Owns the membership overlay and returns its resolved effects; clients do not reimplement constraint semantics. |
|
|
2764
2881
|
| §methods-workspace-prompts Workspace metadata | `listPrompts(workspaceId, limit?)` | Returns nonempty loop-seed prompts from the workspace's model-origin root conversations, newest-first. The positive limit defaults to 100; spawned and forked child prompts are excluded. |
|
|
2765
2882
|
| Workspace metadata | `listWorkspaces()`, `workspaceDerivationStatus(...)` | Reads current workspace identity and derivation progress. |
|
|
2766
2883
|
| §methods-worker-read Worker topology | `readWorker({ workspaceId, identity })` | Ownership-bounds an exact id-or-name lookup and returns one durable Worker projection or `null` under {§application-worker-observation}. Supplying both identities or neither is invalid. |
|
|
@@ -2777,7 +2894,7 @@ already durable on the loop remains authoritative:
|
|
|
2777
2894
|
|-----------------------|------------------------------------------------------|--------------------------------|--------------------------------------|
|
|
2778
2895
|
| Provider/model | The resolved request selection must still agree. | Fold. | 409 provider conflict. |
|
|
2779
2896
|
| `maxTurns` | Keep the durable ceiling. | Fold. | 409 turn-ceiling conflict. |
|
|
2780
|
-
| Partial `
|
|
2897
|
+
| Partial `policy` | Keep the complete durable loop policy. | Fold. | 409 policy conflict. |
|
|
2781
2898
|
|
|
2782
2899
|
The conflict names both selections and directs the caller to cancel or conclude
|
|
2783
2900
|
the loop before changing configuration. A newly enqueued loop instead persists
|
|
@@ -2813,17 +2930,43 @@ creation. A client therefore cannot forge or resume an internal worker, insert
|
|
|
2813
2930
|
a non-mintable spelling, or make the client registry diverge from model worker
|
|
2814
2931
|
control.
|
|
2815
2932
|
|
|
2933
|
+
§capability-admission **One admission path owns external authority.** Core
|
|
2934
|
+
derives one or more `CapabilityDescriptor` demands from each routed statement,
|
|
2935
|
+
then evaluates the service, workspace, immutable worker-bound, mutable worker,
|
|
2936
|
+
and loop policy layers in that order. Every demand of a composed operation must
|
|
2937
|
+
survive before execution or proposal creation. A denial is an exact terse 403
|
|
2938
|
+
identifying the denied descriptor and owning policy scope; it never guesses the
|
|
2939
|
+
model's intent or recommends an alternate operation. COPY demands observation
|
|
2940
|
+
of its source and mutation of its destination; MOVE additionally demands
|
|
2941
|
+
mutation of its source; resource-backed EXEC demands its runtime plus source
|
|
2942
|
+
observation. Unknown schemes, runtimes,
|
|
2943
|
+
and tools continue to their ordinary resolver so capability policy cannot turn
|
|
2944
|
+
absence into a misleading restriction. The same resolver shapes generated
|
|
2945
|
+
resource examples, worker tool documents, and Turn0 surveys. PLAN, OPEN, FOLD,
|
|
2946
|
+
log KILL, and targetless SEND are log/program control rather than routed
|
|
2947
|
+
external demands and therefore remain outside capability selectors.
|
|
2948
|
+
|
|
2816
2949
|
§worker-settings **The worker carries its own behavioral rules.** The
|
|
2817
2950
|
workspace is the world — how things are; each worker is an actor inside it,
|
|
2818
2951
|
carrying the rules its loops obey. Those rules live in one JSON bag
|
|
2819
2952
|
(`workers.settings`), declared by the client at worker creation and mutable
|
|
2820
|
-
between loops through `
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2953
|
+
between loops through `readWorkerCapabilities`/`setWorkerCapabilities`; the
|
|
2954
|
+
public operation is specifically capability-shaped rather than exposing the
|
|
2955
|
+
internal persistence bag. Input is validated at the client boundary, and
|
|
2956
|
+
unknown keys never persist. A fork begins with a default empty mutable bag, but
|
|
2957
|
+
its immutable `capability_bound` captures the delegating actor's effective
|
|
2958
|
+
authority under {§worker-delegation-inherits-policy}. Service and workspace
|
|
2959
|
+
policies remain live ceilings; worker and loop policies may narrow but never
|
|
2960
|
+
widen them. Malformed persisted settings or bounds fail at their owning reader
|
|
2961
|
+
with the worker coordinate and cause rather than silently granting defaults.
|
|
2962
|
+
|
|
2963
|
+
§worker-capability-inspection **Client inspection uses the admission
|
|
2964
|
+
resolver.** The Worker capability actions return the contracts-owned
|
|
2965
|
+
`CapabilityProjection` {§capability-policy-projection}: service, workspace, immutable Worker bound, mutable
|
|
2966
|
+
Worker policy, and their normalized effective intersection. The projection is
|
|
2967
|
+
computed by the same resolver used by dispatch and packet shaping. Changing
|
|
2968
|
+
the mutable layer returns a fresh complete projection, so a client cannot
|
|
2969
|
+
mistake a requested widening for effective authority.
|
|
2827
2970
|
|
|
2828
2971
|
§question-tool **The native request-user-input tool.** Core registers one
|
|
2829
2972
|
in-process `question` runtime at boot. Its body is the MCP2 2026-07-28
|
|
@@ -2836,17 +2979,17 @@ client-interaction lifecycle — durable pause, reconnect discovery,
|
|
|
2836
2979
|
cancellation, and the answer-as-resolution all come from
|
|
2837
2980
|
{§client-interactions}; there is no loopback MCP and no proposal masquerade.
|
|
2838
2981
|
Effect `read`: the tool observes the human's answer and is never
|
|
2839
|
-
proposal-gated.
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
weights, and
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2982
|
+
proposal-gated. Its runtime declares the `interaction` trait, which the shared
|
|
2983
|
+
resolver projects as access class `interact`; any capability-policy layer may
|
|
2984
|
+
therefore admit or deny it without a question-specific switch.
|
|
2985
|
+
|
|
2986
|
+
§worker-tool-admission **Tool visibility and execution share admission.** The
|
|
2987
|
+
reserved tool tree's FIND/READ faces drop a runtime or tool document whenever
|
|
2988
|
+
the effective worker-level capability layers deny its descriptor, before
|
|
2989
|
+
matching and rendering, so counts, weights, and catalog text agree. Turn0
|
|
2990
|
+
applies its loop layer to the same catalog projection. Dispatch evaluates that
|
|
2991
|
+
same descriptor and policy cascade at the operation boundary, never at
|
|
2992
|
+
registration; there is no separate per-tool availability system.
|
|
2850
2993
|
|
|
2851
2994
|
§model-catalog **Model discovery is a bounded local projection, not provider
|
|
2852
2995
|
activity.** Core composes the release-pinned Models.dev snapshot with
|
|
@@ -2955,7 +3098,7 @@ active lifecycle behind. LOOK text anchors resolve through the same
|
|
|
2955
3098
|
|
|
2956
3099
|
| Event | Payload | When fired |
|
|
2957
3100
|
|--------------------------------------------------------------|---------|------------|
|
|
2958
|
-
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A `log_entries` row is committed. |
|
|
3101
|
+
| §notifications-log-entry-notify `log/entry` | `{ entry: LogEntry }` | A non-proposed `log_entries` row is committed, or a proposed row reaches terminal settlement under {§proposal-proposed-hidden}. Delivery completes before a later event may terminate the owning Loop. |
|
|
2959
3102
|
| §notifications-loop-terminated `loop/terminated` | `{ workerId, loopId, result, hitMaxTurns, turnIds, usage: { accounting, curationWeight, curationBudget, contextTokens, contextCapacity, meta }, attributions }` | One loop reaches a terminal state. `result` is the exact universal operation result, including its RFC 9457 Problem Details on failure. `accounting` is the loop's contracts-owned {§provider-accounting}; the two curation facts and two physical-context facts follow {§tokenomics-client-gauge}; `meta` is that turn's opaque provider bag. `attributions` is the sorted union of exact provider-request evidence ({§attribution}), separate from accounting. Worker and loop are an inseparable owning coordinate. |
|
|
2960
3103
|
| §notifications-loop-packet `loop/packet` | `{ workerId, loopId, packetCount }` | One provider packet becomes durable. `packetCount` is the exact count of packet-bearing turns in that Loop; packetless producer turns and physical provider retries never contribute. |
|
|
2961
3104
|
| §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}. |
|
|
@@ -2964,8 +3107,8 @@ active lifecycle behind. LOOK text anchors resolve through the same
|
|
|
2964
3107
|
| §notifications-workspace-branch-batch `workspace/branch-batch` | Branch-batch lifecycle payload | A branch batch enters queued, running, completed, failed, or recovery-required state. |
|
|
2965
3108
|
| §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. |
|
|
2966
3109
|
| §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. |
|
|
2967
|
-
| §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. |
|
|
2968
|
-
| §notifications-reasoning-event `reasoning/event` | `{ workerId, loopId, turnId, modelCallId, phase, delta? }` | A main emission call exposes readable reasoning.
|
|
3110
|
+
| §notifications-notice-event `notice/event` | `{ workerId, loopId, notice: Notice }` | A transient observation or progress notice occurs. `workerId` owns loop activity; only workspace derivation progress uses `null` with `loopId=0`. It cannot alter durable history, scheduling, recovery, or model-visible failure truth. |
|
|
3111
|
+
| §notifications-reasoning-event `reasoning/event` | `{ workerId, loopId, turnId, modelCallId, requestSequence, phase, delta? }` | A main emission call exposes readable reasoning. Each physical request that emits reasoning owns a distinct positive `requestSequence` and balanced start/content/end stream; opening a retry closes the preceding stream before any retry delta. Only content carries a nonempty exact delta. It is transient presentation evidence, never a log row, Notice, packet field, or BARE/child channel. The settled provider response remains the durable authority. |
|
|
2969
3112
|
|
|
2970
3113
|
§notifications-stream-event-failure-isolation The plugin-facing
|
|
2971
3114
|
`NotifyCaps.streamEvent()` remains a synchronous advisory call while core
|
|
@@ -3042,11 +3185,12 @@ Conditional absence never reorders the surviving default sections.
|
|
|
3042
3185
|
| 6 | user | `log` | Append-mostly model-visible history. |
|
|
3043
3186
|
| 7 | user | `child-streams` | Per-turn status; empty content is omitted. |
|
|
3044
3187
|
| 8 | user | `child-workers` | Per-turn status; empty content is omitted. |
|
|
3045
|
-
| 9 | user | `
|
|
3046
|
-
| 10 | user | `
|
|
3047
|
-
| 11 | user | `
|
|
3048
|
-
| 12 | user | `
|
|
3049
|
-
| 13 | user | `
|
|
3188
|
+
| 9 | user | `parent-worker` | The worker's parent by name; omitted for a root worker. |
|
|
3189
|
+
| 10 | user | `errors` | Per-turn failure pointers; empty content is omitted. |
|
|
3190
|
+
| 11 | user | `notices` | Per-turn observations; empty content is omitted. |
|
|
3191
|
+
| 12 | user | `git` | Per-turn workspace status; empty content is omitted. |
|
|
3192
|
+
| 13 | user | `budget` | `Context Token Budget`; omitted when capacity is unknown. |
|
|
3193
|
+
| 14 | user | `prompt` | Current prompt-entry pointers. |
|
|
3050
3194
|
|
|
3051
3195
|
The order favors prefix-cache locality where semantics permit: the definition
|
|
3052
3196
|
and privileged policy lead the resource directory, while the append-mostly
|
|
@@ -3102,7 +3246,8 @@ time of measurement.
|
|
|
3102
3246
|
- **Derivation is exhaustive and demand-led.** Explicit searchable-resource changes may start one coalesced warm. Passive creation and attachment do not. The first model turn starts or 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}
|
|
3103
3247
|
- §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.
|
|
3104
3248
|
- §tokenomics-agnostic-ruler **One model-agnostic curation ruler.** The daemon runs workers on different models in one workspace concurrently, while catalog and log accounting are workspace-wide. `contentWeight = ceil(chars/2)` therefore gives one content one stable number without per-model workspace state or recount passes. It controls curation only; every provider call independently measures the complete request as well as it can.
|
|
3105
|
-
- §tokenomics-neutral-telemetry **Curation telemetry is state, not response allowance.** The model-facing `Context Token Budget` section
|
|
3249
|
+
- §tokenomics-neutral-telemetry **Curation telemetry is state, not response allowance.** The model-facing `Context Token Budget` section begins with exactly two fields on separate lines: `tokensActiveTotal: N (P%)` and `tokensActiveMax: M`. It never presents their difference as free response tokens. The protocol definition directly requires FOLD, KILL, or trimming of irrelevant log items to keep the next packet within the maximum. Per-entry weights remain on log rows where they describe OPEN cost and FOLD savings. Generic packet composition and physical-token speculation are absent.
|
|
3250
|
+
- §tokenomics-pressure-inventory **Pressure identifies its reclaimable concentration.** When the ordinary two-field packet measurement reaches 80% of `tokensActiveMax`, the budget section may append `YOU MUST FOLD, KILL, or trim superseded, stale, or irrelevant log content.` followed by `Largest Log Items`: at most five currently OPEN, addressed log bodies, ordered by `tokensActive` descending and then `log:///` path. Each item repeats only that row's `tokensBody` and `tokensActive`. Folded and bodyless rows cannot enter the list because FOLD would reclaim no body from them. The largest prefix that fits may be shown; this conditional block never pushes an otherwise admissible packet over its maximum. Its own weight participates in the final fixed-point `tokensActiveTotal`.
|
|
3106
3251
|
- §tokenomics-content-hash-identity **Content identity, not per-tokenizer counts.** Static channel writes stamp `content_hash` (SHA-256) as stable content identity. `weight` is stored beside that content and is never keyed or recomputed by model.
|
|
3107
3252
|
- §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` sums every USD-expressible request and is `null` only when no request is expressible; a response-less failure or an uncataloged model is skipped, never allowed to erase the expressible evidence. The derived aggregate usage sums every reported quantity the same way. 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.
|
|
3108
3253
|
- §tokenomics-negative-pressure **Negative curation pressure is honest but never submitted.** The provisional readout may report `tokensActiveTotal` and its percentage above `tokensActiveMax`. Crossing the maximum diverts that would-be model turn into {§overflow-turn}; no over-ceiling packet reaches `provider.generate`. Automatic recovery does not create a strike or consume a model-turn allowance.
|
|
@@ -3114,16 +3259,13 @@ not participate in this disk loop.
|
|
|
3114
3259
|
|
|
3115
3260
|
```mermaid
|
|
3116
3261
|
flowchart LR
|
|
3117
|
-
git["Git tracked
|
|
3118
|
-
|
|
3119
|
-
|
|
3262
|
+
git["Git tracked"] --> resolve["Resolve workspace membership"]
|
|
3263
|
+
include["include"] --> resolve
|
|
3264
|
+
exclude["exclude"] -->|subtract| resolve
|
|
3120
3265
|
resolve --> materialize["Pre-turn materialize<br/>disk → file snapshot"]
|
|
3121
3266
|
materialize --> read["READ snapshot"]
|
|
3122
3267
|
materialize --> edit["EDIT against snapshot"]
|
|
3123
|
-
edit -->
|
|
3124
|
-
view["view"] -->|marks member read-only| gate
|
|
3125
|
-
gate -->|yes| refused["403; no proposal"]
|
|
3126
|
-
gate -->|no| proposal["Proposal"]
|
|
3268
|
+
edit --> proposal["Proposal"]
|
|
3127
3269
|
proposal -->|"client accepts or loop auto"| cas["synced_sig compare-and-swap"]
|
|
3128
3270
|
cas -->|"file snapshot → disk"| project["Project file"]
|
|
3129
3271
|
project --> materialize
|
|
@@ -3132,11 +3274,11 @@ flowchart LR
|
|
|
3132
3274
|
| Concern | Owner and representation |
|
|
3133
3275
|
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3134
3276
|
| Workspace identity | `workspaces.project_root`; null is headless. There is no separate project entity. |
|
|
3135
|
-
| File visibility | Workspace-tier resolved membership: `(
|
|
3277
|
+
| File visibility | Workspace-tier resolved membership: `(tracked files ∪ include) − exclude` ({§membership-baseline}). Every worker sees the same result. |
|
|
3136
3278
|
| File reads | READ returns the materialized file snapshot stored in the entry body channel; it does not read disk directly. |
|
|
3137
3279
|
| File writes | EDIT proposes against that snapshot. Only accepted resolution with the captured `synced_sig` writes the project file. |
|
|
3138
3280
|
| Internal entries | Workspace or worker entries are canonical store state. Writing one never implies a project-file write. |
|
|
3139
|
-
| Authority | Service flags set the membership ceiling;
|
|
3281
|
+
| Authority | Service flags set the membership ceiling; the `members` family's definitions include and exclude within it; client or loop auto resolves proposals. `origin` is attribution. |
|
|
3140
3282
|
|
|
3141
3283
|
§web-search-retrieval **Web discovery is an ordinary MCP concern; retrieval is a first-class composition.** PLURNK owns no search runtime: a search-capable MCP server (e.g. Brave Search) participates through the ordinary MCP contract — admission, read-effect classification, tool documentation, and packet projection are identical to every other MCP tool ({§mcp-tool-presentation}). An executor that wants to materialize discovered pages uses the generic `content: null` `entry()` request ({§exec-entry-sink}): the guarded `WebFetcher` sink fetches candidates in parallel, off the write-serialization chain, and materializes successful bodies as ordinary HTTP entries. Every candidate whose `entry()` call rejects, regardless of failure reason, is mechanically omitted from the model-facing result directory; survivors retain upstream order. Without an entry sink the executor cannot test materialization and omits the verdict.
|
|
3142
3284
|
|
|
@@ -3155,31 +3297,58 @@ and never re-fetch a match.
|
|
|
3155
3297
|
|
|
3156
3298
|
**Git is the substrate and the repository is the boundary:**
|
|
3157
3299
|
|
|
3300
|
+
- §membership-baseline **The baseline contract — chiseled (#400).** To a workspace a
|
|
3301
|
+
project file is exactly one of three things: **invisible**, **added**, or **tracked
|
|
3302
|
+
by git**. There is no fourth category. Membership — what the model can READ and
|
|
3303
|
+
FIND, what is materialized into the store, what a packet can ship to a provider — is
|
|
3304
|
+
the allowlist `(tracked ∪ include) − exclude` and nothing else. No file is a member because
|
|
3305
|
+
it exists on disk, because git does not ignore it, or because a model would find it
|
|
3306
|
+
convenient: ambient admission of untracked files is prohibited, so a workspace rooted
|
|
3307
|
+
in a home directory or a monorepo exposes exactly what was committed or added (the
|
|
3308
|
+
non-member rule, {§fs-write-nonmember}: no read, no leak, no overwrite). Every
|
|
3309
|
+
exception is a named clause in the register ({§membership-model-universe}), admits
|
|
3310
|
+
files by an exact creation record with recorded provenance, and never by `git add`.
|
|
3311
|
+
Changing this clause, the register, or the composition is an operator ruling recorded
|
|
3312
|
+
on the issue that lands it — never an implementation convenience, never a side effect
|
|
3313
|
+
of making a file visible to solve the problem at hand. The 2026-07-12 – 2026-08-27
|
|
3314
|
+
"untracked-but-not-ignored" ambient admission is retired.
|
|
3315
|
+
- §membership-model-universe **The exception register — files in the model's universe.**
|
|
3316
|
+
Admitted by exact creation records (`source: "create"`, origin `constraint`), never
|
|
3317
|
+
staged: (1) a file an accepted EDIT creates; (2) a COPY/MOVE destination
|
|
3318
|
+
({§membership-create-parents}). Admitted by a published standard as projected
|
|
3319
|
+
instruction documents — never as members: (3) the project's `AGENTS.md` and nested
|
|
3320
|
+
`AGENTS.md` files ({§turn0-agents-stunt}, #346), read from disk regardless of git status
|
|
3321
|
+
and materialized as `worker://~/_plurnk/agents.md` and
|
|
3322
|
+
`worker://~/_plurnk/instructions/<subtree>/AGENTS.md`; the file itself is a member
|
|
3323
|
+
only when tracked or added, and the standard never overrides the operator's
|
|
3324
|
+
exclusions — an `AGENTS.md` the repository ignores or an exclusion matches
|
|
3325
|
+
is not projected. (4) A definition the model proposes through the `members`
|
|
3326
|
+
family ({§members-functionality}), admitted only under the operator's ceiling
|
|
3327
|
+
`PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` (shipped `none`), projected with source
|
|
3328
|
+
`model`, and never admitted past the repository's ignore rules or an exclusion.
|
|
3329
|
+
Nothing else.
|
|
3158
3330
|
- §membership-git-membership The workspace owns the Git repository containing
|
|
3159
3331
|
`project_root`. Its tracked files (`git ls-files` semantics) are members with
|
|
3160
3332
|
no explicit overlay; when the root is a package inside a monorepo, the
|
|
3161
3333
|
repository's other packages are members at root-relative paths. An unrelated
|
|
3162
3334
|
or nested independent repository is not discovered or managed by this
|
|
3163
|
-
workspace. When Git is absent there is no filesystem walk;
|
|
3164
|
-
sole source.
|
|
3335
|
+
workspace. When Git is absent there is no filesystem walk; member definitions are
|
|
3336
|
+
then the sole source.
|
|
3165
3337
|
- §git-native-default **Core Git reads use native Git.** Membership and status
|
|
3166
3338
|
execute the installed Git binary. An absent or failed binary yields no
|
|
3167
3339
|
automatic Git membership or status; core has no alternate implementation or
|
|
3168
|
-
fallback.
|
|
3169
|
-
model-invoked subset for shellless deployments, not an ambient Git backend.
|
|
3340
|
+
fallback.
|
|
3170
3341
|
- §membership-git-hermetic Native Git runs with ambient `GIT_*` and
|
|
3171
3342
|
global/system config scrubbed, so repository identity follows `project_root`,
|
|
3172
3343
|
never the daemon's launch environment.
|
|
3173
3344
|
- §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** crosses the creation matrix in {§fs-write-surface}; proposal acceptance cannot bypass its scope, exclusion, or incorporation rules. Reaching past membership is `## EXEC0 [sh]`'s job, not the file scheme's.
|
|
3174
3345
|
- §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.
|
|
3175
3346
|
|
|
3176
|
-
**The overlay — `
|
|
3347
|
+
**The overlay — `include | exclude`.** `workspace_constraints` holds the `members` family's projected definitions and the engine's creation records ({§members-projection}). Resolved membership is `(project repository files ∪ include) − exclude`.
|
|
3177
3348
|
|
|
3178
|
-
- §membership-auto-add **Auto-add** — the project repository's ambient membership is its tracked `ls-files`
|
|
3179
|
-
- §membership-overlay-
|
|
3180
|
-
- §membership-overlay-
|
|
3181
|
-
- §membership-overlay-view **`view`** — keep a member readable but refuse `File.edit`, 403'd at the membership check before any diff. (Admitting an untracked file as `view` rides on `pick`'s scan.)
|
|
3182
|
-
- §membership-resolved-effects **Resolved effect is a read, not a re-derivation.** `workspace.members` surfaces each candidate's resolved effect — `(ls-files ∪ pick) − hide` tagged `member` / `view`, plus the `hide`-excluded `hidden` set — so a client signs file visibility (member / read-only / ignored) without reimplementing the overlay glob-matching. The daemon owns git + the globs; the per-file effect is its to resolve, the client's to render.
|
|
3349
|
+
- §membership-auto-add **Auto-add** — the project repository's ambient membership is its tracked `ls-files`, with `git` origin; an untracked file is never an ambient member ({§membership-baseline}). An accepted creation is incorporated by an exact creation record, never by `git add` ({§membership-model-universe}); a record that cannot be written fails the creation transaction, never an orphan ({§file-create-no-orphans}).
|
|
3350
|
+
- §membership-overlay-include **`include`** — admit a file Git misses through a targeted pattern scan (files only), with `constraint` origin. `source: "members"` is a projected human definition, `source: "model"` a projected model definition, `source: "create"` the exact durable record of an accepted creation. Only `members` inclusions override active Git ignore. In a Git-absent root, inclusions are the sole file-membership source.
|
|
3351
|
+
- §membership-overlay-exclude **`exclude`** — a `!glob` definition removes a tracked or included file: resolution drops matches (`node:path.matchesGlob`) and reconciles so the entry set *equals* the member set. The lever to exclude a committed-but-oversized or sensitive tracked file; exclusions mask creation records without deleting their provenance ({§fs-create-masked}).
|
|
3183
3352
|
|
|
3184
3353
|
**File ops act on the entry, not the disk; the two reconcile only at gates.** A `file:///` member is a row whose body channel holds its *materialized model-readable snapshot*. READ returns that channel; EDIT diffs against editable text snapshots — neither reaches the filesystem directly. Entry and disk reconcile at exactly two gates: the **pre-turn materialize** (disk → entry, below) and the **accept-time write-back** (entry → disk, {§proposal}). Between the gates the entry is the truth the model curates against, and `synced_sig` — the member's last-synced disk stat (`mtime:size`) — is the version token both gates compare on.
|
|
3185
3354
|
|
|
@@ -3194,6 +3363,20 @@ identity, and terminal disposition without exposing raw bytes or a base64 lane.
|
|
|
3194
3363
|
| Binary with readable projection | Derived Unicode as `text/markdown` | READ uses the projection; source-aware EDIT remains 415. |
|
|
3195
3364
|
| Binary without projection/over cap | Empty marker under the source binary mimetype | READ and EDIT return 415; private metadata distinguishes unavailable from limit. |
|
|
3196
3365
|
|
|
3366
|
+
§membership-materialization-limit **A pathological member degrades, never the
|
|
3367
|
+
workspace.** `PLURNK_SERVICE_FILE_MATERIALIZE_MAX_BYTES` is a required positive
|
|
3368
|
+
byte ceiling over one disk source before Core reads it into the canonical file
|
|
3369
|
+
snapshot. Its valid range is `1..104857600`, bounded by the channel storage
|
|
3370
|
+
contract, and it ships at that 100 MiB maximum. An oversized path remains a real member
|
|
3371
|
+
with an empty body channel carrying a durable 413 producer result; no diagnostic
|
|
3372
|
+
sentinel impersonates file content. READ therefore names the path, observed bytes,
|
|
3373
|
+
ceiling, and recovery through the ordinary result contract, while EDIT returns the
|
|
3374
|
+
same 413 instead of diffing against a fictitious empty baseline. Core records the
|
|
3375
|
+
materialization disposition and ceiling privately, so an unchanged disk member is
|
|
3376
|
+
reconsidered when the operator changes the policy and otherwise remains a stat-only
|
|
3377
|
+
no-op. The file write gate independently stats the source against the same ceiling,
|
|
3378
|
+
so safety does not depend on a background warm winning a client-operation race.
|
|
3379
|
+
|
|
3197
3380
|
§derivation-dedup-parallel **The index dedups then parallelizes.** The derivation identity hashes the exact READ channel representation, mimetype, reader behavior, embedding configuration, and applicable search exclusion. A channel or log projection attaches the immutable artifact only after it is complete; identical projections 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 representation completed by a successful pass attaches a terminal classified artifact, identically at concurrency 1 and N. A changed pass emits one immediate `preparing` state, intermediate `indexing` heartbeats at `PLURNK_SERVICE_DERIVE_PROGRESS_HEARTBEAT_MS`, and one immediate `complete` or `failed` state. A no-op pass emits no lifecycle, an indexing heartbeat never claims 100%, and the model-facing Notice buffer retains only the current derivation state while live clients observe each heartbeat.
|
|
3198
3381
|
|
|
3199
3382
|
The artifact also retains a positive `{§mimetype-parse-issues}` count and the
|
|
@@ -3219,9 +3402,9 @@ Notice once per identical observation in a maintenance pass.
|
|
|
3219
3402
|
|
|
3220
3403
|
Lossless chunk admission requires either the embedder's own counter or an exact fallback tokenizer. An empirical estimate never proves that content fits the declared token window. When pending readable content would require vectors and only an estimate is available, maintenance surfaces its degradation Notice and fails before embedding or attaching a derivation; no/disabled embedding and the established empty, binary, excluded, and maximum-size dispositions remain non-vector outcomes.
|
|
3221
3404
|
|
|
3222
|
-
§semantic-max-embed-size **Embedding has
|
|
3405
|
+
§semantic-max-embed-size **Embedding has a bounded size posture.** `PLURNK_SERVICE_MAX_EMBED_SIZE` is the maximum UTF-8 byte size eligible for vectors; the shipped default is 262144 and `0` is the explicit unlimited override. The measured value is the exact addressed channel representation READ exposes and the embedder receives. When the ceiling rejects an oversized representation, its channel 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 channels. Client notices report compact aggregate progress; the digest records every non-vector address, terminal disposition, and reason for forensic inspection.
|
|
3223
3406
|
|
|
3224
|
-
§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}.
|
|
3407
|
+
§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 and materialization policy 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} and {§membership-materialization-limit}.
|
|
3225
3408
|
|
|
3226
3409
|
§membership-emi-divergence-signal **EMI divergence evidence.** The detector that gates the work *is* the one that records this — one mechanism, not a second full read. When change detection finds a member moved out-of-band, the runtime actor records an `EDIT`-shaped row naming the file with `source="file"`; it does not broadcast that workspace change into unrelated workers' logs ({§env-delta-filesystem-narration}). 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. The current file remains ordinarily addressable. A stale anchored edit rejects under {§line-anchors}; a disk race after proposal rejects under {§membership-edit-write-cas}.
|
|
3227
3410
|
|
|
@@ -3231,7 +3414,7 @@ The version travels *with the proposal*, never re-read from the entry at accept:
|
|
|
3231
3414
|
|
|
3232
3415
|
The CAS is the **hard backstop**, at the moment of writing, on every accept path. It composes with the model-facing {§line-anchors}: an anchor rejects a target whose relevant neighborhood changed before dispatch, while the CAS refuses to write against a snapshot disk left after proposal. An unanchored edit deliberately claims no pre-dispatch stale-view guarantee.
|
|
3233
3416
|
|
|
3234
|
-
§membership-git-flags **Permission flags.** Service-wide Git admission comes from {§operator-config-git-ceiling}. `PLURNK_SERVICE_GIT_AUTO=1` (default) includes the repository containing `project_root`; `=0` disables automatic Git membership, leaving
|
|
3417
|
+
§membership-git-flags **Permission flags.** Service-wide Git admission comes from {§operator-config-git-ceiling}. `PLURNK_SERVICE_GIT_AUTO=1` (default) includes the repository containing `project_root`; `=0` disables automatic Git membership, leaving member definitions as the only membership source. `ALLOWED` gates `AUTO`.
|
|
3235
3418
|
|
|
3236
3419
|
**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.
|
|
3237
3420
|
|
|
@@ -3275,7 +3458,7 @@ becomes a packetless `_plurnk` turn. Packetless initialization and recovery turn
|
|
|
3275
3458
|
remain ordinary turn chronology but do not consume `maxTurns`, model-call,
|
|
3276
3459
|
emission-attempt, usage, or cost accounting.
|
|
3277
3460
|
|
|
3278
|
-
- §overflow-turn-script **Recovery is one ordinary admitted `_plurnk` program.** Its canonical {§plan-value} has one `medium`, `in_progress` entry whose content is `Automatically FOLD log bodies newly active at token-budget overflow.`, followed by every causal whole-body FOLD and terminal `SEND0 [102]` with body `Next: YOU MUST ONLY FOLD, KILL, or trim ALL superseded, stale, or irrelevant log content in bulk
|
|
3461
|
+
- §overflow-turn-script **Recovery is one ordinary admitted `_plurnk` program.** Its canonical {§plan-value} has one `medium`, `in_progress` entry whose content is `Automatically FOLD log bodies newly active at token-budget overflow.`, followed by every causal whole-body FOLD and terminal `SEND0 [102]` with body `Next: YOU MUST ONLY FOLD, KILL, or trim ALL superseded, stale, or irrelevant log content in bulk.` The final sentence requires the successor's substantive operations to be one dedicated, comprehensive bulk-curation program. Core authors this internal program with canonical PLAN and SEND framing. Its exact `turnOps` is born FOLDED; successful FOLD rows follow {§fold-open-meta-operations} and therefore remain durable but packet-suppressed. Every recovery row carries `_plurnk` and `overflow`; no model call, synthetic receipt, or parallel explanation exists.
|
|
3279
3462
|
- §overflow-turn-curation **The preceding turn owns the pressure it introduced.** Core deterministically selects every body already created in the packetless candidate turn, every body created by the immediately preceding completed turn in that worker's chronology, and every older body whose visibility that preceding turn's successful OPEN increased. Every selected body is FOLDed whole (`<1,-1>`) through ordinary dispatch. Already-wholly-folded and bodyless rows require no operation. Core performs no relevance judgment, exempts no operation or resource kind, reconstructs no interval delta, re-runs no authored selector, and chooses no unrelated older history.
|
|
3280
3463
|
- §overflow-turn-hard-413 **Recovery fails hard when the causal fold cannot fit.** After the ordinary FOLDs land, Core rebuilds and remeasures once. If the plan changes no visibility or the rebuilt request still exceeds the ceiling, the loop terminalizes with an exact `engine/context/token-budget-overflow` 413 Problem; Core neither submits excess bytes nor chooses unrelated older history. Separately, every `provider.generate` assesses physical capacity under {§provider-surface-capacity}. Core may retry a provider capacity rejection only after withholding automatic prompt-body projection when that changes the request. If it cannot produce changed bytes or the changed request is still rejected, the request-only model turn and provider-owned Problem terminalize at **413 Content Too Large**.
|
|
3281
3464
|
|
|
@@ -3353,7 +3536,7 @@ ordinary operation evidence still reaches that child's direct parent.
|
|
|
3353
3536
|
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3354
3537
|
| `worker_id` | The worker whose self-contained log owns the materialized row. |
|
|
3355
3538
|
| `origin` | The actor tier that wrote the row; a materialized delta is `_plurnk`. |
|
|
3356
|
-
| `source` | The immediate causal identity in this log
|
|
3539
|
+
| `source` | The immediate causal identity in this log: a lineage or commons observation uses canonical `worker://<producer>`, a terminal stream observation uses its causal `log:///<coord>/EXEC`, and a subsystem observation may use its stable token (for example `file`). Self-authored rows omit it. |
|
|
3357
3540
|
|
|
3358
3541
|
§env-delta-no-coalescing **Activity is never coalesced.** Each admitted child
|
|
3359
3542
|
operation and each commons mutation has one occurrence identity. Combining
|
|
@@ -3401,7 +3584,7 @@ the aggregate remains dispatch coordination state.
|
|
|
3401
3584
|
| -------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
3402
3585
|
| 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. |
|
|
3403
3586
|
| `unit`, `before`, `after` | `extent` | Whole-line batches use line counts. A batch containing any exact four-coordinate edit uses Unicode code-point counts. |
|
|
3404
|
-
| `parseIssues`
|
|
3587
|
+
| `parseIssues.before`, `parseIssues.after` | `parseIssues` as `before→after` | Parser-recovery counts for complete source and landed revisions; omitted when both are clean or either is unavailable. |
|
|
3405
3588
|
| `effect.requested`, `source`, `result` | `range` | The admitted marker and its normalized mapping from the common source snapshot into the landed body. |
|
|
3406
3589
|
| `effect.removed`, `inserted` | `change` | Removed and inserted counts in the receipt unit. |
|
|
3407
3590
|
| `effect.context` | Canonical row body | Numbered physical lines at each landed boundary, bounded symmetrically by `PLURNK_SERVICE_EDIT_RECEIPT_CONTEXT_LINES`. |
|
|
@@ -3410,7 +3593,7 @@ the aggregate remains dispatch coordination state.
|
|
|
3410
3593
|
|
|
3411
3594
|
§edit-result-receipt-truth **Receipts describe committed state.** Every row in
|
|
3412
3595
|
one resource-channel EDIT batch carries the same landed revision, extent, and
|
|
3413
|
-
optional
|
|
3596
|
+
optional `parseIssues` transition for the complete source and landed revisions.
|
|
3414
3597
|
When the proposed batch lands unchanged, each row also carries its own requested
|
|
3415
3598
|
marker, source/result mapping, counts, and context. For configured count `C`,
|
|
3416
3599
|
the context contains up to `C` surrounding lines and the first and last `C`
|
|
@@ -3467,7 +3650,7 @@ inspection is advisory and occurs against complete resulting text after
|
|
|
3467
3650
|
successful application. A handler or parser failure emits a Notice, omits
|
|
3468
3651
|
`parseIssues`, and never changes the mutation outcome.
|
|
3469
3652
|
|
|
3470
|
-
### §proposal-ownership Loop
|
|
3653
|
+
### §proposal-ownership Loop disposition and client YOLO
|
|
3471
3654
|
|
|
3472
3655
|
Side-effecting operations propose ({§exec}) and pause dispatch at 202 for an
|
|
3473
3656
|
authority decision ({§engine-rails}, {§methods}). Automatic acceptance has two
|
|
@@ -3475,14 +3658,14 @@ distinct owners:
|
|
|
3475
3658
|
|
|
3476
3659
|
| Mechanism | Authority path | Intended use |
|
|
3477
3660
|
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
|
|
3478
|
-
| §proposal-ownership-loop-auto **Loop
|
|
3479
|
-
| **Client-side YOLO** (`--yolo` / `PLURNK_YOLO`) |
|
|
3661
|
+
| §proposal-ownership-loop-auto **Loop disposition** | `runLoop({ policy: { proposals: "accept" } })` persists a loop-owned disposition; core resolves proposals in process without a client. | Headless automation, benchmarks, CI, fixtures, and unattended use. |
|
|
3662
|
+
| **Client-side YOLO** (`--yolo` / `PLURNK_YOLO`) | A `proposals: "review"` loop emits the ordinary `loop/proposal`; the client returns an accepted proposal through its standard resolution path. | Interactive automatic review. |
|
|
3480
3663
|
|
|
3481
3664
|
Core cannot distinguish client-side YOLO from a fast human acceptance and does
|
|
3482
3665
|
not need to. Loop auto keeps authority inside the loop; client-side YOLO acts
|
|
3483
3666
|
only after authority crosses the client boundary.
|
|
3484
3667
|
|
|
3485
|
-
§proposal-ownership-notification **The notification carries disposition, not policy inputs.** `loop/proposal` carries the core-owned `ProposalDisposition` ({§notifications}, {§proposal-disposition}). A connected client presents only `owner="client"`; it never reimplements
|
|
3668
|
+
§proposal-ownership-notification **The notification carries disposition, not policy inputs.** `loop/proposal` carries the core-owned `ProposalDisposition` ({§notifications}, {§proposal-disposition}). A connected client presents only `owner="client"`; it never reimplements policy from operation or attrs.
|
|
3486
3669
|
|
|
3487
3670
|
---
|
|
3488
3671
|
|
|
@@ -3520,9 +3703,10 @@ source independently from this optional model-exchange record; a request-only
|
|
|
3520
3703
|
turn receives a note instead of a fabricated response.
|
|
3521
3704
|
|
|
3522
3705
|
§digest-turn-artifact-identity **Digest packet artifacts project durable turns.**
|
|
3523
|
-
After selectors are applied, digest retains every turn with exact `turnOps
|
|
3524
|
-
|
|
3525
|
-
them contiguously from `packet000`. The
|
|
3706
|
+
After selectors are applied, digest retains every turn with exact `turnOps`, a
|
|
3707
|
+
valid stored provider request, or malformed stored packet evidence; orders those
|
|
3708
|
+
turns by durable chronology; and names them contiguously from `packet000`. The
|
|
3709
|
+
producer does not affect projection.
|
|
3526
3710
|
|
|
3527
3711
|
| Artifact | Present when | Authority |
|
|
3528
3712
|
|----------|--------------|-----------|
|
|
@@ -3530,6 +3714,8 @@ them contiguously from `packet000`. The producer does not affect projection.
|
|
|
3530
3714
|
| `packetNNN.system.md`, `packetNNN.user.md` | The turn stored a provider request | Stored packet sections projected through `PacketWire` |
|
|
3531
3715
|
| `packetNNN.assistantRaw.json` | The request has an admitted provider response | Stored opaque provider response |
|
|
3532
3716
|
| `packetNNN.response.md`, attempt artifacts | The request received no admitted response | Stored request and attempt state |
|
|
3717
|
+
| `packetNNN.packet.raw.txt` | The stored packet fails typed validation | Exact stored packet text |
|
|
3718
|
+
| `packetNNN.packet.invalid.json` | The stored packet fails typed validation | Turn identity and complete validation error chain |
|
|
3533
3719
|
|
|
3534
3720
|
A source-backed turn without provider participation therefore produces only
|
|
3535
3721
|
`assistant.md`; a request-only turn produces no fabricated assistant. A
|
|
@@ -3633,7 +3819,9 @@ retain distinct contracts and lifetimes.
|
|
|
3633
3819
|
- §log-row-self-explains **Every ≥400 pointer names a record that states its
|
|
3634
3820
|
why.** A model-operation failure is the model's own operation result; its
|
|
3635
3821
|
Problem Details `instance` is that row's `log:///` URI and packet wire renders
|
|
3636
|
-
the
|
|
3822
|
+
the contracts-owned compact `{§problem-projection}` on its meta line whether
|
|
3823
|
+
folded or open. The enclosing row owns status, model-facing path, source, and target;
|
|
3824
|
+
an identical extension is not repeated inside the projection. No
|
|
3637
3825
|
separate item is minted for operation failures. Actionless engine rails mint
|
|
3638
3826
|
`op='error'` items because no authored operation row exists. Invalid provider
|
|
3639
3827
|
emissions are outside this channel because they are not turns. A bare
|
|
@@ -3642,9 +3830,9 @@ retain distinct contracts and lifetimes.
|
|
|
3642
3830
|
engine-internal faults crash and never mint model-facing rows.
|
|
3643
3831
|
- **Asynchronous work does not weaken the contract.** A stream-producing operation returns its initial `102` after acquisition. At conclusion the subscription stores the exact universal terminal result; `stream/concluded` carries it unchanged; the next ambient terminal READ merges it with the stream payload and assigns the committed `log:///.../READ` Problem instance. Timeouts and service cancellations replace the complete terminal result with a new valid 504/499 Problem—they never mutate a status while retaining a contradictory Problem.
|
|
3644
3832
|
- **Self-explaining rows.** A problem `title` names the stable class and `detail` states the occurrence-specific cause. Producer-known operands belong in factual extensions. `stage` appears only when neighboring stages imply different recovery; `recovery` states one generally valid next action; `retryable` is true only when the producer recommends automatically retrying the identical request. Unknown recovery or retryability is omitted rather than guessed. General workflow teaching stays in the packet rather than being duplicated into every failure. The runtime-neutral writing contract is owned by `@plurnk/plurnk-contracts`.
|
|
3645
|
-
- **Exact Problems cross
|
|
3833
|
+
- **Exact Problems cross durable and external boundaries.** Scheme capabilities, proposal application, subscription conclusion, loop settlement, AG-UI, clients, digests, and benchmark records preserve the originating Problem object. The model packet alone derives `{§problem-projection}` without mutating that object. An adapter may add the durable `instance`; it must not rebuild failure truth from `status`, `detail`, `RUN_ERROR`, a scheduler projection, or a legacy string. A failed boundary without a valid Problem is a contract violation and fails hard.
|
|
3646
3834
|
- **Caught diagnostics are bounded.** Core-owned Problems may include a bounded preview of a caught runtime diagnostic when it states the occurrence-specific cause. `PLURNK_SERVICE_ERROR_DETAIL_LIMIT` owns that model-facing character bound; complete errors remain in daemon diagnostics. Input validation and stable contract failures do not spend this allowance on implementation text.
|
|
3647
|
-
- §notice-drain-on-read **Notices** - the few observations that are not log rows render one terse line under their distinct `## Notices` section, never a JSON dump. Packet rendering normalizes whitespace, bounds the producer message with the shared preview limits, and appends any typed position. The notice buffer drains on read;
|
|
3835
|
+
- §notice-drain-on-read **Notices** - the few observations that are not log rows render one terse line under their distinct `## Notices` section, never a JSON dump. Packet rendering normalizes whitespace, bounds the producer message with the shared preview limits, and appends any typed position. The notice buffer drains on read; event Notices appear on at most one packet. Stateful derivation progress and provider availability coalesce in the buffer, so clients observe every checkpoint live while a later model packet receives only the current state under ordinary level filtering.
|
|
3648
3836
|
- §rail-accounting-private **Rail accounting is private.** Visibility is owned by {§engine-rails}: the model sees concrete failures from admitted turns, never rejected emissions, attempt counts, the strike streak, or cycle detection. Surfacing internal state creates a gamification surface where the model optimizes for engine metrics instead of the task.
|
|
3649
3837
|
|
|
3650
3838
|
**The error rows (one channel) + the only non-log notices:**
|
|
@@ -3666,7 +3854,7 @@ retain distinct contracts and lifetimes.
|
|
|
3666
3854
|
|
|
3667
3855
|
§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.
|
|
3668
3856
|
|
|
3669
|
-
§notice-event-notify **Client surface.** Engine Notices broadcast live via the `notice/event`
|
|
3857
|
+
§notice-event-notify **Client surface.** Engine Notices broadcast live via the `notice/event` notification — `{ workerId, loopId, notice: { source, kind, level, message?, position?, …kind-specific } }` per the grammar's `Notice` schema — the moment they land. A loop Notice names its owning Worker; workspace derivation progress alone carries `workerId=null, loopId=0`. 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.
|
|
3670
3858
|
|
|
3671
3859
|
§digest-programmatic-surface **The digest is an importable forensic surface.**
|
|
3672
3860
|
|
|
@@ -3678,7 +3866,7 @@ retain distinct contracts and lifetimes.
|
|
|
3678
3866
|
| `workerId` | Narrows workers and every dependent loop, turn, logical model call, emission attempt, physical request, and log row to that one worker. |
|
|
3679
3867
|
| `workspaceId` | Narrows workers and dependent evidence to one workspace; when both selectors are present they intersect. |
|
|
3680
3868
|
|
|
3681
|
-
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log
|
|
3869
|
+
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log event with its initial and current projection, causal `source`, tags, and structured `attrs`; every exact OPEN/FOLD/log-KILL target effect; the exact Problem on every failed row; each loop's exact terminal result; and every ordered physical provider request. KILLed `turnOps` still produce their chronological `assistant.md` artifacts because curation cannot rewrite what a producer submitted. Each stored packet validates independently: one malformed historical packet remains exact raw evidence with its complete validation error chain and never prevents healthy turns from being projected. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. The reasoning chronology distinguishes readable reasoning content from provider-reported reasoning usage: when tokens were reported but no readable content was returned, it states both facts instead of implying that no reasoning occurred. 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 durable resource coordinates 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; valid packet files remain byte-identical records of what the model saw.
|
|
3682
3870
|
|
|
3683
3871
|
§digest-requiem **A requiem is an out-of-band forensic interview, not a worker
|
|
3684
3872
|
turn.** It cannot execute operations or alter the audited history.
|
|
@@ -3701,7 +3889,7 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
3701
3889
|
§tools-resource-discovery **Executable capability discovery uses ordinary
|
|
3702
3890
|
Plurnk resources.** No generated tool table rides the system packet. Every
|
|
3703
3891
|
runtime enabled for the current worker with an admitted invocation materializes exactly one
|
|
3704
|
-
family document at `worker://~/_plurnk/
|
|
3892
|
+
family document at `worker://~/_plurnk/plurnk/<runtime>.md`. A general runtime's
|
|
3705
3893
|
document contains its {§executor-tool-document}; a runtime with an exact
|
|
3706
3894
|
{§executor-tool-registry} materializes the same single document — per-target
|
|
3707
3895
|
child documents do not exist, shown or stored. The family document summarizes
|
|
@@ -3731,7 +3919,7 @@ enabled executable; executor enablement is the sole user-configured filter
|
|
|
3731
3919
|
shared by discovery and dispatch. A runtime declaration may carry
|
|
3732
3920
|
`resourcesPath` — its generated-doc root relative to the worker's generated
|
|
3733
3921
|
subtree ({§worker-generated-subtree}). Absent, its docs live in the internal
|
|
3734
|
-
`_plurnk/
|
|
3922
|
+
`_plurnk/plurnk` namespace; present (attached MCP families: `/tools`),
|
|
3735
3923
|
the family document materializes at `_plurnk` + that root in the
|
|
3736
3924
|
worker's private entry space. Turn 0 surveys the families (`## FIND0 [+init,+tools]
|
|
3737
3925
|
(worker://~/_plurnk/tools/*.md)`, one row per
|
|
@@ -3744,6 +3932,57 @@ unasked.
|
|
|
3744
3932
|
Attached tools are capabilities like every other runtime; the model never
|
|
3745
3933
|
learns an origin.
|
|
3746
3934
|
|
|
3935
|
+
§members-functionality **File membership is one Worker Functionality family.**
|
|
3936
|
+
Core registers the `members` family with the coordinator ({§functionality-coordinator}):
|
|
3937
|
+
the model, the client, and the operator learn one surface — `list | discover | add |
|
|
3938
|
+
enable | disable | remove`, `worker.members.<verb>` for the client, `## EXEC0 [members]
|
|
3939
|
+
(<verb>)` for the model — for what the model may see, exactly as they do for skills and
|
|
3940
|
+
MCP servers. A definition is one gitignore-style glob, `{ glob }`, relative to the project
|
|
3941
|
+
root; a leading `!` excludes matching members, and an exclusion wins over every inclusion.
|
|
3942
|
+
The coordinator's provenance (`service-configuration`, `client-action`, `model-proposal`)
|
|
3943
|
+
rides the definition; its alias is a short name, suggested from the glob. `list` shows each
|
|
3944
|
+
definition with what it resolved to — `include` or `exclude`, the pattern, the members it
|
|
3945
|
+
admits or removes (count and a bounded sample), and for a model's inclusion the matches the
|
|
3946
|
+
repository's ignore rules refused — so the model sees what its glob did and adapts.
|
|
3947
|
+
`discover` is introspection, never a catalog: a path answers why it is or is not visible
|
|
3948
|
+
(tracked, included by which pattern, a creation record, excluded by which `!glob`, ignored,
|
|
3949
|
+
untracked, absent); a glob previews what `add` would include or exclude. Names only, never
|
|
3950
|
+
content; nothing is added.
|
|
3951
|
+
|
|
3952
|
+
§members-configuration *Available definitions.* The operator's `PLURNK_MEMBERS_<ALIAS>=<glob>`
|
|
3953
|
+
(`!glob` excludes) and `PLURNK_MEMBERS_ENABLED=[…]` (absent or `[]` enables none) are the
|
|
3954
|
+
service-origin definitions, the shape `PLURNK_MCP_*` already has; an empty glob, a bare `!`,
|
|
3955
|
+
or an unknown enabled alias fails the daemon at boot.
|
|
3956
|
+
|
|
3957
|
+
§members-model-scope *The model's authority.* A model's `add` is admitted against
|
|
3958
|
+
`PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` in the file-creation lattice `none < root <
|
|
3959
|
+
namespace`, narrowed by `settings.membersModelScope` (most restrictive wins). Shipped `none`
|
|
3960
|
+
refuses every model definition — inclusion or exclusion — as `403
|
|
3961
|
+
members/functionality/model-scope`, naming `git add` and the operator's `/members add` as
|
|
3962
|
+
the paths that remain; `root` admits patterns inside the root; `namespace` admits `../` too.
|
|
3963
|
+
`auto` loops self-approve proposals, so the ceiling — not the proposal — is the guard
|
|
3964
|
+
({§membership-baseline}). The coordinator hands `admit` the caller (`action` | `operation`)
|
|
3965
|
+
so the family bounds the model without a second grammar.
|
|
3966
|
+
|
|
3967
|
+
§members-projection *One overlay.* Definitions are desired state per Worker
|
|
3968
|
+
({§functionality-state}); the workspace overlay (`workspace_constraints`) is their union
|
|
3969
|
+
across every worker of the workspace (ruling (a)): inclusions union and an exclusion wins.
|
|
3970
|
+
A child's birth snapshot counts as its own desire: a parent's `disable` or `remove`
|
|
3971
|
+
withdraws nothing the child still holds, and a file goes dark only when no worker of the
|
|
3972
|
+
workspace holds an inclusion for it. Human-authored definitions project with source
|
|
3973
|
+
`members`, model-proposed ones with source `model`; the same pattern from both keeps
|
|
3974
|
+
`members`. A `model` inclusion is a pattern scan like a human one but never admits a path
|
|
3975
|
+
the repository ignores ({§membership-model-universe}). The engine's creation records
|
|
3976
|
+
(`source: "create"`, {§fs-create-record}) are not definitions: the projection never
|
|
3977
|
+
overwrites or retires them. Projection happens at the family's publication commit and
|
|
3978
|
+
re-resolves membership; a Worker cooling changes nothing, because desired state is
|
|
3979
|
+
durable. Each enabled definition is one generated document at
|
|
3980
|
+
`worker://~/_plurnk/members/<alias>.md` ({§functionality-documents}) — its glob, origin,
|
|
3981
|
+
provenance, and what it resolved to — surveyed at turn 0 like every family's enabled
|
|
3982
|
+
definitions ({§actor-boundary-catalog-preview}), so the model sees why a file is or is
|
|
3983
|
+
not a member before it asks. There is no other membership path: the client's `/members`
|
|
3984
|
+
verbs are these verbs.
|
|
3985
|
+
|
|
3747
3986
|
§skills-functionality **Agent Skills are one Worker Functionality family.**
|
|
3748
3987
|
Core registers the `skills` family with the coordinator ({§functionality-coordinator});
|
|
3749
3988
|
its adapter owns protocol truth for standard Agent Skills and nothing else. A
|
|
@@ -3811,15 +4050,16 @@ model turn while an unchanged set dispatches nothing. The model manages skills
|
|
|
3811
4050
|
only through the generated `EXEC [skills]` family
|
|
3812
4051
|
({§functionality-model-projection}); it is never taught a package manager.
|
|
3813
4052
|
|
|
3814
|
-
The catalog describes this worker's Functionality
|
|
3815
|
-
|
|
3816
|
-
|
|
4053
|
+
The catalog describes this worker's Functionality under its durable capability
|
|
4054
|
+
ceilings. Turn0 narrows its surveys and examples through the current loop policy,
|
|
4055
|
+
and a direct denied attempt receives the same exact 403 from dispatch rather
|
|
4056
|
+
than a second documentation policy.
|
|
3817
4057
|
Optional non-EXEC operations remain a separate `## Enabled Optional Operations`
|
|
3818
4058
|
section because they are language extensions rather than executable tools.
|
|
3819
4059
|
|
|
3820
4060
|
### §schemes user.schemes — the resource directory
|
|
3821
4061
|
|
|
3822
|
-
§schemes-directory A `## Resources` section renders in the system slot **after the policy sections** — a terse directory of the scheme families available to this worker, 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 as the worker-private skill `worker://~/_plurnk/
|
|
4062
|
+
§schemes-directory A `## Resources` section renders in the system slot **after the policy sections** — a terse directory of the scheme families available to this worker, 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 as the worker-private skill `worker://~/_plurnk/plurnk/<scheme>.md` and discovered via the turn-0 `## FIND0 [+init,+skills] (worker://~/_plurnk/plurnk/*.md)` survey ({§skills-functionality}), keeping the raw packet free of doc links. Meta-owned `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. The directory includes only examples admitted by the effective worker-level capability layers, and Turn0 further narrows discovery through its loop policy using the same resolver ({§capability-admission}); the packet never baits an operation its own admission path will refuse. Materialized pull docs remain worker state, while their discoverability and execution remain policy-bound.
|
|
3823
4063
|
|
|
3824
4064
|
### §inject system.inject — the operator injection
|
|
3825
4065
|
|
|
@@ -3830,7 +4070,7 @@ section because they are language extensions rather than executable tools.
|
|
|
3830
4070
|
§policy-sections One section rides the system slot **after the definition and before capability teaching**: `## Policy` from `PLURNK_SERVICE_POLICY` (default `$XDG_CONFIG_HOME/plurnk/AGENTS.md`, {§host-path-layout}). Policy is 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. The PROJECT `AGENTS.md` is local guidance, not policy: it rides turn 0 as the foisted `worker://~/_plurnk/agents.md` entry ({§turn0-agents-stunt}); all other reference material is skills under the worker's private skills tree ({§skills-functionality}).
|
|
3831
4071
|
|
|
3832
4072
|
On first run, and only when `$XDG_CONFIG_HOME/plurnk` itself is absent, the service seeds
|
|
3833
|
-
`AGENTS.md` from `@plurnk/plurnk-meta/
|
|
4073
|
+
`AGENTS.md` from `@plurnk/plurnk-meta/POLICY.md` ({§teaching-corpus}).
|
|
3834
4074
|
It reads that required source before creating the service home; a failed read
|
|
3835
4075
|
surfaces with its cause and leaves no apparently initialized home.
|
|
3836
4076
|
After that bootstrap the file is user-owned: edits and deletion persist, and a
|
|
@@ -3848,36 +4088,44 @@ created by that attempt. Unknown legacy members or simultaneous
|
|
|
3848
4088
|
legacy/canonical state fail without guessing. No dual read or dual write survives
|
|
3849
4089
|
the transition.
|
|
3850
4090
|
|
|
3851
|
-
§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. Every published pull doc carries an exact H2 `Summary` for ordinary catalog projection. `SchemeRegistry.teach(workerId)` renders the effective directory, `SchemeRegistry.docs(workerId)` resolves corpus-or-manifest documentation, and `referenceEntries(workerId)` supplies the current `/
|
|
4091
|
+
§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. Every published pull doc carries an exact H2 `Summary` for ordinary catalog projection. `SchemeRegistry.teach(workerId)` renders the effective directory, `SchemeRegistry.docs(workerId)` resolves corpus-or-manifest documentation, and `referenceEntries(workerId)` supplies the current `/plurnk/` generated-skill set when core publishes worker Functionality ({§skills-functionality}). One materializer reconciles the worker's private scope exactly: vanished contributions are deleted before current documents are upserted, so an excluded scheme or disabled, detached, replaced, or removed runtime cannot leave a stale model-facing contract.
|
|
3852
4092
|
|
|
3853
4093
|
### §packet-git-status The Git status section — compact repository state
|
|
3854
4094
|
|
|
3855
4095
|
When Git is admitted for the workspace, `## Git Status` reports the current
|
|
3856
|
-
branch, upstream ahead/behind counts, and staged/unstaged/untracked totals
|
|
4096
|
+
branch, upstream ahead/behind counts, and staged/unstaged/untracked totals, then
|
|
4097
|
+
one bounded line per non-empty class (at most eight paths, `+K more`): staged,
|
|
4098
|
+
unstaged, `untracked members` — each path with the inclusion pattern that admits it or
|
|
4099
|
+
`created` for a creation record — and `untracked (not members)`, named because such a
|
|
4100
|
+
file is not a member ({§membership-baseline}) and a human must `git add` it or add a
|
|
4101
|
+
members definition before the model can read it. The section never contradicts the
|
|
4102
|
+
catalog: an untracked file a definition admits is named as the member it is. The
|
|
3857
4103
|
active direct child of a running branch batch additionally receives its assigned
|
|
3858
4104
|
branch and the requirement to commit any project changes and leave the checkout
|
|
3859
4105
|
clean before concluding ({§worker-branch-batch-return}); no other worker receives
|
|
3860
|
-
that instruction. The section never
|
|
4106
|
+
that instruction. The section never carries an unbounded path list. Per-path state belongs to
|
|
3861
4107
|
the runtime actor's durable causal evidence: its `source=file` row carries
|
|
3862
4108
|
the exact two-character porcelain `XY` value as `git` metadata when the status
|
|
3863
4109
|
snapshot names that path. The engine takes one snapshot after membership
|
|
3864
4110
|
reconciliation and uses it for both projections; no per-file Git process exists.
|
|
3865
4111
|
|
|
3866
|
-
### §
|
|
4112
|
+
### §recap Optional Recap footer
|
|
3867
4113
|
|
|
3868
|
-
The user slot
|
|
3869
|
-
operational law already owned by `plurnk.md`. A non-empty `runLoop` /
|
|
3870
|
-
`
|
|
3871
|
-
`
|
|
3872
|
-
|
|
3873
|
-
|
|
4114
|
+
The user slot may end with `## Recap`, a compact recency-biased reminder of
|
|
4115
|
+
selected operational law already owned by `plurnk.md`. A non-empty `runLoop` /
|
|
4116
|
+
`runTurn` `recap` value overrides the default; otherwise core reads
|
|
4117
|
+
`PLURNK_SERVICE_RECAP` or the required meta-owned `recap.md` source for every
|
|
4118
|
+
packet. Empty content intentionally omits the rendered section while retaining
|
|
4119
|
+
one dormant authored source. A failed read fails packet assembly with its cause.
|
|
4120
|
+
The footer is one projection path and one authored source, not a second language
|
|
4121
|
+
contract.
|
|
3874
4122
|
|
|
3875
4123
|
## §matcher Matcher selection and text regions
|
|
3876
4124
|
|
|
3877
4125
|
Body matchers and text scopes are independent. Matcher prefixes choose a
|
|
3878
|
-
dialect (`//` xpath, `/` regex, `$` jsonpath,
|
|
3879
|
-
resources and report evidence. A text scope always addresses
|
|
3880
|
-
text, regardless of mimetype.
|
|
4126
|
+
dialect (`//` xpath, `/` regex, `$` jsonpath, `~` semantic, `&` graph, otherwise
|
|
4127
|
+
glob); they select resources and report evidence. A text scope always addresses
|
|
4128
|
+
the exact readable text, regardless of mimetype.
|
|
3881
4129
|
|
|
3882
4130
|
### §matcher-dispatch Matcher dispatch
|
|
3883
4131
|
|
|
@@ -3889,9 +4137,9 @@ One parsed content matcher crosses three ownership layers:
|
|
|
3889
4137
|
| `@plurnk/plurnk-schemes/Matcher` | Map framework results and typed failures to the universal scheme-result contract. |
|
|
3890
4138
|
| Core `Matcher.matchCandidates` | Apply that operation adapter across caller-supplied `{key, content, mimetype}` candidates and preserve source identity. |
|
|
3891
4139
|
|
|
3892
|
-
§relation-indexed-dialects `~semantic` and
|
|
3893
|
-
the content matcher.
|
|
3894
|
-
|
|
4140
|
+
§relation-indexed-dialects `~semantic` and `&graph` are indexed relation dialects and never route through
|
|
4141
|
+
the content matcher. Language admission accepts exactly one graph symbol (`&sym`, `&<sym`, `&>sym`);
|
|
4142
|
+
runtime validation is only a defensive boundary for typed callers. When the candidates' persistent index is still
|
|
3895
4143
|
deriving, the engine settles the workspace's derivations once and re-runs the selection; a still-incomplete
|
|
3896
4144
|
index is a 503 that is retryable — never a refusal to wait paired with an instruction to wait. Candidate composition has no dependency on the table that
|
|
3897
4145
|
stored a resource.
|
|
@@ -3905,9 +4153,9 @@ container identity.
|
|
|
3905
4153
|
|
|
3906
4154
|
| Matcher body | Selected resources | Match evidence |
|
|
3907
4155
|
| ------------ | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
3908
|
-
|
|
|
3909
|
-
|
|
|
3910
|
-
|
|
|
4156
|
+
| `&<symbol` | In-scope resources that reference `symbol` | Each matching reference's source span |
|
|
4157
|
+
| `&>symbol` | In-scope resources defining names referenced by each definition of `symbol` | Each referenced symbol's definition span |
|
|
4158
|
+
| `&symbol` | Union of definitions of `symbol`, referrers, and definitions of referenced names | Corresponding definition/reference spans, deduplicated by resource + span |
|
|
3911
4159
|
|
|
3912
4160
|
| Result | HTTP status |
|
|
3913
4161
|
|---|---|
|
|
@@ -3917,6 +4165,12 @@ container identity.
|
|
|
3917
4165
|
| Source unparseable for its mimetype | 203 (soft fallback: raw content as text with `reason`) |
|
|
3918
4166
|
| Dialect unsupported by the resource | 415 |
|
|
3919
4167
|
|
|
4168
|
+
§matcher-invalid-expression A malformed matcher Problem identifies the dialect
|
|
4169
|
+
and includes the bounded native parser cause as `diagnostic` when available. Core applies
|
|
4170
|
+
`PLURNK_SERVICE_ERROR_DETAIL_LIMIT` before the cause crosses into the schemes
|
|
4171
|
+
adapter; the Problem offers only the deterministic recovery to revise the
|
|
4172
|
+
expression, never a guess about the intended pattern.
|
|
4173
|
+
|
|
3920
4174
|
§matcher-dispatch-203-soft-fallback On parse failure, 203 returns raw content as the text primitive with `reason`
|
|
3921
4175
|
so the model can use ordinary text retrieval or repair the source.
|
|
3922
4176
|
|
|
@@ -3946,7 +4200,7 @@ resources according to {§find-result-projection}.
|
|
|
3946
4200
|
| jsonpath `$.path` | resources whose deep JSON resolves the path | canonical locator plus exact/enclosing text region when honest |
|
|
3947
4201
|
| xpath `//sel` | resources whose deep XML resolves the selector | canonical locator plus exact/enclosing text region when honest |
|
|
3948
4202
|
| `~`semantic `~q` | resources ranked by indexed chunks | chunk text region when available |
|
|
3949
|
-
|
|
|
4203
|
+
| `&`graph `&<sym` | resources with matching symbol relations | symbol text region when available |
|
|
3950
4204
|
|
|
3951
4205
|
Match evidence is navigation evidence, never an implicit body projection. The
|
|
3952
4206
|
model uses broad FIND to select and page resources, exact FIND to page that
|