@plurnk/plurnk-service 1.8.0 → 1.9.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 +20 -0
- package/INSTALL.md +14 -16
- package/README.md +1 -1
- package/SPEC.md +635 -296
- package/digest-sql/channel-dispositions/dispositions.sql +2 -2
- package/digest-sql/entry-dispositions/dispositions.sql +2 -2
- package/dist/Paths.d.ts +1 -2
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -9
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/core/ChannelWrite.d.ts +8 -3
- package/dist/core/ChannelWrite.d.ts.map +1 -1
- package/dist/core/ChannelWrite.js +40 -8
- package/dist/core/ChannelWrite.js.map +1 -1
- package/dist/core/ChannelWrite.sql +17 -4
- package/dist/core/CoreSchemeServices.d.ts +7 -3
- package/dist/core/CoreSchemeServices.d.ts.map +1 -1
- package/dist/core/CoreSchemeServices.js +8 -1
- package/dist/core/CoreSchemeServices.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +9 -1
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +321 -221
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Engine.d.ts +13 -4
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/Engine.js +44 -22
- package/dist/core/Engine.js.map +1 -1
- package/dist/core/Engine.sql +101 -81
- package/dist/core/EntryAddressBinding.d.ts +32 -0
- package/dist/core/EntryAddressBinding.d.ts.map +1 -0
- package/dist/core/EntryAddressBinding.js +93 -0
- package/dist/core/EntryAddressBinding.js.map +1 -0
- package/dist/core/ExecutorRegistry.d.ts +4 -4
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +28 -28
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/LogBody.d.ts.map +1 -1
- package/dist/core/LogBody.js +19 -1
- package/dist/core/LogBody.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +5 -2
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/OverflowTurn.d.ts.map +1 -1
- package/dist/core/OverflowTurn.js +5 -1
- package/dist/core/OverflowTurn.js.map +1 -1
- package/dist/core/Owner.js +1 -1
- package/dist/core/Owner.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +5 -1
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +21 -11
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/ProposalLifecycle.d.ts +4 -1
- package/dist/core/ProposalLifecycle.d.ts.map +1 -1
- package/dist/core/ProposalLifecycle.js +85 -26
- package/dist/core/ProposalLifecycle.js.map +1 -1
- package/dist/core/ReasoningEvent.d.ts +13 -0
- package/dist/core/ReasoningEvent.d.ts.map +1 -0
- package/dist/core/ReasoningEvent.js +6 -0
- package/dist/core/ReasoningEvent.js.map +1 -0
- package/dist/core/ResourceMutations.d.ts +15 -19
- package/dist/core/ResourceMutations.d.ts.map +1 -1
- package/dist/core/ResourceMutations.js +113 -38
- package/dist/core/ResourceMutations.js.map +1 -1
- package/dist/core/SchemeRegistry.d.ts +12 -11
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +52 -47
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/ToolResources.d.ts.map +1 -1
- package/dist/core/ToolResources.js +20 -19
- package/dist/core/ToolResources.js.map +1 -1
- package/dist/core/Turn.d.ts +1 -1
- package/dist/core/Turn.d.ts.map +1 -1
- package/dist/core/TurnOps.d.ts +2 -2
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +14 -4
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +46 -3
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +165 -78
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/WorkerName.d.ts +1 -0
- package/dist/core/WorkerName.d.ts.map +1 -1
- package/dist/core/WorkerName.js +2 -1
- package/dist/core/WorkerName.js.map +1 -1
- package/dist/core/WorkerName.sql +17 -2
- package/dist/core/WorkspaceGate.js +1 -1
- package/dist/core/WorkspaceGate.js.map +1 -1
- package/dist/core/caps/CapsResolve.d.ts +2 -2
- package/dist/core/caps/CapsResolve.d.ts.map +1 -1
- package/dist/core/caps/CapsResolve.js +5 -7
- package/dist/core/caps/CapsResolve.js.map +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts +1 -1
- package/dist/core/caps/DbChannelCaps.d.ts.map +1 -1
- package/dist/core/caps/DbChannelCaps.js +13 -11
- package/dist/core/caps/DbChannelCaps.js.map +1 -1
- package/dist/core/caps/DbEntryCaps.d.ts +5 -5
- package/dist/core/caps/DbEntryCaps.d.ts.map +1 -1
- package/dist/core/caps/DbEntryCaps.js +27 -30
- package/dist/core/caps/DbEntryCaps.js.map +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts +1 -1
- package/dist/core/caps/DbNotifyCaps.d.ts.map +1 -1
- package/dist/core/caps/DbNotifyCaps.js +5 -3
- package/dist/core/caps/DbNotifyCaps.js.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.d.ts +3 -3
- package/dist/core/caps/DbSubscriptionCaps.d.ts.map +1 -1
- package/dist/core/caps/DbSubscriptionCaps.js +16 -26
- package/dist/core/caps/DbSubscriptionCaps.js.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.d.ts +6 -4
- package/dist/core/caps/SchemeCtxImpl.d.ts.map +1 -1
- package/dist/core/caps/SchemeCtxImpl.js +37 -5
- package/dist/core/caps/SchemeCtxImpl.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts +9 -0
- package/dist/core/file-creation-policy.d.ts.map +1 -0
- package/dist/core/file-creation-policy.js +25 -0
- package/dist/core/file-creation-policy.js.map +1 -0
- package/dist/core/fork.d.ts +4 -1
- package/dist/core/fork.d.ts.map +1 -1
- package/dist/core/fork.js +27 -15
- package/dist/core/fork.js.map +1 -1
- package/dist/core/fork.sql +35 -22
- package/dist/core/git-membership.d.ts +19 -1
- package/dist/core/git-membership.d.ts.map +1 -1
- package/dist/core/git-membership.js +225 -50
- package/dist/core/git-membership.js.map +1 -1
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +28 -19
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/plurnk-uri.d.ts +13 -2
- package/dist/core/plurnk-uri.d.ts.map +1 -1
- package/dist/core/plurnk-uri.js +59 -21
- package/dist/core/plurnk-uri.js.map +1 -1
- package/dist/core/scheme-types.d.ts +1 -0
- package/dist/core/scheme-types.d.ts.map +1 -1
- package/dist/core/worker-ops.sql +10 -6
- package/dist/core/worker-settings.d.ts +1 -0
- package/dist/core/worker-settings.d.ts.map +1 -1
- package/dist/core/worker-settings.js +7 -0
- package/dist/core/worker-settings.js.map +1 -1
- package/dist/core/workspace-settings.d.ts +2 -0
- package/dist/core/workspace-settings.d.ts.map +1 -1
- package/dist/core/workspace-settings.js +5 -1
- package/dist/core/workspace-settings.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +1 -1
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/digest.sql +1 -1
- package/dist/schemes/Exec.d.ts +3 -2
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +55 -22
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecOutputScheme.d.ts.map +1 -1
- package/dist/schemes/ExecOutputScheme.js +5 -4
- package/dist/schemes/ExecOutputScheme.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +170 -80
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Prompt.d.ts +2 -3
- package/dist/schemes/Prompt.d.ts.map +1 -1
- package/dist/schemes/Prompt.js +4 -7
- package/dist/schemes/Prompt.js.map +1 -1
- package/dist/schemes/Skill.d.ts.map +1 -1
- package/dist/schemes/Skill.js +3 -1
- package/dist/schemes/Skill.js.map +1 -1
- package/dist/schemes/Worker.d.ts.map +1 -1
- package/dist/schemes/Worker.js +95 -49
- package/dist/schemes/Worker.js.map +1 -1
- package/dist/schemes/_entry-crud.d.ts +5 -5
- package/dist/schemes/_entry-crud.d.ts.map +1 -1
- package/dist/schemes/_entry-crud.js +22 -17
- package/dist/schemes/_entry-crud.js.map +1 -1
- package/dist/schemes/_entry-crud.sql +58 -23
- package/dist/schemes/_entry-find.d.ts +3 -2
- package/dist/schemes/_entry-find.d.ts.map +1 -1
- package/dist/schemes/_entry-find.js +26 -25
- package/dist/schemes/_entry-find.js.map +1 -1
- package/dist/schemes/_entry-find.sql +10 -7
- package/dist/schemes/_entry-manifest.d.ts +2 -2
- package/dist/schemes/_entry-manifest.d.ts.map +1 -1
- package/dist/schemes/_entry-manifest.js +6 -5
- package/dist/schemes/_entry-manifest.js.map +1 -1
- package/dist/schemes/_entry-ops.d.ts +6 -5
- package/dist/schemes/_entry-ops.d.ts.map +1 -1
- package/dist/schemes/_entry-ops.js +39 -46
- package/dist/schemes/_entry-ops.js.map +1 -1
- package/dist/schemes/_entry-ops.sql +9 -6
- package/dist/schemes/_entry-send.d.ts +2 -2
- package/dist/schemes/_entry-send.d.ts.map +1 -1
- package/dist/schemes/_entry-send.js +16 -14
- package/dist/schemes/_entry-send.js.map +1 -1
- package/dist/schemes/_search-index.d.ts.map +1 -1
- package/dist/schemes/_search-index.js +2 -0
- package/dist/schemes/_search-index.js.map +1 -1
- package/dist/server/Daemon.d.ts +42 -10
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +326 -248
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +84 -19
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.d.ts +3 -1
- package/dist/server/DrainSupervisor.d.ts.map +1 -1
- package/dist/server/DrainSupervisor.js +11 -9
- package/dist/server/DrainSupervisor.js.map +1 -1
- package/dist/server/Functionality.d.ts +44 -0
- package/dist/server/Functionality.d.ts.map +1 -0
- package/dist/server/Functionality.js +422 -0
- package/dist/server/Functionality.js.map +1 -0
- package/dist/server/FunctionalityManager.d.ts +22 -0
- package/dist/server/FunctionalityManager.d.ts.map +1 -0
- package/dist/server/FunctionalityManager.js +121 -0
- package/dist/server/FunctionalityManager.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +61 -0
- package/dist/server/SkillsFunctionality.d.ts.map +1 -0
- package/dist/server/SkillsFunctionality.js +449 -0
- package/dist/server/SkillsFunctionality.js.map +1 -0
- package/dist/server/WorkerCapabilities.d.ts +21 -0
- package/dist/server/WorkerCapabilities.d.ts.map +1 -0
- package/dist/server/WorkerCapabilities.js +214 -0
- package/dist/server/WorkerCapabilities.js.map +1 -0
- package/dist/server/WorkerResidency.d.ts +40 -0
- package/dist/server/WorkerResidency.d.ts.map +1 -0
- package/dist/server/WorkerResidency.js +283 -0
- package/dist/server/WorkerResidency.js.map +1 -0
- package/dist/server/client-input.d.ts +1 -0
- package/dist/server/client-input.d.ts.map +1 -1
- package/dist/server/client-input.js +22 -1
- package/dist/server/client-input.js.map +1 -1
- package/dist/server/dispatch-as-plurnk.d.ts +2 -2
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +89 -25
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/drain.sql +11 -6
- package/dist/server/envelope.d.ts +6 -1
- package/dist/server/envelope.d.ts.map +1 -1
- package/dist/server/envelope.js +8 -2
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/envelope.sql +11 -3
- package/dist/server/lifecycle-recovery.sql +13 -16
- package/dist/server/loopDocs.d.ts +2 -1
- package/dist/server/loopDocs.d.ts.map +1 -1
- package/dist/server/loopDocs.js +67 -51
- package/dist/server/loopDocs.js.map +1 -1
- package/dist/server/loopDocs.sql +14 -10
- package/dist/server/module-discovery.d.ts +2 -2
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +27 -2
- package/dist/server/module-discovery.js.map +1 -1
- package/dist/server/seam-entry-read.sql +9 -7
- package/dist/server/seam-log-read.sql +1 -1
- package/dist/server/seam-loop.sql +10 -1
- package/dist/server/seam-proposal-list.sql +4 -2
- package/dist/server/worker-capabilities.sql +19 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +11 -1
- package/dist/service.js.map +1 -1
- package/migrations/001_schema.sql +486 -136
- package/package.json +34 -32
- package/dist/server/skillDocs.d.ts +0 -9
- package/dist/server/skillDocs.d.ts.map +0 -1
- package/dist/server/skillDocs.js +0 -212
- package/dist/server/skillDocs.js.map +0 -1
- package/dist/server/skillDocs.sql +0 -14
- package/dist/server/workspace-capabilities.sql +0 -19
package/SPEC.md
CHANGED
|
@@ -47,7 +47,7 @@ their absence never makes a client, plugin, or `_plurnk` turn exceptional.
|
|
|
47
47
|
| Field | Contract |
|
|
48
48
|
|---|---|
|
|
49
49
|
| `producer` | Required actor class: `model`, `client`, `plugin`, or `_plurnk`. |
|
|
50
|
-
| `kind` | Required purpose: `inference`, `initialization`, `overflow`, or `
|
|
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
53
|
| `turnOps` | Every admitted source-backed turn preserves its exact PLAN…SEND program as one undecorated actionless log item under {§turn-ops-entry}. The item supplements rather than replaces the executed operation rows. |
|
|
@@ -75,7 +75,7 @@ not fabricate verbatim source.
|
|
|
75
75
|
|
|
76
76
|
| Term | Meaning |
|
|
77
77
|
|---|---|
|
|
78
|
-
| **entry** | The unit of canonical state. Identity: `(
|
|
78
|
+
| **entry** | The unit of canonical state. Identity: `(owner, scheme, authority, pathname)`; the owner Worker determines the workspace ({§entry-identity-no-null}). Holds one or more `channels` of content plus private `attributes`. |
|
|
79
79
|
| **channel** | A named content buffer on an entry. Examples: `body`, `stdout`, `stderr`, `headers`, `symbols`. Each channel has `content`, `mimetype`, curation `weight`, and lifecycle `state`. |
|
|
80
80
|
| **scope** | A scheme-manifest declaration ignored by core; registrations are discovered at boot and are not persisted. Entry sharing and privacy are owner-based; #80 owns retiring this residual axis. |
|
|
81
81
|
| **scheme** | An addressed capability family + handler. Built-ins include `worker`, `prompt`, `log`, and bare/file paths; discovered schemes and executor-runtime tags extend that set. Internal `exec` routes the EXEC op but is not an addressable model namespace. Consumption surface {§scheme-surface}; author contract: [plurnk-schemes](../plurnk-schemes/SPEC.md). |
|
|
@@ -214,6 +214,18 @@ clients render and submit actions but contain no engine logic.
|
|
|
214
214
|
|
|
215
215
|
OpenTelemetry may observe PLURNK; it never becomes product state, failure transport, scheduler input, model teaching, or client protocol. Domain and client activity remain on AG-UI. Reusable packages depend on the OTel API only; the daemon constructs only the explicitly configured trace and metric providers. An unconfigured or standards-valid disabled process loads no SDK or exporter implementation and keeps the API's no-op behavior with bounded overhead. OTel Logs have no provider or initialization path.
|
|
216
216
|
|
|
217
|
+
### §standards-discernment Standards discernment
|
|
218
|
+
|
|
219
|
+
Seven principles govern which exterior standards Plurnk conforms to (#299):
|
|
220
|
+
|
|
221
|
+
1. **UVP first.** Never conform away what users chose Plurnk for; the OP grammar, curated log, packet, and worker graph are the product, not a compatibility gap.
|
|
222
|
+
2. **Right-fit.** Hobbyist-first: an enterprise-grade feature is acceptable only when its cost lands on the party that wants it, never on general adoption.
|
|
223
|
+
3. **Traction.** Count running counterparties today; integration horizon must be shorter than the standard's expected half-life. Sockets stay configurable with no default until a candidate earns it.
|
|
224
|
+
4. **POSIX app identity.** Decades-stable host-ecosystem conventions (XDG, NO_COLOR, man, completions, service units) outrank months-stable AI-pipeline fashions.
|
|
225
|
+
5. **Faces, never organs.** A standard adopts as one adapter or projection behind an existing seam; if it cannot, that is the alarm, and it goes to a design gate.
|
|
226
|
+
6. **Deletion is the price of admission.** A standard earns adoption by deleting bespoke surface (the ACP Plan object deleted the Markdown plan microformat); parallel representations, second discovery paths, and compatibility grammars are refused.
|
|
227
|
+
7. **Two arbiters.** Model-facing surfaces change only on measured model evidence; human-facing surfaces follow host-ecosystem convention without ceremony. Standards bodies get a vote on neither.
|
|
228
|
+
|
|
217
229
|
Configuration uses the standard `OTEL_*` environment: `OTEL_TRACES_EXPORTER` / `OTEL_METRICS_EXPORTER` select `otlp` or `console` per signal (a missing or `none` value keeps that signal off; no SDK default selects an exporter), `OTEL_SERVICE_NAME` names the service (default `plurnk-service`), case-insensitive `true` in `OTEL_SDK_DISABLED` turns the boundary off, and OTLP exporters honor `OTEL_EXPORTER_OTLP_*`. An unknown exporter name fails daemon boot; a typo never silently disables observation. OTel Logs and direct draft semantic-convention use are excluded. HTTP spans carry only an AG-UI-owned bounded route class, never an input pathname or query. Spans otherwise carry high-cardinality identifiers; metric labels stay low-cardinality. Prompts, reasoning, file bodies, arbitrary URLs, secrets, and plugin payloads are never recorded as attributes or metric values by default. Exporter failure cannot change product results or client lifecycle. Daemon, telemetry, and database teardown are independent reverse-ownership phases; every phase runs and aggregate failure preserves every cause.
|
|
218
230
|
|
|
219
231
|
§observability-genai-conventions **GenAI convention projection.** Provider
|
|
@@ -287,9 +299,10 @@ preserving the originating failure.
|
|
|
287
299
|
|
|
288
300
|
```mermaid
|
|
289
301
|
flowchart LR
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
302
|
+
child["Child worker"] -->|"durable activity<br/>environment door"| parent["Direct parent log"]
|
|
303
|
+
actor["Any worker"] -->|"mutate worker:/// commons"| commons["Workspace commons"]
|
|
304
|
+
commons -->|"one folded broadcast occurrence"| logs["Every existing worker log"]
|
|
305
|
+
actor -->|"SEND to named worker<br/>voice door"| log["Addressed worker log"]
|
|
293
306
|
client["User / client"] -->|"loop.inject<br/>voice door"| log
|
|
294
307
|
```
|
|
295
308
|
|
|
@@ -304,34 +317,68 @@ render-time filter.
|
|
|
304
317
|
**attribution** — the delta's provenance ({§env-delta}) — and is never read to
|
|
305
318
|
filter a row.
|
|
306
319
|
|
|
320
|
+
§actor-boundary-attached-functionality **A client operates in the Worker it is
|
|
321
|
+
attached to.** A client connection is attached to one conversation Worker; its
|
|
322
|
+
management commands mutate that Worker's Functionality ({§module-worker-capabilities}),
|
|
323
|
+
and its operations execute in that Worker's environment — executable families,
|
|
324
|
+
runtime schemes, per-Worker tool admission, flag-scoped scheme availability,
|
|
325
|
+
and effect policy resolve through the attached Worker — while the operation
|
|
326
|
+
journals in the client's own worker ({§connection-lifecycle}) and any entry it
|
|
327
|
+
writes binds its principal through that client worker. Every dispatch therefore
|
|
328
|
+
carries two coordinates: `workerId`, the journaling and entry principal, and
|
|
329
|
+
`functionalityWorkerId`, the Worker whose Functionality applies. They are equal
|
|
330
|
+
for model and `_plurnk` dispatches; a client dispatch names its attached
|
|
331
|
+
Worker, which must belong to the same workspace, and Functionality residency is
|
|
332
|
+
acquired for that Worker. Attachment is a connection fact, not topology: it is
|
|
333
|
+
many-to-one, non-owning, and never expressed as parentage.
|
|
334
|
+
|
|
307
335
|
§actor-boundary-two-doors **Cross-worker arrival is limited to two doors.**
|
|
308
336
|
An explicit READ is not an arrival: the reading worker deliberately addresses a
|
|
309
337
|
file or ancestry-authorized entry through ordinary dispatch ({§worker-read-scope}).
|
|
310
338
|
|
|
311
|
-
| Door | Carries
|
|
312
|
-
| ----------- |
|
|
313
|
-
| Environment | A
|
|
314
|
-
| Voice | A directed `loop.inject` or `## SEND0 (worker://name)` message.
|
|
315
|
-
|
|
316
|
-
§actor-boundary-
|
|
339
|
+
| Door | Carries | Wake behavior |
|
|
340
|
+
| ----------- | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
341
|
+
| Environment | A direct child's durable activity to its parent, plus a successful mutation of the deliberately global `worker:///` commons to every worker. | Intermediate activity and commons never wake; a child's terminal disposition wakes its parent. |
|
|
342
|
+
| Voice | A directed `loop.inject` or `## SEND0 (worker://name)` message. | An active worker folds it into its next turn; an idle one wakes. |
|
|
343
|
+
|
|
344
|
+
§actor-boundary-lineage-attention **Addressability is workspace-wide; attention
|
|
345
|
+
is lineage-scoped.** Project files, registered resources, and permitted worker
|
|
346
|
+
entries remain addressable throughout the workspace, but ordinary changes do
|
|
347
|
+
not enter unrelated workers' logs. A child's activity reaches only its direct
|
|
348
|
+
parent. That observer row carries the source occurrence identity and never
|
|
349
|
+
republishes, so grandparents observe what their own direct children do without
|
|
350
|
+
receiving an automatic recursive mirror of every descendant.
|
|
351
|
+
|
|
352
|
+
§actor-boundary-commons-broadcast **`worker:///` is the explicit global
|
|
353
|
+
attention surface.** A successful mutation whose landed effects touch the
|
|
354
|
+
commons emits one occurrence to every worker that existed when it landed.
|
|
355
|
+
Lineage and commons audiences are a union over that one identity: when a child
|
|
356
|
+
mutates the commons, its parent receives one observer row, never a parent copy
|
|
357
|
+
plus a broadcast duplicate. Ordinary project files, private worker entries,
|
|
358
|
+
and remote resources do not acquire ambient attention merely because they are
|
|
359
|
+
workspace-addressable.
|
|
360
|
+
|
|
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} `read-only` overlay, a workspace policy, bounds every worker's writable 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.
|
|
317
362
|
|
|
318
363
|
§actor-boundary-passive-wake **Passive wake follows ownership.** A directed
|
|
319
364
|
voice wakes an idle worker. A parked continuation resumes when an obligation it
|
|
320
|
-
owns — a child or stream — reaches an observable transition
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
control, not a third door through which
|
|
365
|
+
owns — a child or stream — reaches an observable terminal transition
|
|
366
|
+
({§worker-loop-lifecycle}). Intermediate child activity and commons broadcasts
|
|
367
|
+
never wake; they queue until another cause produces a turn ({§env-delta}). The
|
|
368
|
+
obligation edge is continuation control, not a third door through which
|
|
369
|
+
arbitrary workspace state can enter.
|
|
324
370
|
|
|
325
371
|
§actor-boundary-self-hosting **Use the actor path when the work has an
|
|
326
372
|
operation; retain irreducible rails in the kernel.** The workspace has one
|
|
327
373
|
reserved `plurnk` worker. It is durable; `DispatchAsPlurnk` opens a fresh
|
|
328
374
|
administrative loop and turn for each ordinary operation batch. Other workers
|
|
329
|
-
|
|
330
|
-
|
|
375
|
+
cannot read its private log or entries. Generated references are instead
|
|
376
|
+
materialized directly in the addressed worker's private space; only direct
|
|
377
|
+
lineage activity and explicit commons mutations cross the environment door.
|
|
331
378
|
|
|
332
379
|
| Work | Owning path | Why |
|
|
333
380
|
| --------------------------------------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
|
334
|
-
|
|
|
381
|
+
| Worker reference documents | Addressed worker; `_plurnk` EDIT through engine dispatch. | Creating or replacing a private entry is already an operation. |
|
|
335
382
|
| Git membership and disk materialization | Kernel `GitMembership` / entry CRUD. | Ingesting existing disk state is not a model-authored EDIT. |
|
|
336
383
|
| Disk-divergence narration | Kernel writes an EDIT-shaped `source=file` row to the `plurnk` log. | It reports an environment event honestly; no operation is fabricated as having run. |
|
|
337
384
|
| Search derivation and catalog render | Kernel. | They are indexes and read-only projections, not entry operations. |
|
|
@@ -341,33 +388,34 @@ Git membership includes tracked and untracked-but-not-ignored project files
|
|
|
341
388
|
({§membership-auto-add}); it does not stage them or run `git add`.
|
|
342
389
|
|
|
343
390
|
§turn0-agents-stunt **The project AGENTS.md is a turn-0 stunt.** When
|
|
344
|
-
`<projectRoot>/AGENTS.md` exists, LoopDocs materializes it as the
|
|
345
|
-
`worker
|
|
346
|
-
|
|
391
|
+
`<projectRoot>/AGENTS.md` exists, LoopDocs materializes it as the current worker's private
|
|
392
|
+
`worker://~/_plurnk/agents.md` entry and the engine foists one READ of it into
|
|
393
|
+
that model worker's first turn — visible, logged, line-addressable. Absent
|
|
347
394
|
file: no entry, no stunt, nothing 404s. The global XDG configuration `AGENTS.md`
|
|
348
395
|
remains system-prompt policy ({§policy-sections}); the stunt carries only
|
|
349
396
|
local repo guidance.
|
|
350
397
|
|
|
351
|
-
§actor-boundary-doc-injection **
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
398
|
+
§actor-boundary-doc-injection **Generated documents use the actor path.** The
|
|
399
|
+
project's `AGENTS.md`, skills, and Functionality references are materialized in
|
|
400
|
+
the addressed worker's `_plurnk/` subtree ({§worker-generated-subtree}) through
|
|
401
|
+
ordinary `_plurnk` operation turns. Their exact
|
|
402
|
+
EDIT and SEND programs remain durable in that worker's log; generated state is
|
|
403
|
+
neither a hidden database write nor a kernel-owned mirror.
|
|
357
404
|
|
|
358
405
|
§actor-boundary-catalog-preview **Catalog preview.** `PLURNK_SERVICE_FILES_ITEMS`
|
|
359
406
|
foists turn-0 discovery into the worker's first turn, so a worker opens with a
|
|
360
|
-
navigable map instead of blank. An enabled preview executes exactly
|
|
361
|
-
bodyless FIND surveys in order:
|
|
362
|
-
(worker
|
|
363
|
-
(`## FIND0 [+init,+skills] (worker
|
|
364
|
-
tool families (`## FIND0 [+init,+tools] (worker
|
|
365
|
-
|
|
407
|
+
navigable map instead of blank. An enabled preview executes exactly seven baseline
|
|
408
|
+
bodyless FIND surveys in order: enabled Agent Skills (`## FIND0 [+init,+skills]
|
|
409
|
+
(worker://~/_plurnk/skills/*.md) <1,-1>`), Plurnk-generated reference families
|
|
410
|
+
(`## FIND0 [+init,+skills] (worker://~/_plurnk/skills/plurnk/*.md) <1,-1>`), enabled
|
|
411
|
+
tool families (`## FIND0 [+init,+tools] (worker://~/_plurnk/tools/*.md) <1,-1>`),
|
|
412
|
+
enabled outbound agents (`## FIND0 [+init,+agents] (worker://~/_plurnk/agents/*.md)
|
|
413
|
+
<1,-1>`, {§a2a-agents-catalog}), project files (`## FIND0 [+init] (*)`), workspace commons (`## FIND0 [+init]
|
|
366
414
|
(worker:///*)`), and the worker's own space (`## FIND0 [+init] (worker://~/*)`).
|
|
367
415
|
The catalogs select every direct document independently of its authored body;
|
|
368
416
|
ordinary READ supplies its examples and complete instructions on demand. Their
|
|
369
417
|
log classifications make the opening discovery one `init` set while retaining
|
|
370
|
-
`skills` or `
|
|
418
|
+
`skills`, `tools`, or `agents` on the corresponding rows ({§log-item-tags}). A shallow
|
|
371
419
|
result renders direct entries normally and every deeper first-segment directory
|
|
372
420
|
as an actionable `dir/**` summary with its recursive `items` and `tokens`;
|
|
373
421
|
tool-family rows also carry the concise `{§scheme-catalog-summary}` that drives
|
|
@@ -382,7 +430,7 @@ direct-entry-plus-directory count; `-1` enables the ordinary markerless page;
|
|
|
382
430
|
unset / `0` disables previews. `log://` is absent because the current worker's
|
|
383
431
|
log already renders in present mode.
|
|
384
432
|
|
|
385
|
-
§worker-initialization-entry **
|
|
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, orienting READ/FIND, and terminal `SEND0 [102]` rows. Every row is structurally classified `_plurnk` and `init`. The PLAN is the canonical {§plan-value} with one `medium`, `in_progress` entry whose content is `Discover the tooling available and survey the workspace file root.`; 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.
|
|
386
434
|
|
|
387
435
|
### §machine-processes The machine and its processes: workspace, worker, fork
|
|
388
436
|
|
|
@@ -418,19 +466,28 @@ terminal history.**
|
|
|
418
466
|
| Project files ({§machine-processes-one-filesystem}) | Workspace | Shared live; a fork does not create another checkout. |
|
|
419
467
|
| Shared worker entries (`worker:///...`) | Workspace commons | Shared live. |
|
|
420
468
|
| Membership overlay ({§machine-processes-one-overlay}) | Workspace | Shared unchanged; divergent membership requires another workspace. |
|
|
421
|
-
| Log items ({§machine-processes-fork-copies-the-log}) | Worker | Rows, event identities, curation effects, tags, folded body intervals, and the matching observation cursor are copied as terminal history. |
|
|
469
|
+
| Log items ({§machine-processes-fork-copies-the-log}) | Worker | Rows, event identities, curation effects, tags, folded body intervals, 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. |
|
|
422
470
|
| §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. |
|
|
423
|
-
|
|
|
471
|
+
| §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. |
|
|
424
472
|
| Active loops, turns, and cancellation | Worker | Never copied as live work; inherited structure is terminal history, then a new loop starts. |
|
|
425
473
|
|
|
426
474
|
§machine-processes-worker-is-its-log **A worker's conversational memory of
|
|
427
475
|
the shared world is its log, with no hidden per-worker snapshot beside it.**
|
|
428
476
|
OPEN/FOLD changes canonical folded body intervals on that worker's rows ({§open-fold});
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
477
|
+
lineage activity and explicit commons broadcasts arrive as attributed log
|
|
478
|
+
entries ({§env-delta}). Worker-owned entries include deliberate scratch and
|
|
479
|
+
other private resources; their manifest declares whether a FORK snapshots,
|
|
480
|
+
rederives, or omits each class. They are not an invisible mirror of shared state. Workspace
|
|
481
|
+
addressability does not imply packet membership or ambient notification
|
|
482
|
+
({§actor-boundary-lineage-attention}).
|
|
483
|
+
|
|
484
|
+
§machine-processes-fork-pending-activity **A fork is a closed snapshot of the
|
|
485
|
+
parent's view.** It copies rows already materialized in the parent and inherits
|
|
486
|
+
parent-audience occurrences newer than the copied observation cursor through
|
|
487
|
+
the occurrence high-water captured by worker creation. Activity addressed to
|
|
488
|
+
the parent after that boundary is sibling activity, not fork history. Global
|
|
489
|
+
commons broadcasts remain live after the fork because the branch is then an
|
|
490
|
+
existing workspace worker in its own right.
|
|
434
491
|
|
|
435
492
|
§machine-processes-model-worker-readable **A worker's log is private to packets, not to the workspace.** Isolation ({§actor-boundary}) governs what an *actor* sees — its own worker, never a sibling's. It does not wall off the client interface: `readLog({ workspaceId, workerId })` may read any ownership-verified worker in that workspace, and `listWorkers` enumerates them. A client-interface module chooses the default worker from its own conversation binding. The read is observation, never packet membership — no actor sees it.
|
|
436
493
|
|
|
@@ -456,9 +513,9 @@ shared entries, and membership remain live and uncopied.
|
|
|
456
513
|
histories over one workspace are worker forks. A divergent project filesystem
|
|
457
514
|
or membership overlay requires a new workspace.
|
|
458
515
|
|
|
459
|
-
### §worker-scheme The worker:// scheme — the knowledgebase (commons, own space, named spaces
|
|
516
|
+
### §worker-scheme The worker:// scheme — the knowledgebase (commons, own space, named spaces) and worker control (spawn, irc, fork, terminate, cap, collect)
|
|
460
517
|
|
|
461
|
-
§worker-authority-carving **The authority names the OWNER:** `worker:///notes.md` is in the COMMONS — a shared blackboard; `worker://~/draft.md` is the calling worker's own private space; `worker://<name>/result.md` is a named worker's space
|
|
518
|
+
§worker-authority-carving **The authority names the OWNER:** `worker:///notes.md` is in the COMMONS — a shared blackboard; `worker://~/draft.md` is the calling worker's own private space; `worker://<name>/result.md` is a named worker's space. Storage keys the owner on the entries.owner_id column ({§entry-owner}) — the pathname is always the bare entry path, and a FIND's result paths re-apply the queried authority so the model sees the address it typed. `~` is the sole current-worker sigil and cannot be minted; `commons` and `plurnk` are internal worker names unavailable for minting. Every other mintable authority, including `self`, is a literal worker name ({§worker-name}).
|
|
462
519
|
|
|
463
520
|
§worker-name-minting **URI ingestion is permissive; worker minting is not.**
|
|
464
521
|
Every model/client worker-creation door applies the contracts-owned
|
|
@@ -472,9 +529,11 @@ continues to decompose other authorities without treating them as mintable.
|
|
|
472
529
|
| Any other spelling | Refused as `name-invalid` before lookup, insertion, or child startup. |
|
|
473
530
|
| Automatic name | Generated, then admitted through the same predicate. |
|
|
474
531
|
|
|
475
|
-
§worker-read-scope **Named spaces are ancestry-gated reads**: the reader is the owner or an ANCESTOR (the recursive parent_worker_id walk) — oversight flows down the tree, a parent reads `worker://child/result` across generations, a child cannot snoop upward, and an unknown name or unpermitted reader resolves 404 with no existence leak.
|
|
532
|
+
§worker-read-scope **Named spaces are ancestry-gated reads**: the reader is the owner or an ANCESTOR (the recursive parent_worker_id walk) — oversight flows down the tree, a parent reads `worker://child/result` across generations, a child cannot snoop upward, and an unknown name or unpermitted reader resolves 404 with no existence leak. Reserved runtime workers obey the same rule; there is no world-readable named space.
|
|
476
533
|
|
|
477
|
-
§worker-write-scoping **Writes are own-space-and-commons only**: a model writes `worker://~/` and `worker:///` — every named authority is read-only to it (403),
|
|
534
|
+
§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
|
+
|
|
536
|
+
§worker-generated-subtree **`_plurnk/` is Plurnk's generated subtree in every worker space.** Every Plurnk-generated per-Worker document lives under `worker://~/_plurnk/`: the project instructions (`_plurnk/agents.md`, with nested AGENTS.md files under `_plurnk/instructions/**` preserving their subtree paths — the standard's closest-file scope, materialized without any foisted READ or teaching), standard Agent Skills and Plurnk-generated references (`_plurnk/skills/**`), executable families (`_plurnk/tools/**`), and future family catalogs. The subtree is readable exactly like the rest of the space ({§worker-read-scope}) and writable only by the `_plurnk` writer tier: a model, client, or plugin EDIT, KILL, SEND 410, or COPY/MOVE destination whose pathname begins `/_plurnk/` is refused 403 `worker-generated-read-only`, in the commons as well as in own and named spaces. Its documents are materialized through ordinary `_plurnk` **maintenance** turns ({§actor-boundary-doc-injection}), so provenance is legible at the address and durable in the log — but a receipt answers an asker, and maintenance turns have none: their successful rows never render in the packet (failures remain visible), while READ over `log:///` recovers them exactly and the turn's self-FOLD keeps client waterfalls tidy. On FORK the subtree is never byte-copied; the child rederives it from its inherited Functionality ({§machine-processes-entry-inheritance}). A runtime's `resourcesPath` is relative to this root ({§tools-resource-materialization}). No world-readable kernel authority exists; there is no `worker://plurnk/`.
|
|
478
537
|
|
|
479
538
|
§worker-control-addressing **Only an exact authority-only address selects worker
|
|
480
539
|
control.** Control is same-workspace only ({§actor-boundary}). Generic URI
|
|
@@ -583,13 +642,14 @@ The remaining worker surfaces are:
|
|
|
583
642
|
- §worker-scheme-terminate **Terminate** — `## KILL0 (worker://<name>)` aborts a named worker and `## KILL0 (worker://~)` aborts the caller: every unresolved loop in that worker's subtree closes 499 and every subscription in the subtree tears down; a literal name with no worker is 404. Cancellation is structured: descendants cannot detach implicitly. The override to the fire-and-forget default is not a parent-power — whoever holds the address may end it; a worker left alone simply ends at its own SEND signal `200`.
|
|
584
643
|
- §worker-scheme-cap **Cap** — `PLURNK_SERVICE_WORKSPACE_WORKERS_MAX_ACTIVE` ceilings the *concurrent* active workers per workspace (a worker with a non-terminal loop); a spawn or fork past it fails hard (508 — no queue, no retry), irc exempt; `-1` disables it. The fork-bomb brake, sized for workspaces that live for months.
|
|
585
644
|
- §worker-scheme-collect **Collect** — a worker's loop reaching a terminal status
|
|
586
|
-
surfaces to its
|
|
645
|
+
surfaces to its direct parent as an ambient delta ({§env-delta}): a `SEND` from
|
|
587
646
|
`worker://<name>` carrying the loop's exact terminal operation result. A
|
|
588
647
|
**2xx deliverable is born OPEN** (its body
|
|
589
648
|
materialized into the parent's packet, not hidden behind a fold): a child's
|
|
590
649
|
success must reach the parent open and awakening, never a bodyless row. An
|
|
591
650
|
non-2xx result surfaces folded; a failure retains its exact status and Problem. Every death-path is stamped uniformly,
|
|
592
|
-
so no termination is silent; collection is
|
|
651
|
+
so no child termination is silent to its owner; collection is lineage
|
|
652
|
+
supervision, never a
|
|
593
653
|
verb. The **pull** side mirrors the push: a path-absent
|
|
594
654
|
`## READ0 (worker://<name>)` collects that same result on demand for a
|
|
595
655
|
concluded worker; a worker **still running** has not delivered, so the READ
|
|
@@ -602,11 +662,11 @@ The remaining worker surfaces are:
|
|
|
602
662
|
turn the packet's status clump surfaces the live things this worker currently
|
|
603
663
|
holds — open streams (`## Child Streams`) and unconcluded child workers
|
|
604
664
|
(`## Active Child Workers`) — as terse `* <status> <path>` pointers (the same
|
|
605
|
-
shape as the errors section), just above it.
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
665
|
+
shape as the errors section), just above it. Folded child activity is durable
|
|
666
|
+
history; this clump is the current inventory that keeps an active obligation
|
|
667
|
+
visible even when no new activity arrived. It is orienting state, never
|
|
668
|
+
advice: the model sees its live subtree (`* 102 worker://worker-x`, `* active
|
|
669
|
+
sh:///1/2/3`) and reasons for itself — READ/OPEN/KILL via the path.
|
|
610
670
|
Empty sections are omitted, like errors.
|
|
611
671
|
|
|
612
672
|
### §worker-loop-lifecycle Worker and loop lifecycle: drain, reap, and passive wake
|
|
@@ -629,6 +689,12 @@ stateDiagram-v2
|
|
|
629
689
|
|
|
630
690
|
§worker-lifecycle-state-machine The lifecycle store admits only the guarded transitions shown above: `100 → 102`, `102 → 202`, `202 → 100`, and any unresolved state (`100`, `102`, `202`) to a terminal status. Terminal state is immutable. `DrainSupervisor` owns claim, wake, and cancellation; the dispatcher owns model-requested park/conclusion; the daemon owns boot-recovery orchestration; and the engine owns policy terminals. A racing transition that loses observes the durable winner; it does not overwrite it or report the requested state as fact.
|
|
631
691
|
|
|
692
|
+
§worker-lifecycle-live **Worker liveness is existential, not latest-state.** A
|
|
693
|
+
Worker is live while ANY of its loops is unresolved (`100`, `102`, or `202`). A
|
|
694
|
+
newer terminal loop cannot mask older queued, running, or parked work. Name
|
|
695
|
+
collision, workspace worker caps, child obligations, orientation, and recovery
|
|
696
|
+
all use that one definition.
|
|
697
|
+
|
|
632
698
|
§stream-catalog-lifecycle Streams are independently durable subscriptions owned by a worker. Payload and
|
|
633
699
|
lifecycle are orthogonal: zero bytes is a valid payload for both success and
|
|
634
700
|
failure, while the closed subscription and its status are the terminal fact.
|
|
@@ -741,7 +807,7 @@ boundary.
|
|
|
741
807
|
- §worker-lifecycle-total-reap **Cancellation is recursive and reaps every held stream.** `loop.cancel`, worker `KILL`, shutdown, and a worker's SEND signal `499` terminalize every unresolved loop in the cancelled worker subtree and iterate each worker's durable open-subscription rows, invoking each exact callable owner from the process-local live registry. The durable rows answer *what is held*; the live registry answers *how this process tears it down*; the abort signal is a fast-path optimization. There is no implicit detachment. Before shutdown awaits drains, it cancels every process-local proposal waiter through {§proposal-cancel-aborts} with outcome `daemon_stopping`, so a stopped-world dispatch cannot hold teardown open. A stream that is running, mid-spawn (its row written before it is killable), or spawned after the cancel is reaped alike. The teardown abort is bounded: the executor sends a polite signal then SIGKILL after a consumer-set grace (`PLURNK_SERVICE_EXEC_KILL_GRACE_MS`). A model `## KILL0 [code]` on one live stream instead delivers exactly that signal once (bare KILL uses the executor's SIGHUP default; `## KILL0 [9]` uses SIGKILL).
|
|
742
808
|
- §worker-lifecycle-exec-epoch-bound **A stream's kill binds to the scope it captured at spawn.** A stream captures the worker's cancellation scope as it registers and wires its kill to it, re-checking `aborted` AFTER wiring — no check-then-listen gap can drop an abort that lands mid-registration. Because the scope is replaced only once aborted, a captured-then-replaced scope is necessarily already aborted, so replacement never strands a live stream.
|
|
743
809
|
- §worker-lifecycle-no-resurrection **A cancelled worker is not resurrected by its own torn-down work.** A stream conclusion delivered to a cancelled, idle worker starts no fresh drain: an aborted (499) conclusion is skipped, and a straggler that concluded cleanly surfaces its deliverable as an environment delta ({§env-delta}), never a revived loop. The cancel was deliberate; only an explicit `runLoop` request resumes the worker.
|
|
744
|
-
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.**
|
|
810
|
+
- §worker-lifecycle-wake-liveness **A stream conclusion always reaches its worker.** The stream first persists its terminal state. A worker **blocked on a 202 wait** for that stream ({§wait-obligation-matrix}) then **awakens that loop in place** — the blocked loop *is* the continuation, so there is no fresh loop and no summary-as-prompt fiction. An already-active worker needs no injected prompt or second wake because its next packet reads the durable terminal state. A concluded worker receives no synthetic loop from ambient stream closure. The result remains available in the stream's own state under every case.
|
|
745
811
|
- §worker-lifecycle-child-wake **A child worker concluding wakes a parent blocked on it — the topology join.** `worker://` spawn/fork records `parent_worker_id` ({§lifecycle-terms}). When a worker's drain exits having **concluded** — no `202`-blocked loop, no open stream — `DrainSupervisor.#onDrainExit` resumes its parent **in place** through the shared `#wakeParkedWorker`, the same 202→100 resume a stream conclusion uses. So a parent that spawns work and blocks with SEND signal `202` is woken the moment its child finishes; on resume it reads the child's deliverable from the {§worker-scheme-collect} delta in its own log — a control edge, **never an injected prompt**. The wake recurses upward via the parent's own drain-exit. A child still running — or itself blocked at 202 — is not *concluded*, so it does not wake the parent (it's still a live thing the subtree holds). This is the structured-concurrency join: streams and child workers are the same kind of "live thing a worker holds," driving premature-terminate ({§send-premature-terminate}), the wake edge, and the collect delta identically. A worker conclusion is a **bounded, un-loseable** wake: if the conclusion fires while the parent is mid-turn (before its block commits), `#wakeParkedWorker` finds it not-yet-slept and records an **owed wake**, which the drain honors when the parent blocks — so a wait awaiting workers **always returns**, never dead-blocks on a conclude-before-block race. (Only a live exec stream, unbounded absent a timeout, may legitimately hold a wait open.)
|
|
746
812
|
- §worker-optimistic-settlement **Asynchronous settlement receives one bounded worker-local opportunity before model dispatch.** An initiating turn lets only the streams it started settle before its terminal SEND; separately, a stream or direct-child conclusion persists and publishes immediately but holds the parked worker's single `202→100` requeue while another stream or direct child remains live. Both use `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`, shipped at five seconds; zero disables the opportunity. The wake hold ends as soon as no sibling obligation remains, never extends its original deadline, and coalesces every conclusion that lands within it into one requeue. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. A conclusion that lands after provider dispatch begins retains its next wake, while poll, park-deadline, prompt, and operator wakes never open this hold. Only packet/provider dispatch waits: terminal state, client events, cancellation, and child execution do not. One redaction-safe span records elapsed time, quiescence versus deadline, and conclusion count without entering the packet.
|
|
747
813
|
- §worker-lifecycle-idle-is-concluded **An idle worker concludes; it does not park.** A loop is idle only when it has neither live obligations nor completed results awaiting their first packet. A live child or stream blocks a SEND signal `202` join; a completed stream, child result, or same-turn retrieval continues directly to the next packet where it is observed. Only after those sets are drained does signal `202` resolve like signal `200`. There is no held-open idle loop and no `loop/quiesced` soft signal. A concluded worker is durable working history and an addressed arrival reawakens it as a new loop.
|
|
@@ -914,21 +980,25 @@ Author-facing contract: [`@plurnk/plurnk-schemes`](../plurnk-schemes/SPEC.md). B
|
|
|
914
980
|
|
|
915
981
|
When an op carries a target, RFC 3986 supplies the component model and WHATWG
|
|
916
982
|
URL supplies canonical decomposition; an entry key is
|
|
917
|
-
`(
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
983
|
+
`(owner, scheme, authority, pathname)`; the owner Worker determines the
|
|
984
|
+
workspace ({§entry-identity-no-null}).
|
|
985
|
+
The registered manifest's {§manifest-authority} disposition determines the one
|
|
986
|
+
meaning of an authored URI authority before any entry capability is exposed:
|
|
987
|
+
|
|
988
|
+
- §scheme-address-namespace-fold A **namespace scheme** mechanically folds its authored authority into the canonical storage pathname and persists the empty entry authority. For an entry tree, the authored authority is therefore a leading path segment rather than a separate resource coordinate.
|
|
989
|
+
- An **owner scheme** consumes its authored authority while resolving `entries.owner_id` and persists the empty entry authority. `worker` uses this disposition ({§worker-authority-carving}).
|
|
990
|
+
- A **resource scheme** preserves its canonical authority as the durable entry-authority coordinate. Every capability and exact query is bound to that authority; it cannot collide with or observe the same pathname at another authority.
|
|
922
991
|
- §scheme-address-network A **network resource** uses the shared schemes-layer
|
|
923
992
|
normalization contract {§network-address}:
|
|
924
993
|
`https://example.com:8443/page?b=2&a=1` →
|
|
925
|
-
`(https,
|
|
994
|
+
`(https, example.com:8443, /page?b=2&a=1)`. The exact protocol, canonical
|
|
926
995
|
host, non-default port, path, and serialized query are identity; query order,
|
|
927
996
|
duplicates, and an explicit empty `?` survive. A fragment is a Plurnk channel
|
|
928
997
|
selector, not network identity or transport. URL userinfo is rejected and
|
|
929
|
-
request metadata never enters identity. `
|
|
998
|
+
request metadata never enters identity. Plain `http` routes through `https`,
|
|
930
999
|
just as `ws` routes through `wss`; those implementation aliases never alias
|
|
931
|
-
resources
|
|
1000
|
+
resources, and the secure face is the one taught — `http` stays supported
|
|
1001
|
+
for the endpoint that requires it, never advertised as a peer. `SchemeCtx.entries` binds every cap to the addressed protocol.
|
|
932
1002
|
Absolute network URLs are single resources even when their path ends `/` —
|
|
933
1003
|
folder/glob expansion belongs to entry namespaces, never an HTTP origin.
|
|
934
1004
|
- The **`file` class is the workspace filesystem** — a mount namespace with its own resolution and naming law, specified below.
|
|
@@ -936,37 +1006,83 @@ routing and resource identity are separate:
|
|
|
936
1006
|
§client-entry-address A client entry read carries the observing `workerId` and
|
|
937
1007
|
passes its selector through the registered data scheme's
|
|
938
1008
|
{§entry-address-resolution} before querying storage. The scheme returns its
|
|
939
|
-
canonical pathname and semantic owner; core alone resolves that
|
|
940
|
-
`entries.owner_id` and queries the complete `(
|
|
941
|
-
pathname)` identity. Worker and capability-stream authorities reuse their
|
|
1009
|
+
canonical authority, pathname, and semantic owner; core alone resolves that
|
|
1010
|
+
owner to `entries.owner_id` and queries the complete `(owner, scheme,
|
|
1011
|
+
authority, pathname)` identity. Worker and capability-stream authorities reuse their
|
|
942
1012
|
ancestry checks, so unknown or unauthorized owners return the same 404 and
|
|
943
1013
|
cannot select an arbitrary colliding row. The result is the contracts-owned
|
|
944
1014
|
{§entry-read-result}; persistence columns never cross the seam.
|
|
945
1015
|
|
|
1016
|
+
§scheme-entry-matrix URI authority, entry principal, access, and fork
|
|
1017
|
+
inheritance are independent decisions. The built-in surfaces declare them
|
|
1018
|
+
explicitly:
|
|
1019
|
+
|
|
1020
|
+
| Surface | URI authority | Entry principal | Cross-Worker read | FORK entry disposition |
|
|
1021
|
+
|---|---|---|---|---|
|
|
1022
|
+
| Project file | Filesystem name | `commons` | Workspace-shared | Shared live; no copy |
|
|
1023
|
+
| `worker:///...` | Empty selects commons | `resolved` commons | Workspace-shared | Shared live; no copy |
|
|
1024
|
+
| `worker://~/...` | `~` selects caller | `resolved` Worker | Self; parent may use the child's literal name | Quiescent snapshot; `_plurnk/**` rederived |
|
|
1025
|
+
| `worker://<name>/...` | Literal Worker selector | `resolved` Worker | Owner or ancestor | Scheme disposition only when the selected owner is the fork source; otherwise no copy |
|
|
1026
|
+
| `prompt:///...` | Loop-relative coordinate | Calling Worker | Self-only | Quiescent snapshot |
|
|
1027
|
+
| Provisional `skill:///...` | Entry namespace | Calling Worker | Self-only | Quiescent snapshot |
|
|
1028
|
+
| `http(s)://...` | Remote resource identity | Calling Worker | Self-only | Quiescent snapshot; an active stream is omitted |
|
|
1029
|
+
| `wss://...` | Remote resource identity | Calling Worker | Self-only | None |
|
|
1030
|
+
| Executor/MCP output | Optional named actor selector | `resolved` Worker | Owner or ancestor | None |
|
|
1031
|
+
| `a2a://...` outbound resource | Remote-agent identity | Calling Worker | Self-only | None |
|
|
1032
|
+
|
|
1033
|
+
A resolved-owner scheme authorizes the actor selector before returning its
|
|
1034
|
+
principal; a numeric Worker id is never extension input. `worker` ownership has
|
|
1035
|
+
no cross-actor form. Thus identical HTTP, WSS, A2A, prompt, Skill, or unqualified
|
|
1036
|
+
executor addresses in independent root Workers are distinct resources without
|
|
1037
|
+
render-time filtering.
|
|
1038
|
+
|
|
946
1039
|
§fs-namespace **The workspace is a mount namespace; `project_root` is the model's `/`.** Chroot semantics: host paths do not exist inside the jail, and no engine surface folds a host-absolute spelling onto a member. The root is **fixed immutably at workspace creation** (headless is forever); the namespace's mount table changes only through the declared membership overlay ({§membership}), never by re-rooting. At `project_root = /` the jail is the whole filesystem and every rule below degenerates to identity — the design's proof case, and the common benchmark topology.
|
|
947
1040
|
|
|
948
1041
|
§fs-namei **Resolution is namei over the mount table.** The model's CWD is permanently `/`, so `src/x.md` and `/src/x.md` are the same name — the slash rule is a corollary, never a legislated equivalence. Resolution is lexical: `.` and `..` resolve before anything touches storage (`..` is legal *during* traversal); the final name lands in the root subtree (a bare key), on a declared outside-root mount (a `../`-prefixed key — the git-style overlay), or names nothing (404 carrying the resolved form). Containment is the resolution semantics — there is no separate traversal check to forget.
|
|
949
1042
|
|
|
950
1043
|
§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.
|
|
951
1044
|
|
|
952
|
-
§fs-visibility-grantors **
|
|
953
|
-
|
|
954
|
-
§fs-write-surface **The write surface —
|
|
955
|
-
|
|
956
|
-
|
|
|
957
|
-
|
|
958
|
-
|
|
|
959
|
-
|
|
|
960
|
-
|
|
|
961
|
-
|
|
|
962
|
-
|
|
|
963
|
-
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
1045
|
+
§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 `pick` constraint. A `pick` is either explicit client policy or the exact, inspectable record of an accepted creation ({§fs-create-generated-pick}); 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 `pick` admits does not exist for the model and cannot be overwritten.
|
|
1046
|
+
|
|
1047
|
+
§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`.
|
|
1048
|
+
|
|
1049
|
+
| Case | Required admission | Accepted result |
|
|
1050
|
+
|------|--------------------|-----------------|
|
|
1051
|
+
| §fs-create-disabled Absent path, effective scope `none` | None | Refuse without touching disk. |
|
|
1052
|
+
| §fs-create-root Absent path inside `project_root` | Effective scope `root` or `namespace`; no matching `hide` or `view` | Exclusive CREATE (`open(O_CREAT\|O_EXCL)` semantics), then incorporation below. |
|
|
1053
|
+
| §fs-create-namespace Absent canonical `../` path | Effective scope `namespace`; no matching `hide` or `view` | Exclusive CREATE, then an exact `pick` so the outside member is read-write. |
|
|
1054
|
+
| §fs-create-ignored Absent path ignored by active Git | Matching **explicit** `pick` | Exclusive CREATE through that pick; an automatic/generated pick never overrides Git ignore. |
|
|
1055
|
+
| §fs-create-git Absent in-root path admitted by active Git | Not ignored | Exclusive CREATE followed by successful `git add -- <path>`; a staging failure falls back to an exact generated pick. |
|
|
1056
|
+
| §fs-create-pick Absent admitted path without Git incorporation | Existing explicit pick or automatic incorporation permitted | Exclusive CREATE followed by an exact generated pick when no explicit pick already covers it. |
|
|
1057
|
+
| §fs-write-member Existing in-root member | Git or pick membership; no matching `view` | Proposal-gated EDIT. |
|
|
1058
|
+
| §fs-write-outside Existing canonical `../` member | Pick membership; no matching `view` | Proposal-gated EDIT. Git-only outside members are read-only. |
|
|
1059
|
+
| §fs-write-nonmember Existing non-member | None | Refuse; reveal occupancy only, never content. |
|
|
1060
|
+
|
|
1061
|
+
§fs-create-incorporation **Creation incorporation is durable workspace state, not a transient entry exception.** `workspace_constraints.source` distinguishes an operator/client-authored `explicit` constraint from a runtime-authored `create` constraint. An explicit row interprets `glob` as a pattern; a generated row interprets the same field as one literal canonical path, so legal filename metacharacters never become wildcard syntax. Generated constraints appear through the ordinary `workspace.constraints` client surface. An explicit pick covering the same path always wins and is never demoted.
|
|
1062
|
+
|
|
1063
|
+
| Event | Generated-pick lifecycle |
|
|
1064
|
+
|-------|--------------------------|
|
|
1065
|
+
| §fs-create-generated-pick Successful creation not incorporated by Git or an existing explicit pick | Insert exact `{ effect: "pick", glob: canonicalPath, source: "create" }`. |
|
|
1066
|
+
| §fs-create-copy COPY to a new path | Incorporate the destination independently; the source is unchanged. |
|
|
1067
|
+
| §fs-create-move MOVE to a new path | Incorporate the destination, then remove the source's generated exact pick after deleting the source. |
|
|
1068
|
+
| §fs-create-kill KILL or accepted whole-resource deletion | Remove the deleted path's generated exact pick. |
|
|
1069
|
+
| §fs-create-explicit-promotion Explicit `pick` added at the same exact path | Promote/retain the row as `source: "explicit"`; later automatic cleanup cannot remove it. |
|
|
1070
|
+
| §fs-create-masked A later `hide` or active Git-ignore rule excludes a generated-pick member | Preserve the generated pick as dormant policy; removing the exclusion restores membership when the file still exists. A later `view` leaves it visible but read-only. |
|
|
1071
|
+
| §fs-create-ambient-delete Reconciliation confirms a generated-pick path disappeared outside Plurnk | Remove the generated exact pick; explicit picks remain operator policy. |
|
|
1072
|
+
|
|
1073
|
+
The file-creation invariants are deliberately redundant with the matrices only
|
|
1074
|
+
where the invariant closes an architectural failure mode:
|
|
1075
|
+
|
|
1076
|
+
- §file-create-no-orphans A successful create always ends in Git membership or a real pick; no accepted file is orphaned from the workspace that created it.
|
|
1077
|
+
- §file-create-no-clobber Creation is exclusive and an existing non-member remains unreadable and non-overwritable.
|
|
1078
|
+
- §file-create-exclusions-win `hide` and `view` outrank all automatic creation; active Git ignore is overridden only by an explicit pick.
|
|
1079
|
+
- §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.
|
|
1080
|
+
- §file-create-producer-neutral The file contract depends on the operation and target, never the producer identity.
|
|
1081
|
+
- §file-create-single-owner File membership owns prospective admission, incorporation choice, and generated-pick lifecycle; file operations consume that decision rather than re-deriving Git and constraint policy.
|
|
1082
|
+
- §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.
|
|
1083
|
+
|
|
1084
|
+
Refusing an occupied non-member follows the POSIX exclusive-create precedent:
|
|
1085
|
+
namespace occupancy is not secret, but content remains dark.
|
|
970
1086
|
|
|
971
1087
|
§fs-answer-in-canon **The engine answers in canon.** Every engine-authored address — log-row pathname columns, rx spans and error facts, FIND results, the catalog, the foists — renders the one canonical form: exactly what `git ls-files --full-name` prints, byte-for-byte on the git-membership subset. A miss names the RESOLVED form, never an echo of the model's spelling. The single verbatim survivor is the model's own emission text — history is never rewritten. There is no shadow universe of model-preferred addressing.
|
|
972
1088
|
|
|
@@ -983,7 +1099,7 @@ Every fact names the canonical key, never the host root or an echo of the
|
|
|
983
1099
|
model's spelling. These classes let a caller distinguish a wrong address, an
|
|
984
1100
|
invalid range, read-only authority, and occupied hidden state without guessing.
|
|
985
1101
|
|
|
986
|
-
§fs-world-state **The world-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting world. `WorldState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, channel orphan-freedom, the closed admission set (
|
|
1102
|
+
§fs-world-state **The world-state harness — coverage that closes the class.** Op-outcome tests check what an op returned; the harness checks the resulting world. `WorldState.check(db)` asserts, pure-db and read-only: identity uniqueness in practice (no tuple holds two rows), the canonical fixpoint on every file-class key, channel orphan-freedom, the closed admission set (every file row's origin is Git or constraint), and sig-coherence. Generated-pick incorporation and lifecycle require filesystem/Git evidence and are covered by the composed creation matrix rather than a false pure-database proxy. The harness runs as a lifecycle-test epilogue and at every soak turn boundary, where the delta half applies: an idle turn grows the entries table by ZERO. A violation names its law and its row.
|
|
987
1103
|
|
|
988
1104
|
### §scheme-manifest Manifest
|
|
989
1105
|
|
|
@@ -1098,7 +1214,7 @@ Engine → scheme guarantees:
|
|
|
1098
1214
|
- `ctx` is fresh per call. No mutation across calls.
|
|
1099
1215
|
- §universal-read-composition **Exact READ has one composition.** Core resolves
|
|
1100
1216
|
canonical identity and owner once, gives a data scheme its optional
|
|
1101
|
-
`prepareRepresentation({ target, pathname })` opportunity, reads the complete
|
|
1217
|
+
`prepareRepresentation({ target, authority, pathname })` opportunity, reads the complete
|
|
1102
1218
|
canonical channels, selects the authored channel, applies binary and
|
|
1103
1219
|
text-coordinate rules, and finally composes that channel's durable producer
|
|
1104
1220
|
result. Preparation receives neither fragment nor `lineMarker`; finite work
|
|
@@ -1253,7 +1369,7 @@ flowchart LR
|
|
|
1253
1369
|
G --> Q
|
|
1254
1370
|
```
|
|
1255
1371
|
|
|
1256
|
-
§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.
|
|
1372
|
+
§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.
|
|
1257
1373
|
|
|
1258
1374
|
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.
|
|
1259
1375
|
|
|
@@ -1449,9 +1565,9 @@ OPEN/FOLD operate on the **log** (`log:///`) - the model's context-curation surf
|
|
|
1449
1565
|
|
|
1450
1566
|
### §jsonplurnk The Log's wire format
|
|
1451
1567
|
|
|
1452
|
-
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. `
|
|
1568
|
+
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 a non-SEND row — SEND always carries its submit code 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}.
|
|
1453
1569
|
|
|
1454
|
-
- §packet-token-accounting Every row reports its real weight so the packet self-reconciles against the budget: `
|
|
1570
|
+
- §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}
|
|
1455
1571
|
|
|
1456
1572
|
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
1457
1573
|
|
|
@@ -1569,10 +1685,10 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
1569
1685
|
AST: `{ op: "FIND", target (scope), body: MatcherBody | null (predicate), signal: tags | null, lineMarker? }`.
|
|
1570
1686
|
|
|
1571
1687
|
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does. Terminal `*` and `**` are structural catalog selectors and include dot-prefixed entries, so a complete map does not hide `.env.defaults` or `.github`; richer patterns retain native shell behavior. SQLite prefix queries may reduce the candidate set but never decide the match. A trailing slash is a recursive FIND scope only for a scheme whose manifest declares `folderScopes: true`; otherwise it is ordinary resource syntax. This is an explicit plugin contract, never inferred from URL punctuation.
|
|
1572
|
-
- An exact target resolves to the same canonical `(scheme, pathname)` identity
|
|
1688
|
+
- An exact target resolves to the same canonical `(scheme, authority, pathname)` identity
|
|
1573
1689
|
as READ, entry CRUD, and any preceding `prepareFind()`. URI authorities are
|
|
1574
1690
|
identity-bearing: `https://example.com/page` queries
|
|
1575
|
-
`(https,
|
|
1691
|
+
`(https, example.com, /page)`, never an empty-authority row at `/page`.
|
|
1576
1692
|
- §find-channel-selection The target selects a channel under {§channel-selection}. That channel controls candidate eligibility, every matcher dialect's content or derivation, match-evidence coordinates, and exact producer-result composition. A selected channel absent from an exact entry is 404; a broad scope simply excludes entries lacking it. Successful resource-mode results remain complete default-first channel groups, so sibling channels are navigable catalog metadata rather than additional matches.
|
|
1577
1693
|
- §find-glob-filter-on-content `body` matcher operates on the addressed entry channel (glob/regex/jsonpath/xpath), per `plurnk.md` "Pattern Filtering"; the path-glob lives in the (target), not the body.
|
|
1578
1694
|
- §find-semantic-selection Every matcher operates only over the candidate set selected by `(target)`; relation matchers do not bypass that selection. Semantic ranking is exhaustive within that candidate set, then applies the ordinary FIND result scope. Markerless semantic FIND therefore uses the same `<1,16>` default as every other matcher. Integers retain FIND's positional contract: `<N>` selects result N and `<N,M>` selects the inclusive range. A leading decimal first applies a minimum cosine-similarity threshold; following integers select positions within that ranked threshold set. Thus `<0.7,10,20>` means threshold 0.7 followed by results 10 through 20, while `<0.7>` applies the threshold and the ordinary first-16 page.
|
|
@@ -1674,7 +1790,7 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1674
1790
|
|
|
1675
1791
|
- §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.
|
|
1676
1792
|
- §send-premature-terminate **Premature terminate — the pending set.**
|
|
1677
|
-
|
|
1793
|
+
A model's completion claim is gated by one rule: *nothing pending may be silently
|
|
1678
1794
|
discarded*. Pending work has two states: **live obligations** (open
|
|
1679
1795
|
streams/spawns and live child workers) and **completed-but-unobserved
|
|
1680
1796
|
results** (same-turn READ/FIND/OPEN results, failed operations, terminal
|
|
@@ -1684,6 +1800,11 @@ the loop continue; repeated offenses terminate through the engine's 500.
|
|
|
1684
1800
|
and the loop continues; every refusal strikes uniformly, including a
|
|
1685
1801
|
retrieval-only refusal. The pending kind changes the corrective message, not
|
|
1686
1802
|
rail accounting. `[499]` deliberately abandons regardless.
|
|
1803
|
+
- §send-administrative-terminal **An administrative terminal closes its own
|
|
1804
|
+
transaction.** A client, plugin, or `_plurnk` operation program runs in its
|
|
1805
|
+
own administrative loop. Its SEND signal `200` concludes exactly that loop;
|
|
1806
|
+
it neither claims nor consumes the Worker's model-visible pending set. Model
|
|
1807
|
+
completion rails therefore apply only to a model-authored disposition.
|
|
1687
1808
|
- §send-undelivered-child-term **Completion is not delivery.** A result becomes
|
|
1688
1809
|
observed only after crossing a packet boundary. SEND signal `202` parks only on
|
|
1689
1810
|
live obligations. If work has completed but is unobserved, it continues
|
|
@@ -1766,18 +1887,18 @@ effect-qualified hold policy. The post-acceptance materialization path never
|
|
|
1766
1887
|
triggers reclassification.
|
|
1767
1888
|
|
|
1768
1889
|
§exec-registry-resolves The runtime slot (`signal`) selects an executor from
|
|
1769
|
-
the current
|
|
1890
|
+
the current worker snapshot. Installed siblings form the immutable base:
|
|
1770
1891
|
they are discovered and probed at startup, and availability is cached.
|
|
1771
|
-
|
|
1772
|
-
{§module-
|
|
1773
|
-
independent
|
|
1892
|
+
Worker Functionality providers may atomically overlay additional names under
|
|
1893
|
+
{§module-worker-capabilities}; a name has one owner within a worker, while
|
|
1894
|
+
independent workers may use the same name. An absent or empty tag selects
|
|
1774
1895
|
`sh`; a non-empty tag selects exactly that registered executable tool. Unknown
|
|
1775
1896
|
tags are refused 501 with direction to use only the advertised catalogue or
|
|
1776
1897
|
put a complete command in bare `EXEC`; they are never reinterpreted as shell
|
|
1777
1898
|
command words. An unavailable runtime is also 501 and carries the probe
|
|
1778
1899
|
`detail`.
|
|
1779
1900
|
|
|
1780
|
-
For a family runtime, `ExecutorRegistry.toolRegistry(tag,
|
|
1901
|
+
For a family runtime, `ExecutorRegistry.toolRegistry(tag, workerId)`
|
|
1781
1902
|
validates the one executor-owned snapshot used by packet presentation,
|
|
1782
1903
|
dispatch admission, and pull-document materialization. Core performs no
|
|
1783
1904
|
protocol discovery while building a packet and has no alternate tool
|
|
@@ -1820,7 +1941,17 @@ edge. Stream closure remains a wake edge under every poll policy.
|
|
|
1820
1941
|
|
|
1821
1942
|
§exec-host-proposes **Effect-gating.** Each executor declares an `effect` (`pure` | `read` | `host`); the service maps it to policy (`EffectPolicy`). A `host` runtime (subprocess; file-backed sqlite) mutates the host → **propose** (lifecycle {§proposal}): the worker waits for a human gate, then spawns and writes stdout/stderr to channels of a `<runtime>:///<loop>/<turn>/<seq>` entry (the runtime tag is the URI scheme, {§exec}; the coordinate matches the op's log-row coordinate, e.g. `sh:///1/1/2`), returning `102 Processing` immediately. Channel state transitions (`active` → `closed`/`errored`) drive what the model sees at subsequent turn boundaries ({§channel-state}).
|
|
1822
1943
|
|
|
1823
|
-
§entry-owner **Every entry
|
|
1944
|
+
§entry-owner **Every entry has one structurally bound principal.**
|
|
1945
|
+
`entries.owner_id` is a real Worker row and part of the identity key: the
|
|
1946
|
+
workspace's reserved `commons` Worker for deliberately shared content, the
|
|
1947
|
+
effective Worker for private resources, or the exact authorized Worker selected
|
|
1948
|
+
by a resolved-owner scheme. It is never nullable or supplied by a plugin call.
|
|
1949
|
+
Core binds it once through {§entry-address-resolution} before exposing any
|
|
1950
|
+
entry, channel, notification, subscription, mutation, proposal, client-read, or
|
|
1951
|
+
cancellation capability. URI authority remains a separate coordinate; a scheme
|
|
1952
|
+
may use a semantic Worker name there, but the private numeric id never appears
|
|
1953
|
+
in a URI or packet. `plurnk` and `commons` are reserved Workers, and `~` is the
|
|
1954
|
+
current-Worker sigil; none can be minted by a spawn or client.
|
|
1824
1955
|
|
|
1825
1956
|
§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 gated by ancestry (the reader is the owner or an ancestor; oversight flows down the tree, unknown-or-unpermitted resolves 404 with no existence leak). 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.
|
|
1826
1957
|
|
|
@@ -1849,14 +1980,18 @@ from each owned channel as an `origin=_plurnk` READ at
|
|
|
1849
1980
|
| `application/jsonl` or `application/x-ndjson` | complete newline-terminated records | every remaining record |
|
|
1850
1981
|
| every other mimetype | nothing | the complete atomic document |
|
|
1851
1982
|
|
|
1852
|
-
The per-channel cursor advances only through content
|
|
1853
|
-
active atomic document or trailing partial JSONL
|
|
1854
|
-
as malformed structured data.
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1983
|
+
The durable per-subscription, per-channel cursor advances only through content
|
|
1984
|
+
actually published, so an active atomic document or trailing partial JSONL
|
|
1985
|
+
record never reaches the model as malformed structured data. Its transition and
|
|
1986
|
+
the generated READ commit atomically. Ongoing observations are folded and a
|
|
1987
|
+
terminal observation is born OPEN; a terminal state with no newly publishable
|
|
1988
|
+
body still produces one bodyless conclusion row. OPEN, FOLD, or KILL may curate
|
|
1989
|
+
that log row without rewinding the cursor or publishing the terminal result
|
|
1990
|
+
again; the exact terminal result and channel content remain READable at the
|
|
1991
|
+
stream address. Every READ then obeys {§body-projection} and therefore renders
|
|
1992
|
+
its selected result complete. A stream that closes before a same-turn wait
|
|
1993
|
+
remains pending until every selected channel's terminal READ crosses the next
|
|
1994
|
+
packet boundary. The EXEC row separately records the authored invocation.
|
|
1860
1995
|
|
|
1861
1996
|
`## KILL0 (<runtime>:///<loop>/<turn>/<seq>)` cancels an active subprocess via
|
|
1862
1997
|
the subscription registry's stored controller. A terminal stream is immutable:
|
|
@@ -1899,11 +2034,10 @@ Core derives the contracts-owned `ProposalProjection` from the durable proposed
|
|
|
1899
2034
|
| Projection field | Durable authority |
|
|
1900
2035
|
| --------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
|
1901
2036
|
| identity | proposed log row `id`, `worker_id`, `loop_id`, `turn_id`, and validated `op` |
|
|
1902
|
-
| `target` | canonical `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target
|
|
2037
|
+
| `target` | canonical `{ scheme, authority, pathname }` from `attrs.proposalTarget` for staged COPY/MOVE, otherwise the log row target |
|
|
1903
2038
|
| `body` | proposed operation result `rx.body`; absent means the empty review body |
|
|
1904
2039
|
| `attrs` | proposed log row `attrs` object |
|
|
1905
2040
|
| `flags` | validated persisted loop flags expanded over contracts-owned defaults |
|
|
1906
|
-
| `staleClobberRisk` | target-matched ambient file divergence in the proposal's worker and turn |
|
|
1907
2041
|
| `disposition` | {§proposal-disposition}; the same value drives automatic settlement and client presentation |
|
|
1908
2042
|
|
|
1909
2043
|
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.
|
|
@@ -1929,12 +2063,11 @@ removes ownerless rows without fabricating cancellation, payload, or replay.
|
|
|
1929
2063
|
|
|
1930
2064
|
`ProposalDisposition` is either `{ owner: "client" }` or `{ owner: "loop", decision: "accept" | "reject", outcome? }`. The decision table is complete and ordered:
|
|
1931
2065
|
|
|
1932
|
-
| `auto` |
|
|
1933
|
-
| ------ |
|
|
1934
|
-
| true |
|
|
1935
|
-
|
|
|
1936
|
-
| false |
|
|
1937
|
-
| false | any | false | client |
|
|
2066
|
+
| `auto` | `noProposals` | Disposition |
|
|
2067
|
+
| ------ | ------------- | ---------------------------------------- |
|
|
2068
|
+
| true | any | loop accept |
|
|
2069
|
+
| false | true | loop reject, outcome `no_review_channel` |
|
|
2070
|
+
| false | false | client |
|
|
1938
2071
|
|
|
1939
2072
|
Thus `auto` wins the otherwise nonsensical `auto + noProposals` combination. 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.
|
|
1940
2073
|
|
|
@@ -1948,7 +2081,7 @@ Thus `auto` wins the otherwise nonsensical `auto + noProposals` combination. Loo
|
|
|
1948
2081
|
|
|
1949
2082
|
§subscriptions-subscription-registry-routes-cancellation READ on a streaming scheme is a subscription, not a one-shot. The scheme establishes its protocol-specific acquisition boundary, returns `102 Processing`, and stays alive through the `StreamSubscription` returned by `subscriptions.open()`. The service commits that initial operation result normally; later chunk and terminal work cannot rewrite it. Durable terminal truth lives on the subscription and its channels. The service records durable subscription identity and metadata in SQLite and retains the callable `SubscriptionHandle` only in its process-local live registry. SEND signal `499`, worker cancellation, turn-scoped reap, and shutdown all route through that one live registry; no handler-specific cancellation hook or database access is part of the plugin contract.
|
|
1950
2083
|
|
|
1951
|
-
The durable row is lifecycle evidence and the lookup key, not a serialized callback. `subscriptions.open()` establishes both halves before yielding a composed `StreamSubscription`: an `AbortSignal` whose fused `notifyChunk` and terminal `close` methods are safe to retain without the operation's general `SchemeCtx`. `close(result, summary?)` validates
|
|
2084
|
+
The durable row is lifecycle evidence and the lookup key, not a serialized callback. `subscriptions.open()` establishes both halves before yielding a composed `StreamSubscription`: an `AbortSignal` whose fused `notifyChunk` and terminal `close` methods are safe to retain without the operation's general `SchemeCtx`. `close(result, summary?, channelResults?)` validates one universal terminal producer result plus exact named channel overrides. One SQLite transition closes the subscription and installs each channel's terminal `producerResult`; its lifecycle state derives from that result. The transition then wakes the worker when appropriate and unregisters the live handle. `close_status` is a constrained relational projection of `close_result.status`, never an independent result, while `channel_results` preserves historical overrides after a later subscription replaces the channel's current evidence. A durable open row without a live handle is an explicit lifecycle failure, never a fabricated cancellation success. Channel state ({§channel-state}) + log entries ({§no-chunk-rows}) carry lifecycle.
|
|
1952
2085
|
|
|
1953
2086
|
At process restart every still-open row is necessarily missing its callable owner. Boot
|
|
1954
2087
|
settles it as interruption (`500`) and errors active channels before evaluating parked
|
|
@@ -2009,7 +2142,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
2009
2142
|
|
|
2010
2143
|
- **Schema-alignment test**: loads `@plurnk/plurnk-contracts/schema/*.json`, parses DDL via `node:sqlite` introspection, asserts every required schema field has a corresponding `NOT NULL` column. Contract drift fails CI.
|
|
2011
2144
|
- DDL = storage truth; JSON Schemas = wire truth. Tested-aligned, allowed to differ where ergonomics demand.
|
|
2012
|
-
- §entry-identity-no-null **Identity components are never NULL.** The entries identity tuple — (
|
|
2145
|
+
- §entry-identity-no-null **Identity components are never NULL.** The entries identity tuple — `(owner, scheme, authority, pathname)` — admits no NULL component because SQLite treats NULLs as distinct under a UNIQUE index, allowing duplicate logical identities. `owner_id` references one Worker, which is the sole workspace coordinate; `entries` stores no redundant `workspace_id` that could contradict it. `authority` defaults to the canonical empty string for namespace and owner schemes; resource schemes persist their canonical authority there. File members persist under the reserved **`file`** scheme (`storedScheme: "file"`; they still render as bare paths); `entries.scheme` and `entries.authority` are `NOT NULL`; a manifest declaring `storedScheme: null` is refused at registration.
|
|
2013
2146
|
|
|
2014
2147
|
### §sql-ts-boundary SQL/TS responsibility boundary
|
|
2015
2148
|
|
|
@@ -2201,6 +2334,7 @@ Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-
|
|
|
2201
2334
|
| `PLURNK_HOST` | `127.0.0.1` | Bind address for the listener. Local-only by default. |
|
|
2202
2335
|
| `PLURNK_PORT` | `3044` | TCP port for THE client surface — the AG-UI+ listener (the plurnk-agui plugin module binds it at boot). Production is single-listener. |
|
|
2203
2336
|
| §operator-config-git-ceiling `PLURNK_SERVICE_GIT_ALLOWED` | `1` | Hard service ceiling: only `1` admits Git membership, status, branch batching, and `git`/`isogit` executors; every other value denies them before executor registration or packet teaching. |
|
|
2337
|
+
| §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. |
|
|
2204
2338
|
| `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. |
|
|
2205
2339
|
| `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). |
|
|
2206
2340
|
| §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}). |
|
|
@@ -2217,12 +2351,14 @@ Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-
|
|
|
2217
2351
|
| `PLURNK_SERVICE_REQUIEM_RETRY_MAX_TOKENS` | `32768` | Retry allowance; must be at least the initial requiem allowance ({§digest-requiem}). |
|
|
2218
2352
|
| `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}). |
|
|
2219
2353
|
| `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}). |
|
|
2354
|
+
| §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}). |
|
|
2355
|
+
| `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}). |
|
|
2220
2356
|
|
|
2221
2357
|
Every core knob listed is enforced at its owning read site; `.env.defaults` is the authoritative default ({§operator-config-env-defaults}). Provider, scheme, executor, mimetype, and client-interface knobs are documented by their owning packages and appear in the assembled catalog.
|
|
2222
2358
|
|
|
2223
2359
|
**Two override semantics — ceiling vs default.** Which kind a var is determines what "override" means across the cascade:
|
|
2224
2360
|
|
|
2225
|
-
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-workspace constraint, not a per-call seam argument. `PLURNK_SERVICE_GIT_ALLOWED` ({§operator-config-git-ceiling}), `PLURNK_SERVICE_MAX_COMMANDS`, `PLURNK_SERVICE_MAX_STRIKES`, and `PLURNK_SERVICE_MAX_TURNS` (`-1` ships it off; a positive value caps the per-call request). The sandbox/cost guarantee: the operator caps it; no client widens it.
|
|
2361
|
+
- **Ceiling** (most-restrictive-wins) — an operator-set hard bound nothing downstream may exceed: not a lower-precedence file, not a per-workspace constraint, not a per-call seam argument. `PLURNK_SERVICE_GIT_ALLOWED` ({§operator-config-git-ceiling}), `PLURNK_SERVICE_FILE_CREATE_SCOPE` ({§operator-config-file-create-scope}), `PLURNK_SERVICE_MAX_COMMANDS`, `PLURNK_SERVICE_MAX_STRIKES`, and `PLURNK_SERVICE_MAX_TURNS` (`-1` ships it off; a positive value caps the per-call request). The sandbox/cost guarantee: the operator caps it; no client widens it.
|
|
2226
2362
|
- **Default** (explicit-wins) — a fallback the most-specific setter replaces freely: `PLURNK_MODEL` (a `runLoop({selector})` request overrides it) and the config-time vars (`HOST` / `PORT` / `DB_PATH`).
|
|
2227
2363
|
|
|
2228
2364
|
§operator-config-shipped-defaults **The shipped `.env.defaults` is itself under
|
|
@@ -2279,6 +2415,7 @@ boundary. Operator-arcane knobs stay environment-only.
|
|
|
2279
2415
|
| `settings.filesItems` | Integer `>= -1` | Explicit replacement {§operator-config-workspace-files-items} |
|
|
2280
2416
|
| `settings.maxCommands` | Non-negative integer | Tightening ceiling {§operator-config-workspace-max-commands} |
|
|
2281
2417
|
| `settings.git` | Boolean | Tightening denial {§operator-config-workspace-git} |
|
|
2418
|
+
| `settings.fileCreateScope` | `none`, `root`, or `namespace` | Tightening ceiling {§operator-config-workspace-file-create-scope} |
|
|
2282
2419
|
| `settings.client` | Nonempty string | Stable self-identification {§client-metadata} |
|
|
2283
2420
|
| `settings.execs` | Record of policy-key to string | Subtractive executor layer {§operator-config-workspace-execs} |
|
|
2284
2421
|
|
|
@@ -2295,10 +2432,11 @@ leak into another.
|
|
|
2295
2432
|
workspace: a client tightens the runaway-op guard and never raises it past
|
|
2296
2433
|
the operator's.
|
|
2297
2434
|
- §operator-config-workspace-max-commands-floor The cap bounds *actions* only.
|
|
2298
|
-
PLAN (
|
|
2435
|
+
PLAN (complete current Plan) and the final disposition `SEND` (`102`, `200`, `202`,
|
|
2299
2436
|
`300`, or `499`) are never counted and always dispatch, so `0` is a valid
|
|
2300
2437
|
floor — the tightest — admitting a plan and disposition with zero actions.
|
|
2301
2438
|
- §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.
|
|
2439
|
+
- §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`.
|
|
2302
2440
|
- §operator-config-workspace-execs `settings.execs` is a workspace-stable
|
|
2303
2441
|
snapshot of one `Record<string, string>` policy layer using
|
|
2304
2442
|
{§executor-policy}. Keys are matched case-insensitively and must be
|
|
@@ -2307,7 +2445,7 @@ leak into another.
|
|
|
2307
2445
|
effective workspace registry is authoritative and the settings layer only
|
|
2308
2446
|
intersects it: settings cannot register or re-enable a runtime. A canonical
|
|
2309
2447
|
key for a currently absent tag is accepted as inert policy and applies if a
|
|
2310
|
-
|
|
2448
|
+
worker Functionality provider later publishes that tag. Dispatch and
|
|
2311
2449
|
model-facing tool-resource materialization use the same registered-set intersection and policy
|
|
2312
2450
|
predicate, so a workspace-disabled tag is neither executable nor taught.
|
|
2313
2451
|
|
|
@@ -2321,7 +2459,8 @@ External plugins declare their own env vars in their own `.env.defaults`, assemb
|
|
|
2321
2459
|
|
|
2322
2460
|
## §rpc Module seam
|
|
2323
2461
|
|
|
2324
|
-
Core
|
|
2462
|
+
Core implements the contracts-owned {§application-port} and owns the typed
|
|
2463
|
+
module setup seam. It owns no external listener, public
|
|
2325
2464
|
action-name catalog, or generic string-dispatched method registry. A
|
|
2326
2465
|
client-interface module such as `plurnk-agui` owns its public protocol, action
|
|
2327
2466
|
names, request validation, discovery result, and event projection.
|
|
@@ -2331,21 +2470,36 @@ names, request validation, discovery result, and event projection.
|
|
|
2331
2470
|
```mermaid
|
|
2332
2471
|
flowchart LR
|
|
2333
2472
|
register["Daemon.registerModule"] --> setup["module.setup(ModuleSetupSeam)"]
|
|
2334
|
-
setup --> capabilities["Register static capabilities,<br/>workspace
|
|
2335
|
-
capabilities --> ready["
|
|
2336
|
-
ready -->
|
|
2473
|
+
setup --> capabilities["Register static capabilities,<br/>workspace activators, and actions"]
|
|
2474
|
+
capabilities --> ready["Process-wide schemes ready"]
|
|
2475
|
+
ready --> recovery["Reconcile durable lifecycle"]
|
|
2476
|
+
recovery --> start["module.start(ApplicationPort)"]
|
|
2337
2477
|
start --> interface["Module-owned listener<br/>and client protocol"]
|
|
2338
|
-
|
|
2478
|
+
recovery -->|durable workspace work| demand["First workspace demand"]
|
|
2479
|
+
interface -->|client workspace work| demand
|
|
2480
|
+
demand --> lease["Acquire capability residency"]
|
|
2481
|
+
lease --> activate["Activate if cold;<br/>publish workspace docs"]
|
|
2482
|
+
activate --> calls["Typed ApplicationPort calls"]
|
|
2483
|
+
calls --> release["Release demand lease"]
|
|
2484
|
+
release --> warm["Bounded warm grace / idle LRU"]
|
|
2485
|
+
warm -->|new demand| lease
|
|
2486
|
+
warm -->|idle bound| cool["Deactivate providers;<br/>withdraw runtime projections"]
|
|
2487
|
+
interface -->|worldless call| calls
|
|
2339
2488
|
calls --> core["Core state and orchestration"]
|
|
2340
2489
|
core --> events["subscribeToEvents<br/>(workspaceId, event, payload)"]
|
|
2341
2490
|
events --> interface
|
|
2342
2491
|
```
|
|
2343
2492
|
|
|
2344
2493
|
Every registered module's `setup` runs in registration order before any
|
|
2345
|
-
module's `start`. Core then readies schemes,
|
|
2346
|
-
|
|
2347
|
-
|
|
2348
|
-
|
|
2494
|
+
module's `start`. Core then readies process-wide schemes, reconciles durable
|
|
2495
|
+
lifecycle, and starts modules in registration order. Persisted workspaces with
|
|
2496
|
+
no durable work stay passive until first demand; activation publishes their
|
|
2497
|
+
complete capabilities and documentation before the demanding operation
|
|
2498
|
+
proceeds. `setup` is the readiness boundary for every capability registered
|
|
2499
|
+
with Core: recovery may demand a workspace provider before `start`. `start`
|
|
2500
|
+
opens module-owned exterior ingress only after recovery, so no registered
|
|
2501
|
+
capability may depend on it. Shutdown begins started and self-closing module
|
|
2502
|
+
closure in reverse order and surfaces aggregated close failures.
|
|
2349
2503
|
|
|
2350
2504
|
§module-discovery **Third-party daemon-module composition is manifest
|
|
2351
2505
|
discovery.** A package declares `plurnk: { kind: "module", module:
|
|
@@ -2353,18 +2507,22 @@ discovery.** A package declares `plurnk: { kind: "module", module:
|
|
|
2353
2507
|
factory returning one). At boot, core scans installed packages under the
|
|
2354
2508
|
executor family's discovery and trust rules ({§plugin-discovery}) and
|
|
2355
2509
|
registers every trusted declaring module before any module setup runs, in
|
|
2356
|
-
package-
|
|
2510
|
+
package-name order. The service's explicit composition — the AG-UI,
|
|
2357
2511
|
hooks, and MCP modules — carries init options and is wired in service.ts;
|
|
2358
2512
|
discovery never duplicates those packages. An untrusted declaring package is
|
|
2359
2513
|
skipped with a boot warning, never executed. A module export that is neither
|
|
2360
|
-
an object nor a no-arg factory
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2514
|
+
an object nor a no-arg factory, a factory returning a non-object, or an object
|
|
2515
|
+
with a non-function lifecycle member fails boot loudly.
|
|
2516
|
+
|
|
2517
|
+
§module-shutdown-order `Daemon.stop()` first rejects new capability demand and
|
|
2518
|
+
aborts proposals, branches, derivations, and worker scopes. It simultaneously
|
|
2519
|
+
begins every module closer in reverse registration order, allowing exterior
|
|
2520
|
+
listeners to stop accepting work while active requests observe those
|
|
2521
|
+
cancellations. It then settles branches, drains, module closers, streaming
|
|
2522
|
+
producers, derivations, mimetypes, and schemes before its final conclusion-wake
|
|
2523
|
+
barrier. The supervisor owns each asynchronous wake task from acceptance
|
|
2524
|
+
through settlement; a task failure participates in the shutdown aggregate. The
|
|
2525
|
+
database may be released only after the final wake barrier resolves.
|
|
2368
2526
|
|
|
2369
2527
|
§crash-only-stop The settle sequence is deadline-bounded
|
|
2370
2528
|
(`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
|
|
@@ -2374,8 +2532,12 @@ unbounded one.
|
|
|
2374
2532
|
|
|
2375
2533
|
```mermaid
|
|
2376
2534
|
flowchart LR
|
|
2377
|
-
stop[
|
|
2378
|
-
|
|
2535
|
+
stop[Begin stop] --> abort[Abort core producers]
|
|
2536
|
+
stop --> moduleClose[Begin reverse module closure]
|
|
2537
|
+
abort --> drains[Settle worker drains]
|
|
2538
|
+
drains --> joined[Settle module closures]
|
|
2539
|
+
moduleClose --> joined
|
|
2540
|
+
joined --> producers[Settle streaming producers]
|
|
2379
2541
|
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
2380
2542
|
resources --> wakes[Settle conclusion wakes]
|
|
2381
2543
|
wakes --> database[Release database]
|
|
@@ -2385,30 +2547,166 @@ flowchart LR
|
|
|
2385
2547
|
|---|---|
|
|
2386
2548
|
| `registerRuntimes([{ decl, executor, availability, scheme? }, ...])` | Validates the complete canonical tag set under {§executor-runtime-declaration}, then publishes every process-wide executor and optional claimed scheme facet atomically. |
|
|
2387
2549
|
| `registerScheme(name, handler)` | Adds one process-wide addressable scheme handler; scheme readiness and model-facing capability publication remain core-owned. |
|
|
2388
|
-
| §module-action-registration `registerModuleAction({ name, scope, handler })` | Adds one non-empty, extension-unique action. `scope` is exactly `worldless` or `
|
|
2389
|
-
| §module-
|
|
2390
|
-
| §module-
|
|
2391
|
-
| §module-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2550
|
+
| §module-action-registration `registerModuleAction({ name, scope, inputSchema, outputSchema, handler })` | Adds one non-empty, extension-unique action with resolvable JSON Schemas. `scope` is exactly `worldless`, `workspace`, or `worker`; the handler receives schema-validated params and a separate matching context. Scoped contexts contain trusted bound identifiers, never client parameters. A client-interface module decides whether and how the name becomes public, validates successful output, and owns collisions with its built-ins. |
|
|
2551
|
+
| §module-worker-provider `registerWorkerCapabilityProvider(namespaceOwner, provider)` | Registers one extension-unique Functionality provider. `activate({ workspaceId, workerId, retain })` reconstructs that worker's effective snapshot; idempotent `deactivate({ workspaceId, workerId })` releases its process-local resources. Core coalesces activation and cooling, publishes complete private documentation before use, and supplies `retain()` so provider work that outlives its caller holds an idempotently releasable residency lease. Dormant workers perform no provider work at boot. |
|
|
2552
|
+
| §module-worker-state `readWorkerModuleState(workerId, namespaceOwner)` | Reads the provider's one nullable JSON state value for one worker. Core owns worker isolation and storage; the provider owns and validates its schema. Secret values are forbidden when a durable symbolic reference can identify their authoritative source. |
|
|
2553
|
+
| §module-functionality-adapter `registerFunctionalityAdapter(adapter)` | Registers one family of managed Functionality beneath the shared coordinator ({§functionality-coordinator}). The adapter owns protocol truth; the coordinator owns lifecycle, durable state, publication, and both projections. |
|
|
2554
|
+
| §module-worker-capabilities `replaceWorkerCapabilities({ workspaceId, workerId, namespaceOwner, state, runtimes })` | Replaces one provider's complete durable state and runtime/scheme snapshot for one worker at a quiescent workspace-operation boundary. Core validates worker membership and base/peer namespace claims before mutation, commits the snapshot, and reconciles that worker's private pull docs as one operation. Failure restores the prior state and presentation. The empty runtime set removes that provider from the worker's Functionality. |
|
|
2555
|
+
|
|
2556
|
+
§module-worker-quiescence **A worker's Functionality snapshot changes only
|
|
2557
|
+
between workspace operations.** A replacement attempt while a turn or another
|
|
2395
2558
|
capability mutation owns the workspace fails 409 instead of waiting behind an
|
|
2396
2559
|
unbounded proposal. Candidate discovery may occur before the gate, but the
|
|
2397
2560
|
provider must re-check its old connection for active user work after acquiring
|
|
2398
2561
|
the gate. Infrastructure-owned watches may be cancelled during replacement;
|
|
2399
2562
|
an active request, input exchange, or Task keeps the old snapshot authoritative
|
|
2400
|
-
and makes replacement fail 409.
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2563
|
+
and makes replacement fail 409. The workspace-wide gate is an atomicity
|
|
2564
|
+
boundary, not ownership: only the addressed worker's snapshot changes. A Worker's
|
|
2565
|
+
own accepted Functionality mutation is the one replacement that waits instead of
|
|
2566
|
+
failing: it queues fairly behind the turn that raised it and publishes at that
|
|
2567
|
+
turn's boundary ({§functionality-model-mutation}). Two publications take no
|
|
2568
|
+
gate of their own because their demand already holds whatever applies: a
|
|
2569
|
+
Worker's activation (demanded from a client action, an operation, or a child's
|
|
2570
|
+
turn inside its parent's held lineage) and a family's turn-admission refresh
|
|
2571
|
+
({§skills-hotload}), which republishes inside the turn it is admitting. The
|
|
2572
|
+
three modes are explicit at the host boundary — `try` (409 while held),
|
|
2573
|
+
`wait` (queue behind the holder), `none` (publish within the demand) — and
|
|
2574
|
+
nothing else may choose `none`.
|
|
2575
|
+
|
|
2576
|
+
§module-worker-inheritance **Functionality inherits by value.** Creating a
|
|
2577
|
+
child copies every parent `worker_module_state` row into the child in the same
|
|
2578
|
+
database transaction. Parent and child thereafter mutate independently;
|
|
2579
|
+
activation reconstructs each worker's own snapshot and generated references.
|
|
2580
|
+
Workers that merely share a workspace never share enabled Functionality.
|
|
2581
|
+
|
|
2582
|
+
§module-worker-residency **Persistence is not residency.** Model execution,
|
|
2583
|
+
capability-aware client reads and operations, worker module actions, and
|
|
2584
|
+
provider work retained through the setup context hold that worker's Functionality
|
|
2585
|
+
resident. Daemon boot, workspace or worker creation, attachment, listing,
|
|
2586
|
+
renaming, an idle client, and durable queued or parked state do not. After the
|
|
2587
|
+
final lease releases, core keeps the complete snapshot warm for
|
|
2588
|
+
`PLURNK_SERVICE_WORKER_WARM_MS` (default `900000`) while an idle LRU keeps at
|
|
2589
|
+
most `PLURNK_SERVICE_WORKER_WARM_MAX` (default `2`) lease-free workers.
|
|
2590
|
+
`0` disables the respective grace or idle allowance; `-1` disables that bound.
|
|
2591
|
+
Only lease-free workers cool. New demand cancels pending cooling or waits for
|
|
2592
|
+
in-progress cooling before one coalesced reactivation.
|
|
2593
|
+
|
|
2594
|
+
Cooling runs at a quiescent workspace-operation boundary. It deactivates every
|
|
2595
|
+
provider for that worker, withdraws its ephemeral executor and scheme snapshots,
|
|
2596
|
+
and evicts its passive process caches; durable workspace, worker, history,
|
|
2597
|
+
module-state, and reference-entry rows remain unchanged. Client connection
|
|
2598
|
+
presence and naming never participate in this lifecycle. Shutdown cancels warm
|
|
2599
|
+
timers and closes all still-resident provider resources through their module
|
|
2600
|
+
owner.
|
|
2601
|
+
|
|
2602
|
+
The version-1 baseline table `worker_module_state` stores one JSON value per
|
|
2603
|
+
`(worker_id, namespace_owner)`. It is not an alternate registry: executable and
|
|
2604
|
+
resource presentation always comes from the in-memory snapshot reconstructed by
|
|
2605
|
+
the registered provider. Deleting a worker cascades its state; child creation
|
|
2606
|
+
applies {§module-worker-inheritance}.
|
|
2607
|
+
|
|
2608
|
+
### §functionality Worker Functionality: one lifecycle above every family
|
|
2609
|
+
|
|
2610
|
+
§functionality-coordinator **Core owns one coordinator above every family
|
|
2611
|
+
adapter.** Agent Skills, MCP servers, and outbound A2A agents are families of
|
|
2612
|
+
managed Functionality. Each family registers one adapter
|
|
2613
|
+
({§functionality-adapter}); the coordinator owns the common lifecycle —
|
|
2614
|
+
`list | discover | add | enable | disable | remove` — its durable per-Worker
|
|
2615
|
+
state ({§functionality-state}), serialization per Worker and family, atomic
|
|
2616
|
+
publication ({§functionality-publication}), and both projections: worker-scoped
|
|
2617
|
+
client actions `worker.<family>.<verb>` and one generated executor family per
|
|
2618
|
+
Worker ({§functionality-model-projection}). An explicit client action and an
|
|
2619
|
+
accepted model proposal converge on the same coordinator method; no family
|
|
2620
|
+
invents a third management grammar, configuration path, proposal policy, or
|
|
2621
|
+
hotload mechanism.
|
|
2622
|
+
|
|
2623
|
+
| Verb | Common contract |
|
|
2624
|
+
|---|---|
|
|
2625
|
+
| `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. |
|
|
2626
|
+
| `discover` | Inspect a query or source and return inert candidates with provenance. Discovery never installs, persists, enables, executes, or widens authority. |
|
|
2627
|
+
| `add` | Admit one exact definition through the adapter, persist it as a worker-origin definition, prepare it, and enable it atomically. A worker definition may shadow a same-alias service definition; a second worker definition for one alias is a 409 collision. |
|
|
2628
|
+
| `enable` | Prepare and publish one available definition; re-enabling an unavailable one retries its preparation. |
|
|
2629
|
+
| `disable` | Withdraw the effective capability while keeping the definition available and client-visible. |
|
|
2630
|
+
| `remove` | Disable and forget the Worker's own definition; a same-alias service definition becomes visible again, disabled. Service definitions are disable-only. |
|
|
2631
|
+
|
|
2632
|
+
§functionality-adapter **An adapter owns protocol truth and nothing else.** It
|
|
2633
|
+
declares its family (the action segment and EXEC tag), its one namespace owner,
|
|
2634
|
+
the exact definition schema one `add` accepts, its service-contributed
|
|
2635
|
+
definitions with their default enabledness, inert discovery, admission of an
|
|
2636
|
+
authored definition, two-phase preparation of the enabled set, and teardown.
|
|
2637
|
+
Preparation returns the family's runtimes, its generated documents, one outcome
|
|
2638
|
+
per enabled alias, and a snapshot with `commit`/`abort`; the coordinator
|
|
2639
|
+
never tears down a previous snapshot behind the adapter — it commits after a
|
|
2640
|
+
successful publication, aborts after a failed one, and tears down only on
|
|
2641
|
+
deactivation. Protocol continuations (an OAuth completion, an input-required
|
|
2642
|
+
answer) remain adapter-registered actions beneath the common grammar. An
|
|
2643
|
+
adapter may declare `forget`: before the coordinator forgets a Worker-origin
|
|
2644
|
+
definition on `remove` it lets the adapter release what that definition
|
|
2645
|
+
installed or provisioned ({§skills-remove}); a failed release rejects the
|
|
2646
|
+
removal and changes nothing.
|
|
2647
|
+
|
|
2648
|
+
§functionality-state **One durable value per Worker and family.** The
|
|
2649
|
+
coordinator stores `{ version: 1, definitions: { [alias]: { origin, enabled,
|
|
2650
|
+
definition? } } }` in `worker_module_state` under the adapter's namespace
|
|
2651
|
+
owner. A `service` alias persists only its enabledness; a `worker` alias
|
|
2652
|
+
persists its exact definition. Enabledness is durable desired state; active,
|
|
2653
|
+
unavailable, and authorization-required are the current preparation outcome.
|
|
2654
|
+
Inheritance by value is the table's own birth snapshot
|
|
2655
|
+
({§module-worker-inheritance}); service, user, project, and client configuration
|
|
2656
|
+
contribute available definitions and defaults but are never the live effective
|
|
2657
|
+
authority.
|
|
2658
|
+
|
|
2659
|
+
§functionality-publication **One replacement publishes a family.** The
|
|
2660
|
+
coordinator prepares the enabled set, then replaces the family's state and
|
|
2661
|
+
runtimes — the family's manager runtime first, the adapter's capabilities after
|
|
2662
|
+
it — in one {§module-worker-capabilities} call, so admission, generated
|
|
2663
|
+
documentation, resources, effects, Turn 0, client status, and teardown derive
|
|
2664
|
+
from one committed snapshot. A failed replacement aborts the preparation and
|
|
2665
|
+
keeps the previous snapshot authoritative. Mutations serialize per Worker and
|
|
2666
|
+
family; shutdown — and any caller that must observe a boundary publication
|
|
2667
|
+
before acting, through `settleFunctionality` — settles every queued
|
|
2668
|
+
publication before closing the database.
|
|
2669
|
+
|
|
2670
|
+
§functionality-documents **A family's generated documents travel with its
|
|
2671
|
+
snapshot.** Preparation may return documents addressed relative to the
|
|
2672
|
+
Worker's generated subtree ({§worker-generated-subtree}); the coordinator
|
|
2673
|
+
contributes them to the Worker's reference entries so they reconcile with the
|
|
2674
|
+
same `_plurnk` materialization as every other generated document. The model
|
|
2675
|
+
surface is silent until active (operator ruling, #333): a disabled or
|
|
2676
|
+
enabled-but-unavailable definition publishes no document and no Turn 0 row —
|
|
2677
|
+
the hot path carries only working capability, never "a thing you cannot do."
|
|
2678
|
+
An unavailable definition's exact Problem stays reachable on demand: the
|
|
2679
|
+
family's `list` verb returns it and invoking the alias is rejected with it.
|
|
2680
|
+
|
|
2681
|
+
§functionality-model-projection **The model face is a generated executor
|
|
2682
|
+
family per Worker.** Every activated Worker publishes, for each registered
|
|
2683
|
+
family, one executor tagged with the family name whose registered targets are
|
|
2684
|
+
exactly the six verbs; its documents render through
|
|
2685
|
+
{§tools-resource-materialization} like every family, so the model learns the
|
|
2686
|
+
manager from `_plurnk/skills/plurnk/<family>.md` and never from hand-written
|
|
2687
|
+
teaching. `list` and `discover` are `read` effects and run ungated; `add`,
|
|
2688
|
+
`enable`, `disable`, and `remove` are `host` effects and propose through
|
|
2689
|
+
the ordinary Exec proposal lifecycle. A verb's JSON outcome streams into the
|
|
2690
|
+
family's output entry. `ExecArgs` carries no Worker identity, which is why
|
|
2691
|
+
the manager is published per Worker rather than once.
|
|
2692
|
+
|
|
2693
|
+
§functionality-model-mutation **An accepted mutation publishes at its turn
|
|
2694
|
+
boundary.** The verb runs inside the turn that raised it, which holds the
|
|
2695
|
+
workspace; the coordinator persists desired state and prepares immediately —
|
|
2696
|
+
so the result reports `active`, `unavailable`, or `authorization-required`
|
|
2697
|
+
— and a failed preparation publishes an enabled-but-unavailable outcome rather
|
|
2698
|
+
than rejecting. Publication queues behind that turn in the family's serialized
|
|
2699
|
+
lane and settles before the Worker's next operation or packet. An explicit
|
|
2700
|
+
client action instead publishes now, rejects a failed preparation, and fails
|
|
2701
|
+
409 while the workspace is held ({§module-worker-quiescence}). Rejecting a
|
|
2702
|
+
proposal prepares, persists, and publishes nothing.
|
|
2703
|
+
|
|
2704
|
+
### §methods ApplicationPort function set
|
|
2705
|
+
|
|
2706
|
+
`ApplicationPort` is the contracts-owned interface implemented by `Daemon` and
|
|
2707
|
+
consumed by every exterior adapter ({§application-port}). Its function names are
|
|
2708
|
+
transport-neutral library calls, not public wire names; this table specifies
|
|
2709
|
+
Core's behavior behind them.
|
|
2412
2710
|
|
|
2413
2711
|
| Area | Function | Core contract |
|
|
2414
2712
|
|---------------------------------------------------|----------|---------------|
|
|
@@ -2417,16 +2715,16 @@ Its function names are transport-neutral library calls, not public wire names.
|
|
|
2417
2715
|
| §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. |
|
|
2418
2716
|
| §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. |
|
|
2419
2717
|
| §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. |
|
|
2420
|
-
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, maxTurns?, flags?, openPaths?, selector?, childSelector? })` | Validates a model worker and provider policy, persists it with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. The exact terminal result arrives only through `loop/terminated`; parking and resuming do not replace the loop. |
|
|
2421
|
-
| §methods-loop-cancel Loops | `cancelDrain(workerId, reason?)` |
|
|
2422
|
-
| §methods-op-mirror Client dispatch | `dispatchClientAction({ workspaceId, workerId, statements })` | Dispatches already-parsed grammar statements as one client action in one administrative loop. 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. |
|
|
2423
|
-
| Client observation | `look({ workspaceId, workerId, statement })` | Runs an already-parsed READ through the full resolver without a log row. A non-READ statement is rejected ({§op-look}). |
|
|
2718
|
+
| §methods-loop-run Loops | `runLoop({ workspaceId, workerId, prompt, source?, maxTurns?, flags?, openPaths?, selector?, childSelector? })` | Validates a model worker and provider policy, persists it with the effective turn ceiling, then returns an immediate status-100 acknowledgement with `loopId` and `action`. 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. |
|
|
2719
|
+
| §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. |
|
|
2720
|
+
| §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. |
|
|
2721
|
+
| 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}). |
|
|
2424
2722
|
| §methods-log-read Reads | `readLog({ workspaceId, workerId, ...coordinate })` | Ownership-checks the worker, then reads by ids, recency, or the complete `loopSeq`/`turnSeq`/`sequence` display coordinate. `limit` defaults to 100 and is capped at 1000. |
|
|
2425
2723
|
| §methods-entry-read Reads | `readEntry({ workspaceId, workerId, target, channel?, offset? })` | Resolves the selector from that worker's perspective and returns {§entry-read-result}, either complete or as one channel suffix, without creating action evidence. |
|
|
2426
2724
|
| Providers | `listProviders()` | Lists configured aliases with provider/model identity, active state, and the effective provider-derived `inputCapacity` when known. |
|
|
2427
2725
|
| Model catalog | `listModels(query)` | Returns one validated bounded {§model-catalog-wire} page under {§model-catalog}; performs no provider request or selection. |
|
|
2428
2726
|
| 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. |
|
|
2429
|
-
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings?, constraints? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope,
|
|
2727
|
+
| §methods-workspace-create Workspace lifecycle | `createWorkspace({ name?, projectRoot?, settings?, constraints? })` | Validates `settings` through {§operator-config-workspace-settings}, creates the world and its client envelope, applies constraints, 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. |
|
|
2430
2728
|
| §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. |
|
|
2431
2729
|
| §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. |
|
|
2432
2730
|
| §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. |
|
|
@@ -2434,8 +2732,11 @@ Its function names are transport-neutral library calls, not public wire names.
|
|
|
2434
2732
|
| §methods-workspace-rename Workspace metadata | `renameWorkspace(workspaceId, name)` | Changes only the world's unique mutable name; workers, log, and membership remain intact. |
|
|
2435
2733
|
| Workspace metadata | `constrain(...)`, `unconstrain(...)`, `listConstraints(...)`, `listMembers(...)` | Owns the membership overlay and returns its resolved effects; clients do not reimplement constraint semantics. |
|
|
2436
2734
|
| §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. |
|
|
2437
|
-
| Workspace metadata | `listWorkspaces()`, `
|
|
2438
|
-
|
|
|
2735
|
+
| Workspace metadata | `listWorkspaces()`, `workspaceDerivationStatus(...)` | Reads current workspace identity and derivation progress. |
|
|
2736
|
+
| §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. |
|
|
2737
|
+
| §methods-worker-list Worker topology | `listWorkers(workspaceId, query?)` | Returns the workspace's durable Worker projections under {§application-worker-observation}. The origin filter is exact; an explicitly present `parentWorkerId` filters roots (`null`) or one immediate parent (id), while omission returns every lineage position. |
|
|
2738
|
+
| §methods-worker-loops Loop lifecycle | `listWorkerLoops({ workspaceId, workerId })` | Ownership-checks the Worker and returns its Loops in sequence order under {§application-loop-observation}, including the validated exact terminal result when one exists. It performs no scheduling or event replay. |
|
|
2739
|
+
| Extension actions | `listModuleActions()`, `invokeModuleAction(name, params, context)` | Lists setup-registered `{ name, scope, inputSchema, outputSchema }` descriptors in sorted order. Invocation requires a context matching the registered scope; missing names, forged scope, and missing workspace identity fail before the owner runs. Handler values remain opaque to core. |
|
|
2439
2740
|
|
|
2440
2741
|
§methods-loop-run-fold-consistency **A folded prompt cannot silently reconfigure
|
|
2441
2742
|
its loop.** When `runLoop` targets an active or 202-parked loop, core appends the
|
|
@@ -2633,6 +2934,7 @@ active lifecycle behind. LOOK text anchors resolve through the same
|
|
|
2633
2934
|
| §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. |
|
|
2634
2935
|
| §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. |
|
|
2635
2936
|
| §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. |
|
|
2937
|
+
| §notifications-reasoning-event `reasoning/event` | `{ workerId, loopId, turnId, modelCallId, phase, delta? }` | A main emission call exposes readable reasoning. A nonempty stream is balanced start/content/end; 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. |
|
|
2636
2938
|
|
|
2637
2939
|
§notifications-stream-event-failure-isolation The plugin-facing
|
|
2638
2940
|
`NotifyCaps.streamEvent()` remains a synchronous advisory call while core
|
|
@@ -2654,7 +2956,9 @@ outward envelope that requires it and owns workspace fan-out.
|
|
|
2654
2956
|
### §connection-lifecycle Client action evidence
|
|
2655
2957
|
|
|
2656
2958
|
A module client is an actor ({§machine-processes}). Its dispatched side effects
|
|
2657
|
-
write to its own client worker with `origin="client"
|
|
2959
|
+
write to its own client worker with `origin="client"` and execute in the
|
|
2960
|
+
Functionality of the Worker the client is attached to
|
|
2961
|
+
({§actor-boundary-attached-functionality}); one client action owns
|
|
2658
2962
|
one administrative loop, and its statements become ordered operation turns
|
|
2659
2963
|
inside that loop. A proposal may hold its turn and loop across an external
|
|
2660
2964
|
interrupt/resume, but those records preserve durable evidence rather than
|
|
@@ -2764,7 +3068,7 @@ time of measurement.
|
|
|
2764
3068
|
|
|
2765
3069
|
§tokenomics-client-gauge **Clients receive curation and physical occupancy as separate pairs.** `loop/terminated.usage` carries latest packet-bearing model-turn `curationWeight`/`curationBudget` and latest-emission-call `contextTokens`/`contextCapacity`; each unknown fact is `null`. Packetless chronology cannot erase an assembled-request gauge. Both physical facts bind to that same call: a preflight rejection may report capacity while its absent physical request leaves `contextTokens=null`, never borrowed from an earlier call. Clients never divide provider-reported physical tokens by Core curation weight. `providers.list` exposes each instantiated alias's `inputCapacity`. A model switch replaces the latest-turn facts together; aggregate provider accounting remains cardinal monetary evidence, not a gauge input.
|
|
2766
3070
|
|
|
2767
|
-
- **Derivation is
|
|
3071
|
+
- **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}
|
|
2768
3072
|
- §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.
|
|
2769
3073
|
- §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.
|
|
2770
3074
|
- §tokenomics-neutral-telemetry **Curation telemetry is state, not response allowance.** The model-facing `Context Token Budget` section contains 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. Packet-level composition, rankings, and physical token speculation are absent.
|
|
@@ -2835,14 +3139,14 @@ and never re-fetch a match.
|
|
|
2835
3139
|
- §membership-git-hermetic Native Git runs with ambient `GIT_*` and
|
|
2836
3140
|
global/system config scrubbed, so repository identity follows `project_root`,
|
|
2837
3141
|
never the daemon's launch environment.
|
|
2838
|
-
- §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**
|
|
3142
|
+
- §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.
|
|
2839
3143
|
- §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.
|
|
2840
3144
|
|
|
2841
3145
|
**The overlay — `pick | view | hide`, removed by `drop`.** A `workspace_constraints` table is the client's supersede over Git. Resolved membership is `(project repository files ∪ pick) − hide`, with `view` enforced at the edit gate.
|
|
2842
3146
|
|
|
2843
|
-
- §membership-auto-add **Auto-add** — the project repository's membership is its tracked `ls-files` plus untracked-but-not-ignored files (`git ls-files --others --exclude-standard`), with `git` origin.
|
|
2844
|
-
- §membership-overlay-pick **`pick`** — admit
|
|
2845
|
-
- §membership-overlay-hide **`hide`** — exclude a tracked file: resolution drops matches (`node:path.matchesGlob`) and reconciles so the entry set *equals* the member set. The lever to exclude a committed-but-sensitive tracked file;
|
|
3147
|
+
- §membership-auto-add **Auto-add** — the project repository's ambient membership is its tracked `ls-files` plus untracked-but-not-ignored files (`git ls-files --others --exclude-standard`), with `git` origin. An accepted creation selected for Git incorporation is explicitly staged; failure falls back to an exact generated pick, never an orphan ({§file-create-no-orphans}).
|
|
3148
|
+
- §membership-overlay-pick **`pick`** — admit a file Git misses through a targeted constraint scan (files only), with `constraint` origin. `source: "explicit"` records operator/client policy; `source: "create"` is the exact durable record of an accepted creation. Only explicit picks override active Git ignore. In a Git-absent root, picks are the sole file-membership source.
|
|
3149
|
+
- §membership-overlay-hide **`hide`** — exclude a tracked or picked file: resolution drops matches (`node:path.matchesGlob`) and reconciles so the entry set *equals* the member set. The lever to exclude a committed-but-sensitive tracked file; exclusions mask generated creation picks without deleting their provenance ({§fs-create-masked}).
|
|
2846
3150
|
- §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.)
|
|
2847
3151
|
- §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.
|
|
2848
3152
|
|
|
@@ -2888,13 +3192,13 @@ Lossless chunk admission requires either the embedder's own counter or an exact
|
|
|
2888
3192
|
|
|
2889
3193
|
§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}.
|
|
2890
3194
|
|
|
2891
|
-
§membership-emi-divergence-signal **EMI divergence
|
|
3195
|
+
§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}.
|
|
2892
3196
|
|
|
2893
|
-
§membership-edit-write-cas **The write-back is a compare-and-swap — never a clobber, never a clever merge.** EDIT is *naive against the editable text snapshot*: it diffs the model's change onto the entry's body channel — the exact Unicode the model READ — and the proposal carries the `synced_sig` that snapshot was taken at. Binary sources are refused before this path ({§membership-source-projection}). At accept, `applyResolution` re-stats disk and lands the proposed content only if that signature still matches. If disk moved out-of-band in the propose→accept window — a sibling worker, the user's editor, a build step — the write is **refused** with the same neutral `edit-collision` as {§edit-collision}, and **nothing is written**. The engine neither blind-writes over the ambient change (a *clobber*) nor silently re-diffs the model's edit against a state it never saw (getting *clever*) — both would bury a stale-view contract violation under a fallback. The collision surfaces instead: a ≥400 apply downgrades to a reject ({§proposal}), so the model sees that EDIT **did not occur** (400; the `edit_collision` outcome is forensics-only)
|
|
3197
|
+
§membership-edit-write-cas **The write-back is a compare-and-swap — never a clobber, never a clever merge.** EDIT is *naive against the editable text snapshot*: it diffs the model's change onto the entry's body channel — the exact Unicode the model READ — and the proposal carries the `synced_sig` that snapshot was taken at. Binary sources are refused before this path ({§membership-source-projection}). At accept, `applyResolution` re-stats disk and lands the proposed content only if that signature still matches. If disk moved out-of-band in the propose→accept window — a sibling worker, the user's editor, a build step — the write is **refused** with the same neutral `edit-collision` as {§edit-collision}, and **nothing is written**. The engine neither blind-writes over the ambient change (a *clobber*) nor silently re-diffs the model's edit against a state it never saw (getting *clever*) — both would bury a stale-view contract violation under a fallback. The collision surfaces instead: a ≥400 apply downgrades to a reject ({§proposal}), so the model sees that EDIT **did not occur** (400; the `edit_collision` outcome is forensics-only). Reconciliation aligns the current file projection and records the `source=file` evidence in the runtime log ({§membership-emi-divergence-signal}); the model re-reads and re-proposes against the fresh snapshot.
|
|
2894
3198
|
|
|
2895
3199
|
The version travels *with the proposal*, never re-read from the entry at accept: a sibling worker in the same workspace may reconcile while this proposal sits paused, advancing the entry's `synced_sig` to the drifted disk — comparing against the *current* entry sig would wave that clobber through, so the comparison is always against the sig the proposal was computed at. A proposal that assumed an **absent** path (a create) conflicts only if a file has since appeared; a member with **no recorded snapshot** (an un-materialized entry, null `synced_sig`) has no baseline to guard and writes through — the two are told apart by the proposal's `existed` flag, not by a null sig alone. On a clean landing the entry refreshes to the written content and `synced_sig` is **restamped** to it, so the next reconcile recognizes the model's own write (not an external divergence) and a second same-turn edit bases on the landed bytes, not a stale sig. This is the write-side twin of the read-side change-gate ({§membership-change-gated-sync}): one `synced_sig`, gating both the re-read and the write.
|
|
2896
3200
|
|
|
2897
|
-
The CAS is the **hard backstop**, at the moment of writing, on every accept path. It composes with
|
|
3201
|
+
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.
|
|
2898
3202
|
|
|
2899
3203
|
§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 `pick` as the only membership source. `ALLOWED` gates `AUTO`.
|
|
2900
3204
|
|
|
@@ -2940,7 +3244,7 @@ becomes a packetless `_plurnk` turn. Packetless initialization and recovery turn
|
|
|
2940
3244
|
remain ordinary turn chronology but do not consume `maxTurns`, model-call,
|
|
2941
3245
|
emission-attempt, usage, or cost accounting.
|
|
2942
3246
|
|
|
2943
|
-
- §overflow-turn-script **Recovery is one ordinary admitted `_plurnk` program.** Its
|
|
3247
|
+
- §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 in the next turn.` The final sentence requires the successor's substantive operations to be one dedicated, comprehensive bulk-curation program; mandatory PLAN and SEND framing still applies. 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.
|
|
2944
3248
|
- §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.
|
|
2945
3249
|
- §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**.
|
|
2946
3250
|
|
|
@@ -2961,55 +3265,56 @@ to reverse or refine that curation through ordinary log operations.
|
|
|
2961
3265
|
### §env-delta The environment delta: what changed since the model last looked
|
|
2962
3266
|
|
|
2963
3267
|
Catalog FIND results ({§packet-catalog}) state what existed when observed. The
|
|
2964
|
-
environment delta supplies
|
|
2965
|
-
|
|
3268
|
+
environment delta supplies structurally addressed activity without copying the
|
|
3269
|
+
workspace into every worker's private state.
|
|
2966
3270
|
|
|
2967
3271
|
```mermaid
|
|
2968
3272
|
flowchart LR
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
pull --> log["Observer's self-contained log<br/>origin=_plurnk
|
|
3273
|
+
child["Child commits activity"] --> event["One durable occurrence"]
|
|
3274
|
+
commons["Any worker mutates worker:///"] --> event
|
|
3275
|
+
event --> parent["Direct-parent audience"]
|
|
3276
|
+
event -->|"commons only"| global["Workspace audience"]
|
|
3277
|
+
parent --> pull["Pre-turn lossless pull<br/>(cursor, captured high-water]"]
|
|
3278
|
+
global --> pull
|
|
3279
|
+
pull --> log["Observer's self-contained log<br/>origin=_plurnk"]
|
|
2976
3280
|
log --> packet["Packet lists coordinate;<br/>OPEN recalls exact body"]
|
|
2977
3281
|
```
|
|
2978
3282
|
|
|
2979
3283
|
§env-delta-log-pull **Pull the event record, never a world snapshot.** At
|
|
2980
|
-
pre-turn, a worker materializes
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
first
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
and
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
| Producer
|
|
3008
|
-
|
|
|
3009
|
-
| §env-delta-
|
|
3010
|
-
| §env-delta-
|
|
3011
|
-
| §env-delta-
|
|
3012
|
-
| §env-delta-
|
|
3284
|
+
pre-turn, a worker materializes only occurrences whose structural audience
|
|
3285
|
+
includes that worker. The set is exhaustive, unranked, and exactly once; the
|
|
3286
|
+
engine makes no relevance decision. Each copied event retains the operation,
|
|
3287
|
+
result, typed attributes, and initial log classifications ({§log-item-tags}).
|
|
3288
|
+
Every producer appends to one workspace-scoped occurrence journal with a
|
|
3289
|
+
monotonic identity. A pull captures one closed `(worker cursor, high-water]`
|
|
3290
|
+
interval, materializes each addressed identity idempotently, then advances the
|
|
3291
|
+
cursor only after the whole interval is durable. An event racing the capture is
|
|
3292
|
+
therefore in this interval or a later one, never neither or both. Source-log
|
|
3293
|
+
curation cannot erase the occurrence record.
|
|
3294
|
+
|
|
3295
|
+
The cursor is observation progress, not a private copy of resource contents. A
|
|
3296
|
+
fresh worker captures the current high-water when its worker row is created, so
|
|
3297
|
+
pre-existing broadcast history stays out while later occurrences remain
|
|
3298
|
+
deliverable even before its first packet. A fork instead copies the parent's
|
|
3299
|
+
cursor and captures its own fork high-water atomically with worker creation
|
|
3300
|
+
({§machine-processes-fork-pending-activity}). Observer rows retain the source
|
|
3301
|
+
identity and never publish another occurrence.
|
|
3302
|
+
|
|
3303
|
+
§env-delta-worker-entry-visibility **The commons is global; every other
|
|
3304
|
+
resource follows lineage.** A successful state-changing `EDIT`, `COPY`, `MOVE`,
|
|
3305
|
+
entry-path `KILL`, or entry-path `SEND` signal `410` whose landed effects touch `worker:///...` acquires the workspace
|
|
3306
|
+
audience and retains the commons address in its observer row. Mutations to
|
|
3307
|
+
`worker://~/...`, named worker spaces, project files, and remote resources do
|
|
3308
|
+
not broadcast. When authored by a child, their
|
|
3309
|
+
ordinary operation evidence still reaches that child's direct parent.
|
|
3310
|
+
|
|
3311
|
+
| Producer / event | Durable occurrence | Observer projection |
|
|
3312
|
+
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
3313
|
+
| §env-delta-child-activity Direct-child activity | Every final op-bearing child log row except runtime-private `_plurnk/operation` maintenance: prompt, PLAN, operation result, or actionless error. | Direct parent only; one exact attributed row born folded. Provider reasoning, calls, rejected emissions, turnOps, and harness maintenance do not cross. |
|
|
3314
|
+
| §env-delta-child-termination Direct-child termination | The child's exact terminal loop result when the loop contains at least one non-maintenance turn. | Direct parent only; a 2xx deliverable is born open and every failure is folded ({§worker-scheme-collect}). |
|
|
3315
|
+
| §env-delta-commons-mutation Commons mutation | One successful resolved operation whose landed effects touch `worker:///...`. | Every existing worker; one folded row per observer, deduplicated with any lineage audience. |
|
|
3316
|
+
| §env-delta-filesystem-narration Project-file divergence | Runtime-owned reconciliation evidence remains in the runtime actor's own log. | No ambient observer row. Current content remains addressable and stale hash edits reject at their owned boundary. |
|
|
3317
|
+
| §env-delta-entry-materialization Executor `entry()` sink | The runtime records typed materialization evidence under its owning actor. | No ambient observer row unless the resulting operation itself is direct-child activity or a commons mutation ({§exec-entry-sink}). |
|
|
3013
3318
|
|
|
3014
3319
|
§env-delta-attribution **Ownership, authorship, and cause are independent.**
|
|
3015
3320
|
|
|
@@ -3017,21 +3322,19 @@ cross this door, while an ancestry-authorized explicit READ remains available.
|
|
|
3017
3322
|
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
3018
3323
|
| `worker_id` | The worker whose self-contained log owns the materialized row. |
|
|
3019
3324
|
| `origin` | The actor tier that wrote the row; a materialized delta is `_plurnk`. |
|
|
3020
|
-
| `source` | The causal identity.
|
|
3325
|
+
| `source` | The immediate causal identity in this log. A lineage or commons observation uses the canonical `worker://<producer>` control identity; self-authored rows omit it. |
|
|
3021
3326
|
|
|
3022
|
-
§env-delta-no-coalescing **
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
the record and conflate state comparison with event replay.
|
|
3327
|
+
§env-delta-no-coalescing **Activity is never coalesced.** Each admitted child
|
|
3328
|
+
operation and each commons mutation has one occurrence identity. Combining
|
|
3329
|
+
them would destroy causal order and conflate event replay with a state
|
|
3330
|
+
comparison.
|
|
3027
3331
|
|
|
3028
3332
|
§env-delta-passive **Observation never forces a turn.** Deltas materialize only
|
|
3029
|
-
while a packet is already assembling
|
|
3030
|
-
worker. Urgent directed communication
|
|
3031
|
-
({§actor-boundary-two-doors})
|
|
3032
|
-
|
|
3033
|
-
{§
|
|
3034
|
-
there.
|
|
3333
|
+
while a packet is already assembling. Intermediate child activity and commons
|
|
3334
|
+
broadcasts therefore cannot wake an idle worker. Urgent directed communication
|
|
3335
|
+
uses the voice door ({§actor-boundary-two-doors}); direct-child terminal
|
|
3336
|
+
disposition alone carries the structured-concurrency wake owned by
|
|
3337
|
+
{§worker-scheme-collect}. Stream progress remains owned by {§exec-stream}.
|
|
3035
3338
|
|
|
3036
3339
|
### §edit-result-render Mutation log rows render truthful effects
|
|
3037
3340
|
|
|
@@ -3150,8 +3453,6 @@ only after authority crosses the client boundary.
|
|
|
3150
3453
|
|
|
3151
3454
|
§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 precedence from flags, operation, or attrs.
|
|
3152
3455
|
|
|
3153
|
-
§proposal-ownership-auto-stale-clobber **Auto is not blind — it refuses a stale clobber.** When an EDIT's target diverged on disk this turn, accepting it would overwrite an ambient change. The projection carries `staleClobberRisk=true`, and core's loop disposition rejects it; the model can re-READ and retry. This brackets the read→propose window for loop auto, while the compare-and-swap ({§membership-edit-write-cas}) brackets propose→write for every accept path.
|
|
3154
|
-
|
|
3155
3456
|
---
|
|
3156
3457
|
|
|
3157
3458
|
## §packet Packet shape
|
|
@@ -3245,16 +3546,18 @@ evidence when a downstream standard cannot represent the complete list.
|
|
|
3245
3546
|
| row producer | ordinary OPEN projection |
|
|
3246
3547
|
|---|---|
|
|
3247
3548
|
| any `READ` or `FIND` | complete selected operation result |
|
|
3248
|
-
| any `PLAN` | complete
|
|
3549
|
+
| any `PLAN` | complete canonical Plurnk Plan JSON {§plan-value} |
|
|
3249
3550
|
| actionless lowercase `prompt` | budgeted head under {§prompt-projection} |
|
|
3250
3551
|
| structured `EDIT` receipt or textual `COPY`/`MOVE` effects | complete receipt-owned join context |
|
|
3251
3552
|
| every other nonempty body | head bounded independently by `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS` |
|
|
3252
3553
|
| bodyless row | `"display":"none","body":""` |
|
|
3253
3554
|
|
|
3254
|
-
READ and FIND own their range or pagination before packet rendering; the packet never applies a second hidden substring bound to their selected result. PLAN is likewise complete while OPEN: it is the model's explicit persistent reasoning inventory,
|
|
3555
|
+
READ and FIND own their range or pagination before packet rendering; the packet never applies a second hidden substring bound to their selected result. PLAN is likewise complete while OPEN: it is the model's explicit persistent reasoning inventory, serialized once as compact JSON rather than clipped or reparsed from source text. Prompt rows follow their separate adaptive projection contract. Structured mutation contexts already carry the receipt-owned bound in {§edit-result-receipt-truth}, so packet rendering does not preview them again. Actionless source artifacts, SEND/WORK/FORK bodies, EXEC commands, environment-delta EDIT spans, and extension-produced bodies use the ordinary fixed bound. When an OPEN projection differs from its canonical body, `chunk` follows the displayed `body` with the exact selected and complete extents defined by {§jsonplurnk}; complete and FOLDED bodies omit it. `## READ0 (log:///<coordinate>/<OP>)` applies its default or explicit text range to the canonical body; the unsuffixed exact shorthand and authoritative suffix behavior are defined by {§log-coordinate-hierarchy}. `## FIND0 (log:///...)` and search match that same full body. FOLD hides the ordinary projection, and OPEN restores the producer's projection without changing its bound. System/policy sections are not log bodies. Notices are transient non-log observations; they share the ordinary line/character bounds but have no durable body or recovery URI.
|
|
3255
3556
|
|
|
3256
3557
|
§prompt-entry **Prompt as a first-class entry and log row.** Each prompt is stored once at `prompt:///<loop>/<N>` as an owner-keyed text/markdown entry, then published to its turn as one actionless lowercase `prompt` log row. No synthetic EDIT or READ operation is invented. The row is born OPEN and obeys {§body-projection}. The **Active User Prompts** section closes the user-slot status clump as a paths-only list (`* prompt:///<loop>/<N>`), so every frame remains directly READable even after its log row is folded or killed.
|
|
3257
3558
|
|
|
3559
|
+
§prompt-causal-source **Prompt authorship and delivery are distinct facts.** The harness publishes every prompt row with `origin="_plurnk"`; the row's existing `source` carries the canonical address of a different causal actor. Native WORK, FORK, and directed worker SEND derive `worker://<sender>` from the authenticated sender worker ID. A trusted exterior adapter may supply its own canonical actor address through {§methods-loop-run}. An absent source means the owning worker itself. Attribution persists with the prompt frame through active delivery, parking, orphan recovery, restart, and later log projection; model syntax cannot author it.
|
|
3560
|
+
|
|
3258
3561
|
§prompt-projection **Prompt storage is unbounded by model context; automatic materialization is not.** Core persists every accepted prompt completely before packet assembly. The selected provider's derived `inputCapacity` and the alias-resolved percentage from `PLURNK_SERVICE_PROMPT_PROJECTION` derive one aggregate curation-weight allowance for OPEN prompt bodies. Complete prompt bodies render when their aggregate weight fits. Otherwise all OPEN prompt rows share the allowance: full bodies consume only their required share, unused shares are redistributed, and partial bodies render the largest leading complete-line region that fits their share or an exact character-bound prefix when the first physical line alone is larger. The sum of their rendered body weights never exceeds the allowance. Every partial body carries its exact `chunk` after `body`; the canonical `prompt:///` entry and `log:///` body remain complete and READ/FIND-addressable. When provider input capacity is unknown the percentage is underivable, so prompt rows retain the ordinary bounded projection rather than inventing capacity. This policy never rejects, summarizes, or discards a prompt because it exceeds a context window.
|
|
3259
3562
|
|
|
3260
3563
|
§prompt-self-only The frame is self-only and owner-keyed:
|
|
@@ -3344,7 +3647,7 @@ retain distinct contracts and lifetimes.
|
|
|
3344
3647
|
| `workerId` | Narrows workers and every dependent loop, turn, logical model call, emission attempt, physical request, and log row to that one worker. |
|
|
3345
3648
|
| `workspaceId` | Narrows workers and dependent evidence to one workspace; when both selectors are present they intersect. |
|
|
3346
3649
|
|
|
3347
|
-
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log row, including causal `source` and structured `attrs`, every exact OPEN/FOLD target effect from {§fold-open-meta-operations}, the exact Problem on every failed row, each loop's exact terminal result, and every ordered physical provider request. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and
|
|
3650
|
+
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log row, including causal `source` and structured `attrs`, every exact OPEN/FOLD target effect from {§fold-open-meta-operations}, the exact Problem on every failed row, each loop's exact terminal result, and every ordered physical provider request. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and 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; packet files remain byte-identical records of what the model saw.
|
|
3348
3651
|
|
|
3349
3652
|
§digest-requiem **A requiem is an out-of-band forensic interview, not a worker
|
|
3350
3653
|
turn.** It cannot execute operations or alter the audited history.
|
|
@@ -3366,79 +3669,115 @@ turn.** It cannot execute operations or alter the audited history.
|
|
|
3366
3669
|
|
|
3367
3670
|
§tools-resource-discovery **Executable capability discovery uses ordinary
|
|
3368
3671
|
Plurnk resources.** No generated tool table rides the system packet. Every
|
|
3369
|
-
|
|
3370
|
-
`worker
|
|
3371
|
-
{§executor-tool-document}; a runtime with an exact
|
|
3372
|
-
{§executor-tool-registry}
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
executable witness ({§executor-tool-document}). Tool-result/output schemas remain
|
|
3383
|
-
ordinary evidence and never enter these documents.
|
|
3672
|
+
runtime enabled for the current worker with an admitted invocation materializes exactly one
|
|
3673
|
+
family document at `worker://~/_plurnk/skills/plurnk/<runtime>.md`. A general runtime's
|
|
3674
|
+
document contains its {§executor-tool-document}; a runtime with an exact
|
|
3675
|
+
{§executor-tool-registry} materializes the same single document — per-target
|
|
3676
|
+
child documents do not exist, shown or stored. The family document summarizes
|
|
3677
|
+
the server or runtime, lists every enabled target as a directly copyable
|
|
3678
|
+
`## EXEC0` heading with its input signature ({§operation-annotation} carries the
|
|
3679
|
+
target one-liner; no invocation dispatch would reject is ever advertised), and
|
|
3680
|
+
carries each detailed target's richer input-side contract as a
|
|
3681
|
+
`## <target>` section of the same document, that target's own headings demoted
|
|
3682
|
+
one level beneath it. A detail-less target's invocation line is its whole
|
|
3683
|
+
teaching. Tool-result/output schemas remain ordinary evidence and never enter
|
|
3684
|
+
this document.
|
|
3384
3685
|
|
|
3385
3686
|
```mermaid
|
|
3386
3687
|
flowchart LR
|
|
3387
3688
|
Survey["Turn 0 FIND<br/>tools/*.md"] --> Families["family paths + summaries"]
|
|
3388
|
-
Families --> Read["READ selected family"]
|
|
3689
|
+
Families --> Read["READ selected family<br/>only when needed"]
|
|
3389
3690
|
Read --> Exec["EXEC annotated invocation"]
|
|
3390
|
-
Read --> Detail["READ exact child<br/>only when needed"]
|
|
3391
|
-
Detail --> Exec
|
|
3392
3691
|
```
|
|
3393
3692
|
|
|
3394
|
-
§tools-resource-materialization The runtime registry,
|
|
3395
|
-
|
|
3396
|
-
|
|
3693
|
+
§tools-resource-materialization The runtime registry, worker executor policy,
|
|
3694
|
+
tool resources, and dispatch use one effective worker snapshot. A
|
|
3695
|
+
worker-disabled, unavailable, detached, replaced, or removed runtime has no
|
|
3397
3696
|
tool resource; an exact registry's empty set publishes no executable family and
|
|
3398
|
-
admits no invocation. Reconciliation deletes stale
|
|
3697
|
+
admits no invocation. Reconciliation deletes stale documents
|
|
3399
3698
|
before upserting the current set. `PLURNK_SERVICE_DOCS_EXCLUDE` does not hide an
|
|
3400
3699
|
enabled executable; executor enablement is the sole user-configured filter
|
|
3401
3700
|
shared by discovery and dispatch. A runtime declaration may carry
|
|
3402
|
-
`resourcesPath` —
|
|
3403
|
-
|
|
3404
|
-
|
|
3405
|
-
|
|
3701
|
+
`resourcesPath` — its generated-doc root relative to the worker's generated
|
|
3702
|
+
subtree ({§worker-generated-subtree}). Absent, its docs live in the internal
|
|
3703
|
+
`_plurnk/skills/plurnk` namespace; present (attached MCP families: `/tools`),
|
|
3704
|
+
the family document materializes at `_plurnk` + that root in the
|
|
3705
|
+
worker's private entry space. Turn 0 surveys the families (`## FIND0 [+init,+tools]
|
|
3706
|
+
(worker://~/_plurnk/tools/*.md)`, one row per
|
|
3406
3707
|
server carrying its summary) and, for each server named in
|
|
3407
|
-
`PLURNK_MCP_EXPANDED`,
|
|
3408
|
-
|
|
3708
|
+
`PLURNK_MCP_EXPANDED`, adds one complete family-document READ
|
|
3709
|
+
(`_plurnk/tools/<server>.md <1,-1>`) so turn 0 names every tool it exposes.
|
|
3409
3710
|
Attached tools are capabilities like every other runtime; the model never
|
|
3410
3711
|
learns an origin.
|
|
3411
3712
|
|
|
3412
|
-
§skills-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3713
|
+
§skills-functionality **Agent Skills are one Worker Functionality family.**
|
|
3714
|
+
Core registers the `skills` family with the coordinator ({§functionality-coordinator});
|
|
3715
|
+
its adapter owns protocol truth for standard Agent Skills and nothing else. A
|
|
3716
|
+
definition is `SkillDefinition` — the standard skill `name`, the universal
|
|
3717
|
+
root `scope` (`project` = `<projectRoot>/.agents/skills`, `global` =
|
|
3718
|
+
`~/.agents/skills`), and for a Worker-installed skill the standard installer
|
|
3719
|
+
`source` that provides it. Plurnk bundles no skills of its own and never
|
|
3720
|
+
seeds or mutates a universal root absent an explicit `add`/`remove`.
|
|
3721
|
+
|
|
3722
|
+
*Available definitions.* The filesystem is the only truth about installation:
|
|
3723
|
+
every `<root>/<name>/SKILL.md` directory under the project then the global
|
|
3724
|
+
root is one service-origin definition, enabled by default, project shadowing
|
|
3725
|
+
global by name; when the standard installer's `skills-lock.json` records a
|
|
3726
|
+
source it rides the definition. A Worker's durable state owns enablement
|
|
3727
|
+
({§functionality-state}); a disabled skill stays client-visible and leaves no
|
|
3728
|
+
model-facing trace.
|
|
3729
|
+
|
|
3730
|
+
*Discovery is inert.* `discover {query}` searches the ecosystem registry
|
|
3731
|
+
(`PLURNK_SERVICE_SKILLS_REGISTRY_URL`, default `https://skills.sh`; empty disables it
|
|
3732
|
+
with 501 `registry-not-configured`) and returns one candidate per hit with
|
|
3733
|
+
`registry` provenance and the exact `owner/repo` source. `discover {source}`
|
|
3734
|
+
lists the skills one standard package reference contains with `source`
|
|
3735
|
+
provenance. Neither installs, persists, or enables. Client `configuration`
|
|
3736
|
+
contributes nothing and is refused with 400.
|
|
3737
|
+
|
|
3738
|
+
*Admission.* `add {alias, definition}` requires `alias = name`, a `source`,
|
|
3739
|
+
and a project root when `scope` is `project`; the Worker's own definition may
|
|
3740
|
+
shadow a service skill of the same name.
|
|
3741
|
+
|
|
3742
|
+
*Preparation.* For each enabled alias the adapter locates the directory at the
|
|
3743
|
+
definition's scope; a Worker definition whose directory is absent is installed
|
|
3744
|
+
through the standard CLI (`PLURNK_SERVICE_SKILLS_CLI`, default `npx --yes skills`:
|
|
3745
|
+
`add <source> --agent universal --skill <name> --yes [--global]`, run with
|
|
3746
|
+
`HOME` set to the service's user home so the installer's `~` is the global
|
|
3747
|
+
root) and the installed `SKILL.md` — never the installer's output — is the
|
|
3748
|
+
evidence.
|
|
3749
|
+
Each admitted skill requires standard `name` and `description` frontmatter
|
|
3750
|
+
with `name` matching its directory. A missing, uninstallable, or invalid skill
|
|
3751
|
+
is `unavailable` with its exact Problem (`skill-missing`, `install-failed`,
|
|
3752
|
+
`skill-invalid`) under the coordinator's failure policy
|
|
3753
|
+
({§functionality-model-mutation}); one bad skill never fails the family.
|
|
3754
|
+
|
|
3755
|
+
*Documents.* The family publishes `worker://~/_plurnk/skills/index.md` —
|
|
3756
|
+
which always exists and lists the active skills by name and description — and
|
|
3757
|
+
one `worker://~/_plurnk/skills/<name>.md` per active skill, with an exact H2
|
|
3758
|
+
`Summary` carrying the standard `description` and the instruction body
|
|
3759
|
+
preserved verbatim ({§functionality-documents}). They are discovered by the
|
|
3760
|
+
turn-0 `+init,+skills` FIND survey; disabled and unavailable skills are
|
|
3761
|
+
absent from model teaching.
|
|
3762
|
+
|
|
3763
|
+
§skills-remove **`remove` uninstalls what the Worker installed.** Before the
|
|
3764
|
+
coordinator forgets a Worker-origin skill definition the adapter removes that
|
|
3765
|
+
skill from the definition's scope through the standard CLI (`remove <name>
|
|
3766
|
+
--yes [--global]`), verified by the directory's absence; a failed removal
|
|
3767
|
+
rejects the mutation. A same-named skill at a lower-precedence root is then
|
|
3768
|
+
revealed as a service definition, disabled ({§functionality-coordinator}).
|
|
3769
|
+
Service definitions are disable-only.
|
|
3770
|
+
|
|
3771
|
+
§skills-hotload **Out-of-band installers are admitted at the next turn.** The
|
|
3772
|
+
family keeps one signature of the installed roots per resident Worker; turn
|
|
3773
|
+
admission recomputes it under the workspace gate before packet assembly and
|
|
3774
|
+
republishes the family through the coordinator when it changed, so a skill
|
|
3775
|
+
installed or removed by any other tool is discoverable in the first subsequent
|
|
3776
|
+
model turn while an unchanged set dispatches nothing. The model manages skills
|
|
3777
|
+
only through the generated `EXEC [skills]` family
|
|
3778
|
+
({§functionality-model-projection}); it is never taught a package manager.
|
|
3779
|
+
|
|
3780
|
+
The catalog describes this worker's Functionality, not temporary authority. Loop
|
|
3442
3781
|
mode remains a dispatch concern: an ask-mode EXEC receives the ordinary exact
|
|
3443
3782
|
403 restriction instead of requiring a second per-loop documentation overlay.
|
|
3444
3783
|
Optional non-EXEC operations remain a separate `## Enabled Optional Operations`
|
|
@@ -3446,7 +3785,7 @@ section because they are language extensions rather than executable tools.
|
|
|
3446
3785
|
|
|
3447
3786
|
### §schemes user.schemes — the resource directory
|
|
3448
3787
|
|
|
3449
|
-
§schemes-directory A `## Resources` section renders in the system slot **after the policy sections** — a terse directory of the scheme families available this
|
|
3788
|
+
§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/skills/plurnk/<scheme>.md` and discovered via the turn-0 `## FIND0 [+init,+skills] (worker://~/_plurnk/skills/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.
|
|
3450
3789
|
|
|
3451
3790
|
### §inject system.inject — the operator injection
|
|
3452
3791
|
|
|
@@ -3454,7 +3793,7 @@ section because they are language extensions rather than executable tools.
|
|
|
3454
3793
|
|
|
3455
3794
|
### §policy system.policy — the client's policy injection
|
|
3456
3795
|
|
|
3457
|
-
§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
|
|
3796
|
+
§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}).
|
|
3458
3797
|
|
|
3459
3798
|
On first run, and only when `$XDG_CONFIG_HOME/plurnk` itself is absent, the service seeds
|
|
3460
3799
|
`AGENTS.md` from `@plurnk/plurnk-meta/PLURNK_PERSONALITY.md` ({§teaching-corpus}).
|
|
@@ -3475,7 +3814,7 @@ created by that attempt. Unknown legacy members or simultaneous
|
|
|
3475
3814
|
legacy/canonical state fail without guessing. No dual read or dual write survives
|
|
3476
3815
|
the transition.
|
|
3477
3816
|
|
|
3478
|
-
§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(
|
|
3817
|
+
§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 `/skills/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.
|
|
3479
3818
|
|
|
3480
3819
|
### §packet-git-status The Git status section — compact repository state
|
|
3481
3820
|
|
|
@@ -3485,7 +3824,7 @@ active direct child of a running branch batch additionally receives its assigned
|
|
|
3485
3824
|
branch and the requirement to commit any project changes and leave the checkout
|
|
3486
3825
|
clean before concluding ({§worker-branch-batch-return}); no other worker receives
|
|
3487
3826
|
that instruction. The section never repeats an unbounded path list. Per-path state belongs to
|
|
3488
|
-
the durable causal
|
|
3827
|
+
the runtime actor's durable causal evidence: its `source=file` row carries
|
|
3489
3828
|
the exact two-character porcelain `XY` value as `git` metadata when the status
|
|
3490
3829
|
snapshot names that path. The engine takes one snapshot after membership
|
|
3491
3830
|
reconciliation and uses it for both projections; no per-file Git process exists.
|
|
@@ -3645,7 +3984,7 @@ presentation aid, never part of canonical content; matchers and mutations
|
|
|
3645
3984
|
consume canonical bytes before rendering. A producer may set `startLine: null`
|
|
3646
3985
|
only when its content is already source-numbered, such as an effect receipt.
|
|
3647
3986
|
|
|
3648
|
-
§render-rule-find-renders-result A log row's canonical full body is resolved once by `LogBody`: READ/FIND, actionless source artifacts, prompt, and extension result content comes from `rx.content`; EDIT uses its structured receipt or an environment-delta span; COPY/MOVE concatenate the textual receipt contexts in their ordered `effects`;
|
|
3987
|
+
§render-rule-find-renders-result A log row's canonical full body is resolved once by `LogBody`: READ/FIND, actionless source artifacts, prompt, and extension result content comes from `rx.content`; EDIT uses its structured receipt or an environment-delta span; COPY/MOVE concatenate the textual receipt contexts in their ordered `effects`; PLAN serializes its canonical value through the shared {§json-result-rendering} spread as `application/json`; EXEC and SEND/WORK/FORK use their statement body. Whole-channel COPY/MOVE effects are bodyless rather than fabricating a text projection. Packet rendering applies {§body-projection} and the coordinate projection in {§render-rule-line-navigable-prefix}. READ/FIND over `log:///` and search consume the complete canonical body instead. Status and content are orthogonal: a failed terminal stream READ retains its Problem Details and failure status while rendering captured diagnostic output; failure never erases evidence.
|
|
3649
3988
|
|
|
3650
3989
|
An `EDIT` log row renders its bounded effect receipt (`rx.receipt`) as row
|
|
3651
3990
|
metadata and join context, not its input statement. Proposal-gated file EDITs
|