@plurnk/plurnk-service 1.23.0 → 1.25.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 +17 -15
- package/INSTALL.md +96 -20
- package/README.md +2 -2
- package/SPEC.md +519 -206
- package/dist/Paths.d.ts +1 -0
- package/dist/Paths.d.ts.map +1 -1
- package/dist/Paths.js +1 -0
- package/dist/Paths.js.map +1 -1
- package/dist/build-info.json +1 -1
- package/dist/core/AdministrativeLoop.d.ts +1 -1
- package/dist/core/AdministrativeLoop.d.ts.map +1 -1
- package/dist/core/AdministrativeLoop.js +5 -5
- package/dist/core/AdministrativeLoop.js.map +1 -1
- package/dist/core/AdmittedTurnExecutor.d.ts +3 -2
- package/dist/core/AdmittedTurnExecutor.d.ts.map +1 -1
- package/dist/core/AdmittedTurnExecutor.js +14 -1
- package/dist/core/AdmittedTurnExecutor.js.map +1 -1
- package/dist/core/CapabilityResolver.js +1 -1
- package/dist/core/CapabilityResolver.js.map +1 -1
- package/dist/core/DataStatementRunner.d.ts.map +1 -1
- package/dist/core/DataStatementRunner.js +1 -3
- package/dist/core/DataStatementRunner.js.map +1 -1
- package/dist/core/Dispatcher.d.ts +10 -0
- package/dist/core/Dispatcher.d.ts.map +1 -1
- package/dist/core/Dispatcher.js +71 -18
- package/dist/core/Dispatcher.js.map +1 -1
- package/dist/core/Dispatcher.sql +0 -7
- package/dist/core/Engine.d.ts +2 -2
- package/dist/core/Engine.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.d.ts +16 -4
- package/dist/core/ExecutorRegistry.d.ts.map +1 -1
- package/dist/core/ExecutorRegistry.js +31 -31
- package/dist/core/ExecutorRegistry.js.map +1 -1
- package/dist/core/FabricatedLog.d.ts +2 -0
- package/dist/core/FabricatedLog.d.ts.map +1 -1
- package/dist/core/FabricatedLog.js +18 -7
- package/dist/core/FabricatedLog.js.map +1 -1
- package/dist/core/HostPaths.d.ts +7 -1
- package/dist/core/HostPaths.d.ts.map +1 -1
- package/dist/core/HostPaths.js +25 -12
- package/dist/core/HostPaths.js.map +1 -1
- package/dist/core/LogEntryProjection.d.ts +1 -0
- package/dist/core/LogEntryProjection.d.ts.map +1 -1
- package/dist/core/LogEntryProjection.js +10 -0
- package/dist/core/LogEntryProjection.js.map +1 -1
- package/dist/core/LoopDriver.d.ts.map +1 -1
- package/dist/core/LoopDriver.js +8 -1
- package/dist/core/LoopDriver.js.map +1 -1
- package/dist/core/LoopLifecycle.d.ts +1 -1
- package/dist/core/LoopLifecycle.js +1 -1
- package/dist/core/LoopLifecycle.sql +2 -2
- package/dist/core/LoopPolicies.d.ts.map +1 -1
- package/dist/core/LoopPolicies.js +12 -7
- package/dist/core/LoopPolicies.js.map +1 -1
- package/dist/core/OperatorConfig.d.ts.map +1 -1
- package/dist/core/OperatorConfig.js +97 -51
- package/dist/core/OperatorConfig.js.map +1 -1
- package/dist/core/PacketBuilder.d.ts +2 -0
- package/dist/core/PacketBuilder.d.ts.map +1 -1
- package/dist/core/PacketBuilder.js +44 -32
- package/dist/core/PacketBuilder.js.map +1 -1
- package/dist/core/PacketBuilder.sql +3 -1
- package/dist/core/SchemeRegistry.d.ts.map +1 -1
- package/dist/core/SchemeRegistry.js +3 -2
- package/dist/core/SchemeRegistry.js.map +1 -1
- package/dist/core/TurnDispositionHandler.d.ts +1 -1
- package/dist/core/TurnDispositionHandler.d.ts.map +1 -1
- package/dist/core/TurnDispositionHandler.js +6 -15
- package/dist/core/TurnDispositionHandler.js.map +1 -1
- package/dist/core/TurnMaterialization.js +1 -1
- package/dist/core/TurnMaterialization.js.map +1 -1
- package/dist/core/TurnOps.d.ts +1 -0
- package/dist/core/TurnOps.d.ts.map +1 -1
- package/dist/core/TurnOps.js +18 -0
- package/dist/core/TurnOps.js.map +1 -1
- package/dist/core/TurnRunner.d.ts +6 -0
- package/dist/core/TurnRunner.d.ts.map +1 -1
- package/dist/core/TurnRunner.js +18 -8
- package/dist/core/TurnRunner.js.map +1 -1
- package/dist/core/TurnSources.sql +0 -15
- package/dist/core/env-defaults.d.ts +12 -1
- package/dist/core/env-defaults.d.ts.map +1 -1
- package/dist/core/env-defaults.js +46 -7
- package/dist/core/env-defaults.js.map +1 -1
- package/dist/core/file-creation-policy.d.ts.map +1 -1
- package/dist/core/file-creation-policy.js +2 -1
- package/dist/core/file-creation-policy.js.map +1 -1
- package/dist/core/packet-wire.d.ts +10 -2
- package/dist/core/packet-wire.d.ts.map +1 -1
- package/dist/core/packet-wire.js +87 -69
- package/dist/core/packet-wire.js.map +1 -1
- package/dist/core/results.d.ts +2 -0
- package/dist/core/results.d.ts.map +1 -1
- package/dist/core/results.js +7 -0
- package/dist/core/results.js.map +1 -1
- package/dist/digest/Digest.d.ts.map +1 -1
- package/dist/digest/Digest.js +10 -4
- package/dist/digest/Digest.js.map +1 -1
- package/dist/digest/DigestRender.d.ts.map +1 -1
- package/dist/digest/DigestRender.js +51 -22
- package/dist/digest/DigestRender.js.map +1 -1
- package/dist/digest/DigestRequiem.d.ts.map +1 -1
- package/dist/digest/DigestRequiem.js +10 -4
- package/dist/digest/DigestRequiem.js.map +1 -1
- package/dist/digest/digest-rows.d.ts +9 -1
- package/dist/digest/digest-rows.d.ts.map +1 -1
- package/dist/digest/digest-rows.js.map +1 -1
- package/dist/digest/digest.sql +16 -0
- package/dist/observe/init.d.ts +1 -0
- package/dist/observe/init.d.ts.map +1 -1
- package/dist/observe/init.js +5 -1
- package/dist/observe/init.js.map +1 -1
- package/dist/schemes/EffectPolicy.d.ts.map +1 -1
- package/dist/schemes/EffectPolicy.js +4 -4
- package/dist/schemes/EffectPolicy.js.map +1 -1
- package/dist/schemes/Exec.d.ts +1 -0
- package/dist/schemes/Exec.d.ts.map +1 -1
- package/dist/schemes/Exec.js +30 -14
- package/dist/schemes/Exec.js.map +1 -1
- package/dist/schemes/ExecScheduler.d.ts.map +1 -1
- package/dist/schemes/ExecScheduler.js +4 -3
- package/dist/schemes/ExecScheduler.js.map +1 -1
- package/dist/schemes/ExecScratch.d.ts.map +1 -1
- package/dist/schemes/ExecScratch.js +2 -1
- package/dist/schemes/ExecScratch.js.map +1 -1
- package/dist/schemes/File.d.ts.map +1 -1
- package/dist/schemes/File.js +15 -10
- package/dist/schemes/File.js.map +1 -1
- package/dist/schemes/Log.d.ts.map +1 -1
- package/dist/schemes/Log.js +25 -3
- package/dist/schemes/Log.js.map +1 -1
- package/dist/server/AgentRoots.d.ts +6 -0
- package/dist/server/AgentRoots.d.ts.map +1 -0
- package/dist/server/AgentRoots.js +25 -0
- package/dist/server/AgentRoots.js.map +1 -0
- package/dist/server/ConfigurationDiagnostics.d.ts +12 -0
- package/dist/server/ConfigurationDiagnostics.d.ts.map +1 -0
- package/dist/server/ConfigurationDiagnostics.js +40 -0
- package/dist/server/ConfigurationDiagnostics.js.map +1 -0
- package/dist/server/Daemon.d.ts +11 -5
- package/dist/server/Daemon.d.ts.map +1 -1
- package/dist/server/Daemon.js +124 -48
- package/dist/server/Daemon.js.map +1 -1
- package/dist/server/DaemonModule.d.ts +11 -4
- package/dist/server/DaemonModule.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.d.ts.map +1 -1
- package/dist/server/EnvFunctionality.js +5 -2
- package/dist/server/EnvFunctionality.js.map +1 -1
- package/dist/server/Functionality.d.ts +6 -0
- package/dist/server/Functionality.d.ts.map +1 -1
- package/dist/server/Functionality.js +173 -37
- package/dist/server/Functionality.js.map +1 -1
- package/dist/server/FunctionalityManager.js +6 -6
- package/dist/server/FunctionalityManager.js.map +1 -1
- package/dist/server/MembersFunctionality.d.ts.map +1 -1
- package/dist/server/MembersFunctionality.js +13 -41
- package/dist/server/MembersFunctionality.js.map +1 -1
- package/dist/server/PluginSources.d.ts +20 -0
- package/dist/server/PluginSources.d.ts.map +1 -0
- package/dist/server/PluginSources.js +52 -0
- package/dist/server/PluginSources.js.map +1 -0
- package/dist/server/PlurnkSkill.js +1 -1
- package/dist/server/PlurnkSkill.js.map +1 -1
- package/dist/server/Retention.d.ts.map +1 -1
- package/dist/server/Retention.js +11 -30
- package/dist/server/Retention.js.map +1 -1
- package/dist/server/ServiceModules.d.ts +1 -0
- package/dist/server/ServiceModules.d.ts.map +1 -1
- package/dist/server/ServiceModules.js +9 -3
- package/dist/server/ServiceModules.js.map +1 -1
- package/dist/server/SkillSource.d.ts +37 -0
- package/dist/server/SkillSource.d.ts.map +1 -0
- package/dist/server/SkillSource.js +267 -0
- package/dist/server/SkillSource.js.map +1 -0
- package/dist/server/SkillsFunctionality.d.ts +8 -27
- package/dist/server/SkillsFunctionality.d.ts.map +1 -1
- package/dist/server/SkillsFunctionality.js +218 -270
- package/dist/server/SkillsFunctionality.js.map +1 -1
- package/dist/server/WorkerModelResolver.d.ts +1 -2
- package/dist/server/WorkerModelResolver.d.ts.map +1 -1
- package/dist/server/WorkerModelResolver.js +18 -19
- package/dist/server/WorkerModelResolver.js.map +1 -1
- package/dist/server/WorkspacePlugins.d.ts +25 -0
- package/dist/server/WorkspacePlugins.d.ts.map +1 -0
- package/dist/server/WorkspacePlugins.js +43 -0
- package/dist/server/WorkspacePlugins.js.map +1 -0
- package/dist/server/dispatch-as-plurnk.d.ts.map +1 -1
- package/dist/server/dispatch-as-plurnk.js +3 -1
- package/dist/server/dispatch-as-plurnk.js.map +1 -1
- package/dist/server/envelope.js +1 -1
- package/dist/server/envelope.js.map +1 -1
- package/dist/server/loop-model.d.ts.map +1 -1
- package/dist/server/loop-model.js +1 -2
- package/dist/server/loop-model.js.map +1 -1
- package/dist/server/module-discovery.d.ts +6 -0
- package/dist/server/module-discovery.d.ts.map +1 -1
- package/dist/server/module-discovery.js +46 -24
- package/dist/server/module-discovery.js.map +1 -1
- package/dist/server/skills-problems.d.ts +8 -0
- package/dist/server/skills-problems.d.ts.map +1 -0
- package/dist/server/skills-problems.js +18 -0
- package/dist/server/skills-problems.js.map +1 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +55 -45
- package/dist/service.js.map +1 -1
- package/docs/env.md +7 -8
- package/docs/members.md +4 -3
- package/docs/skills.md +45 -22
- package/migrations/012_emission.sql +77 -0
- package/package.json +38 -36
- package/plurnk.service +29 -0
- package/dist/core/PreviousEmission.d.ts +0 -14
- package/dist/core/PreviousEmission.d.ts.map +0 -1
- package/dist/core/PreviousEmission.js +0 -24
- package/dist/core/PreviousEmission.js.map +0 -1
package/SPEC.md
CHANGED
|
@@ -133,7 +133,7 @@ package map. The default installed composition is specified in {§bundled-set}.
|
|
|
133
133
|
|
|
134
134
|
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.
|
|
135
135
|
|
|
136
|
-
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
|
|
136
|
+
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 makes observability unavailable with a configuration diagnostic ({§configuration-repair-path}); offline checking rejects the same input before loading an SDK. 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.
|
|
137
137
|
|
|
138
138
|
§observability-genai-conventions **GenAI convention projection.** Provider
|
|
139
139
|
request spans follow the [GenAI registry at `c88d504`](https://github.com/open-telemetry/semantic-conventions-genai/tree/c88d504ab3d9879f8e50d3cc87e69775e11db234),
|
|
@@ -188,12 +188,13 @@ an explicitly specified subpath, not in the frozen root barrel.
|
|
|
188
188
|
```mermaid
|
|
189
189
|
flowchart LR
|
|
190
190
|
LISTENER["Bind client listener<br/>unready: HTTP 503"] --> DB["Acquire daemon lock<br/>and admit SQLite schema"]
|
|
191
|
-
DB -->
|
|
192
|
-
PROVIDER --> DAEMON["Construct and start<br/>daemon composition"]
|
|
191
|
+
DB --> DAEMON["Construct and start<br/>daemon composition"]
|
|
193
192
|
DAEMON --> CLIENT["Activate client transport"]
|
|
193
|
+
CLIENT --> SELECT["Worker selects or first uses a model"]
|
|
194
|
+
SELECT --> PROVIDER["Construct and verify<br/>selected provider"]
|
|
194
195
|
LISTENER -. failure .-> FAIL["Fail startup<br/>durable state untouched"]
|
|
195
196
|
DB -. failure .-> CLOSE_LISTENER["Close listener"] --> FAIL
|
|
196
|
-
PROVIDER -. failure .->
|
|
197
|
+
PROVIDER -. failure .-> REPAIR["Return selection failure<br/>client remains available"]
|
|
197
198
|
DAEMON -. failure .-> TEARDOWN["Close every started owner"] --> FAIL
|
|
198
199
|
```
|
|
199
200
|
|
|
@@ -220,14 +221,17 @@ address by URL, never by port (#641): AG-UI mounts `/` and `/agui`, A2A the
|
|
|
220
221
|
well-known card and its endpoint path, on the same address.
|
|
221
222
|
|
|
222
223
|
§startup-admission-order After listener ownership, database admission completes
|
|
223
|
-
before
|
|
224
|
+
before capability initialization can perform external work. Provider construction
|
|
225
|
+
and endpoint verification occur on worker selection or first use, never merely
|
|
226
|
+
because a default selector is configured. Startup validates selectors without
|
|
227
|
+
provider I/O and retains their configuration diagnostics. Every
|
|
224
228
|
later startup failure closes resources in reverse ownership order while
|
|
225
229
|
preserving the originating failure: daemon, observability, database, listener.
|
|
226
230
|
|
|
227
231
|
§startup-readiness-line **Readiness is one stdout line.** After the client interface is mounted
|
|
228
232
|
the service prints exactly one line, `plurnk-service agui=<url> db=<json string> route=<json string>`:
|
|
229
233
|
the URL brackets an IPv6 host, and the database path and the route (the active model route or
|
|
230
|
-
`no model`) are JSON strings, so a path or route containing spaces is exact and a consumer parses
|
|
234
|
+
`no model`, or `invalid model configuration`) are JSON strings, so a path or route containing spaces is exact and a consumer parses
|
|
231
235
|
the URL as a URL and the strings as JSON; nothing else the service prints on stdout before it has
|
|
232
236
|
that prefix. Before the line the listener answers `503 service-starting`; after it,
|
|
233
237
|
`discover` is the identity check a launcher uses to tell this daemon from any other listener. A bind
|
|
@@ -378,7 +382,7 @@ direct-entry-plus-directory count; `-1` enables the ordinary markerless page;
|
|
|
378
382
|
unset / `0` disables previews. `log://` is absent because the current worker's
|
|
379
383
|
log already renders in present mode.
|
|
380
384
|
|
|
381
|
-
§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}. Its reasoning and program are stored before execution. NOTEs from its reasoning and program, the orienting READ/FIND surveys, and the reasoning READ in {§reasoning-initial-read} execute under {§op-execution-order}. Its program supplies the worked example as the first request's assistant message ({§packet-wire-envelope}); no program READ, actionless source row or simulated READ is added. Every orienting row is structurally classified `_plurnk` and `init`. The namespace surveys and their asides follow {§actor-boundary-catalog-preview}.
|
|
385
|
+
§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}. Its reasoning and program are stored before execution. NOTEs from its reasoning and program, the orienting READ/FIND surveys, and the reasoning READ in {§reasoning-initial-read} execute under {§op-execution-order}. Its program is announced at `log:///1/1/1/emission` ({§emission-row}) and supplies the worked example as the first request's assistant message ({§packet-wire-envelope}); no program READ, actionless source row or simulated READ is added. Every orienting row is structurally classified `_plurnk` and `init`. The namespace surveys and their asides follow {§actor-boundary-catalog-preview}.
|
|
382
386
|
|
|
383
387
|
Incoming messages publish once as inbound SEND rows in the first model turn
|
|
384
388
|
({§message-arrival}); initialization neither READs nor archives them. The turn
|
|
@@ -600,15 +604,13 @@ Every admitted authority is a literal `workers.name`; self-addressing uses the c
|
|
|
600
604
|
`"parent": null` at a root, so a worker never infers its rank from silence.
|
|
601
605
|
- §packet-current-turn **The packet says who and which turn, below the log.** The
|
|
602
606
|
`## Worker` block is the first section after the log, carrying
|
|
603
|
-
`{"path": "worker://<name>", "parent": <address or null>, "loop": L, "turn": T
|
|
604
|
-
|
|
605
|
-
and `ops://<worker>/L/T` are the model's own and `log:///L/T/*` its rows
|
|
606
|
-
the
|
|
607
|
-
are its receipts; a model never infers the present from the last row's coordinate, which may or may
|
|
608
|
-
not be its own turn. The block
|
|
607
|
+
`{"path": "worker://<name>", "parent": <address or null>, "loop": L, "turn": T}`: the actor,
|
|
608
|
+
whose child it is, and the coordinate this packet's response becomes, so `reasoning://<worker>/L/T`
|
|
609
|
+
and `ops://<worker>/L/T` are the model's own and `log:///L/T/*` its rows; a model never infers the
|
|
610
|
+
present from the last row's coordinate, which may or may not be its own turn. The block
|
|
609
611
|
changes every turn, so nothing of it precedes the log, and the packet carries no date, time
|
|
610
|
-
or zone anywhere. The
|
|
611
|
-
|
|
612
|
+
or zone anywhere. The coordinate only; the source addresses stay
|
|
613
|
+
documented, not taught.
|
|
612
614
|
|
|
613
615
|
Worker control rides the daemon's inject seam (active→fold, idle→enqueue+drain), so the handler creates/branches the worker and hands off; the daemon owns provider + system prompt. FORK/WORK carry the seed task in the body and are their own ops, dispatched to worker control — never the entry-copy path.
|
|
614
616
|
|
|
@@ -1051,7 +1053,7 @@ boundary.
|
|
|
1051
1053
|
- §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.
|
|
1052
1054
|
- §worker-lifecycle-child-wake **Each child task completion notifies its parent.** Terminal-task publication, including failure and cancellation of a parked task, notifies the direct parent without injecting a prompt. Other unfinished tasks or streams in that child remain independent obligations; they cannot suppress notification. The parent's eligible waits requeue in place under {§loop-wake-identity} and the bounded {§worker-optimistic-settlement} opportunity. Durable revisioning covers completion-before-park and restart; drain teardown and whole-worker quiescence are not completion identities.
|
|
1053
1055
|
- §worker-optimistic-settlement **Asynchronous settlement receives one bounded worker-local opportunity before model dispatch.** An initiating turn lets only the streams it started settle before program completion; separately, a stream conclusion, direct-child conclusion or addressed reply persists and publishes immediately but holds eligible parked loops' `202→100` requeues while another stream or direct child remains live. Both use `PLURNK_SERVICE_OPTIMISTIC_WAIT_MS`, shipped at five seconds; zero disables the opportunity. The wake hold ends as soon as no sibling obligation remains, never extends its original deadline, and coalesces arrivals within that window into at most one requeue per eligible loop. With no sibling obligation the wake is immediate; at the deadline, surviving work follows the ordinary monitored lifecycle. An arrival after provider dispatch begins retains its next wake, while poll, new-request and operator wakes never open this hold. Only packet/provider dispatch waits: durable state, client events, cancellation and the replying program do not. One redaction-safe span records elapsed time, quiescence versus deadline, and arrival count without entering the packet.
|
|
1054
|
-
- §worker-lifecycle-idle-is-concluded **Idle is not
|
|
1056
|
+
- §worker-lifecycle-idle-is-concluded **Idle is not completion.** An empty WAIT continues; an eligible parameterless KILL with observed arrivals and results and no held work concludes under {§wait-obligation-matrix}, with or without an answer body. A concluded worker retains durable history; a later addressed arrival starts a new loop.
|
|
1055
1057
|
- §worker-lifecycle-no-lost-loop **A loop is never stranded by a drain's exit.** A drain relinquishes its registry slot only after a lock-held re-claim confirms the queue is empty; a loop enqueued during that teardown is either re-claimed by the exiting drain or claimed by a fresh drain that a later inject starts. The relinquish and the start are serialized, so neither the lost-loop hang nor a transient double-drain can occur.
|
|
1056
1058
|
- §worker-lifecycle-durable-disposition **Durable disposition wins cancellation races.** At a turn boundary, the engine reads the loop's durable status before interpreting a process-local abort. A committed `202` park survives a later daemon-shutdown signal; only a loop still durably running at `102` can be terminalized by that cancellation. Wake selection rechecks shutdown and worker cancellation before requeuing each parked loop.
|
|
1057
1059
|
- §worker-lifecycle-restart-recovery **Restart is owner-loss reconciliation, not replay.** Before opening client transports, the service holds an exclusive database-adjacent daemon lock; a second live owner fails before touching SQLite, while a dead-PID crash claim is replaced atomically without a timeout lease. Boot preserves accepted `100` loops and restores their drains. A `102` loop belonged to a vanished drain/provider call, so it settles `500` with the interruption on its durable row—never replayed across an unknown effect boundary. Every pending physical provider request first settles as an error with absent usage and explicitly unknown cost; then its logical model call closes. Recovery never fabricates zero evidence. Every durable proposed operation likewise lost its process-local resolution waiter and settles as a visible `500 owner_vanished` occurrence rather than an unresolvable interrupt ({§proposal-list}). A pending client interaction also lost its exact awaiting operation, so boot removes the orphan instead of replaying work or inventing a response ({§client-interactions}). Every durable-open subscription belonged to a vanished callable: active channels become errored and its row closes `500`. A `202` continuation requeues on an unseen completion or when no live obligation remains. Otherwise it stays parked on surviving children; the drain restores inherited stream observation through the same guarded scheduler ({§worker-wait-timing}). Child terminalization wakes its parked parent on every outcome, including provider exceptions, cancellation, and restart interruption, recursively through the durable parent edges. These operations are idempotent, so an interrupted recovery safely repeats.
|
|
@@ -1095,7 +1097,8 @@ append-only log high-water mark. Every log-targeted KILL in the program resolves
|
|
|
1095
1097
|
row membership at or below that same boundary, while prior curation effects still
|
|
1096
1098
|
compose normally. Message arrivals and other pre-program rows already present in the turn
|
|
1097
1099
|
remain selectable; preceding and later operation rows cannot be captured by
|
|
1098
|
-
their own program.
|
|
1100
|
+
their own program. The emission row ({§emission-row}) is written after the snapshot, so the
|
|
1101
|
+
program it announces cannot select it; a later program can. A directly dispatched
|
|
1099
1102
|
single operation captures the equivalent boundary before dispatch. This limits
|
|
1100
1103
|
only log-row selection: operation phasing and same-turn resource effects retain
|
|
1101
1104
|
their ordinary contracts.
|
|
@@ -1279,10 +1282,10 @@ The parser owns its boundaries; core admits determinate work and exposes its fai
|
|
|
1279
1282
|
| Outside response text | Store it as the turn's `outside` source under {§outside-text}; never a row, never delivered, never completion. |
|
|
1280
1283
|
| Lost boundary after a closed operation | Admit the closed operations and record the boundary diagnostic under {§unparsed-tail-boundary}. |
|
|
1281
1284
|
| Lost boundary before any closed operation | Reject the attempt; neither outside text nor a reasoning NOTE substitutes for a closed response operation. |
|
|
1282
|
-
| Outside text carrying a log-entry heading | Reject the attempt ({§fabricated-log-entry}). |
|
|
1285
|
+
| Outside text carrying a log-entry heading, other than an emission row's | Reject the attempt ({§fabricated-log-entry}). |
|
|
1283
1286
|
| A response the provider stopped at a repeated line | Reject the attempt with the provider's sentence as its diagnostic ({§repetition-stop}); no provider recovery, notice, or problem row. |
|
|
1284
1287
|
|
|
1285
|
-
§fabricated-log-entry **Only the harness writes the log.** A line of outside response text that begins with a log-entry heading, `### log:///<loop>/<turn>/<sequence>/` ({§log-wire-format}), is the model continuing the packet's transcript instead of answering it: it writes the receipts it expects and then acts on them. The attempt is rejected under {§invalid-emission-attempts}, so neither that text nor any operation beside it runs or is stored as outside text ({§outside-text}), and its one diagnostic, at the heading's line, reads `` `### log:///2/1/5/READ` is a log entry, and only the harness writes the log. Write the operation, then wait for its receipt. `` Text inside an operation body is not examined, so a SEND or KILL may quote a receipt. In 10,486 recorded emissions, 101 carried such a heading in outside text, every one a fabrication: 85 of 1,675 from deepseek-flash, 81 of them opening with one, and 16 from glm-5.3-flash, deepseek-v4-pro and qwen3.8-flash, which appended an invented `## Log` after their own operations.
|
|
1288
|
+
§fabricated-log-entry **Only the harness writes the log.** A line of outside response text that begins with a log-entry heading, `### log:///<loop>/<turn>/<sequence>/` ({§log-wire-format}), is the model continuing the packet's transcript instead of answering it: it writes the receipts it expects and then acts on them. The attempt is rejected under {§invalid-emission-attempts}, so neither that text nor any operation beside it runs or is stored as outside text ({§outside-text}), and its one diagnostic, at the heading's line, reads `` `### log:///2/1/5/READ` is a log entry, and only the harness writes the log. Write the operation, then wait for its receipt. `` Text inside an operation body is not examined, so a SEND or KILL may quote a receipt. A heading whose leaf is `emission` is exempt: the transcript shows it before each of the worker's own emissions ({§emission-row}), and repeating it invents no receipt, so the attempt is admitted and the heading stays outside text, counted by the digest as an echo. In 10,486 recorded emissions, 101 carried such a heading in outside text, every one a fabrication: 85 of 1,675 from deepseek-flash, 81 of them opening with one, and 16 from glm-5.3-flash, deepseek-v4-pro and qwen3.8-flash, which appended an invented `## Log` after their own operations.
|
|
1286
1289
|
|
|
1287
1290
|
Warnings and closer recovery ({§closer-fallback}) do not reject. `finish=length`
|
|
1288
1291
|
discloses truncation and precludes completion; it is not independently a rejection.
|
|
@@ -1530,6 +1533,8 @@ invalid range, read-only authority, and occupied hidden state without guessing.
|
|
|
1530
1533
|
|
|
1531
1534
|
§file-directory-target **A directory is named as a directory.** Inside the root, a READ (or other exact-path read), KILL or EDIT whose target is a directory on disk — with or without a trailing slash — is refused `path-is-directory`, never as a missing or non-member file, since admitting it is not what the model needs: READ and KILL answer 404, EDIT 403. The detail is `'<key>' is a directory, not a file; <OP> reads/removes/writes one file.` and the recovery names the listing that reaches its files, `` List its files with `FIND (<key>/)`, then READ one by its path. `` (KILL: `then KILL each by its path`; EDIT: `` Name a file inside it, as `EDIT (<key>/<file>)`; list its files with `FIND (<key>/)`. ``). Beyond the root the disk stays dark and {§membership-read-refusal} holds unchanged.
|
|
1532
1535
|
|
|
1536
|
+
§file-find-directory **FIND recognizes existing directories without requiring a trailing slash.** An exact target naming a directory inside the workspace root resolves to the same recursive collection as its slash-suffixed spelling, including content matching and result pagination. Only workspace members appear; an empty directory or one containing only non-members yields a successful empty survey. Exact files stay exact, genuinely absent paths retain their missing-entry error, and this resolution does not inspect disk paths beyond the root. Shared glob and non-file URI semantics are unchanged.
|
|
1537
|
+
|
|
1533
1538
|
### §scheme-manifest Manifest
|
|
1534
1539
|
|
|
1535
1540
|
§scheme-manifest-manifest Per the framework-owned author contract ({§manifest}), each registered scheme exposes one closed `SchemeManifest`. `Manifest.of` validates the complete declaration and enforces that `manifest.name` matches `package.json#plurnk.name` before registration.
|
|
@@ -2175,7 +2180,7 @@ same transitions the dispatcher's atomic curation event makes, without the row.
|
|
|
2175
2180
|
The initialization turn records a short `_plurnk`-authored rationale containing
|
|
2176
2181
|
a fenced NOTE. The shared reasoning extractor ({§reasoning-notes}) executes that
|
|
2177
2182
|
NOTE through ordinary dispatch, creating its log item and immutable source.
|
|
2178
|
-
The program begins with its own NOTE and READs its reasoning
|
|
2183
|
+
The program begins with its own NOTE and READs its reasoning,
|
|
2179
2184
|
demonstrating both NOTE placements and their ordinary results. The initial message arrives separately as an
|
|
2180
2185
|
inbound SEND ({§message-arrival}). Neither initialization nor later turns
|
|
2181
2186
|
manufacture a task inventory.
|
|
@@ -2199,7 +2204,7 @@ a turn whose emission or reasoning carries a foreign tool-call grammar or leaked
|
|
|
2199
2204
|
|
|
2200
2205
|
AST: `{ op: "KILL", target, matcher: MatcherBody | null, lineMarker: TextLineMarker | null, body: null }` ({§kill-scope} and {§matcher-option} in the contracts SPEC own the grammar).
|
|
2201
2206
|
|
|
2202
|
-
KILL deletes context from the **log** (`log:///`, {§packet}). Without a scope it retires the selected rows from the active projection ({§log-history-projection}). With a one-line or inclusive two-line scope it removes only that body's intersecting body-relative physical lines from the readable projection, and the row stays active. An anchor may be one published on that body or one returned by READing its `log:///` coordinate ({§line-anchors}); an anchor absent from the current body selects no line, as with an out-of-bounds numeric line. Scoped KILL is one-way: intervals accumulate, the durable body is untouched, and subsequent access follows {§log-readable-projection}. A scoped KILL on a bodyless row is a friendly 200 no-op with `matched` reported. A KILL that addresses no row is 404 on an exact coordinate and 204 on a sweep ({§log-curation-folder-idiom}). Selection composes target/glob with an optional heading pattern ({§log-curation-set-selection}). Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2207
|
+
KILL deletes context from the **log** (`log:///`, {§packet}). Without a scope it retires the selected rows from the active projection ({§log-history-projection}). With a one-line or inclusive two-line scope it removes only that body's intersecting body-relative physical lines from the readable projection, and the row stays active. An anchor may be one published on that body or one returned by READing its `log:///` coordinate ({§line-anchors}); an anchor absent from the current body selects no line, as with an out-of-bounds numeric line. Scoped KILL is one-way: intervals accumulate, the durable body is untouched, and subsequent access follows {§log-readable-projection}. A scoped KILL on a bodyless row is a friendly 200 no-op with `matched` reported. An emission row is curated whole ({§emission-row}): a scope covering every line retires it like an unscoped KILL; on its exact coordinate a narrower scope is 422 `emission-curated-whole`, whose recovery names both forms that retire it; a sweep leaves it intact. A KILL that addresses no row is 404 on an exact coordinate and 204 on a sweep ({§log-curation-folder-idiom}). Selection composes target/glob with an optional heading pattern ({§log-curation-set-selection}). Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2203
2208
|
|
|
2204
2209
|
§log-scope-recovery A log-body scope follows the file slicer's range rule ({§range-starts-at-one} in the schemes SPEC): a range starting at 0 — `<0,-1>` included — is refused 416 `range-not-satisfiable` on every body, empty ones too, and never clamped; its detail is the slicer's own sentence, `Range <0,-1> starts at 0, which is not a line; lines are numbered from 1.`, and its recovery names the forms a log body takes — `Write <1,-1> to trim every line of the body; KILL (log:///1/9/2/READ) with no scope retires the whole row.`, or `To trim lines 1 through M, write <1,M>; …` — never the insert and append positions a body cannot take. Every other scope that names no line is 400 `curation-scope-invalid` and likewise names the model's mistake in its coordinates and the forms that work on that row: `<0>` offers `<1>`; an end below 1 offers `<L,-1>`; a backward `<5,3>` offers `<3,5>`; anything else offers `<L>`, `<L,M>` and the unscoped row KILL.
|
|
2205
2210
|
|
|
@@ -2209,7 +2214,7 @@ A READ carrying active native media is atomic: any KILL scope is ignored and the
|
|
|
2209
2214
|
|
|
2210
2215
|
| Fact | Owner | Effect |
|
|
2211
2216
|
| --- | --- | --- |
|
|
2212
|
-
| Initial body suppression | Immutable event `initial_folded` | Packet presentation only; explicit retrieval can read an initially hidden body. |
|
|
2217
|
+
| Initial body suppression | Immutable event `initial_folded` | Packet presentation only; explicit retrieval can read an initially hidden body. An emission row's hidden body reaches the packet as its assistant message ({§emission-row}). |
|
|
2213
2218
|
| Deliberate scoped KILL | Current projection `folded`, initially empty | Packet, READ, FIND, COPY, and search omit those lines; later retrieval cannot undo trimming. |
|
|
2214
2219
|
|
|
2215
2220
|
Packet display combines both masks. Other consumers use only deliberate trimming.
|
|
@@ -2243,7 +2248,7 @@ The `## Log` section is a sequence of ordinary Markdown records separated by one
|
|
|
2243
2248
|
| facts | One strict JSON object in stable alphabetical order. | Present only when a fact exists. Asides, scopes, opaque invocation metadata and result facts belong here, not on the H3. |
|
|
2244
2249
|
| body | Coordinate-prefixed lines. | Present when the row is visible. |
|
|
2245
2250
|
|
|
2246
|
-
Patterns retain their literal spelling; a pattern containing a line break is JSON-quoted to keep the H3 on one physical line. Receipts are descriptive records, not reconstructed operation headings. Absent fields are not invented. Every physical body line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, so source text cannot create a record boundary. The section contains records only, with no leading prose or enclosing fence.
|
|
2251
|
+
An emission row's body is not in its record: it follows the record as the worker's assistant message ({§packet-wire-envelope}). Patterns retain their literal spelling; a pattern containing a line break is JSON-quoted to keep the H3 on one physical line. Receipts are descriptive records, not reconstructed operation headings. Absent fields are not invented. Every physical body line retains its canonical numeric `N:` or anchored `@hash N:` coordinate, so source text cannot create a record boundary. The section contains records only, with no leading prose or enclosing fence.
|
|
2247
2252
|
|
|
2248
2253
|
§log-address-metadata **Addresses name their relationship, not the row's producer.**
|
|
2249
2254
|
|
|
@@ -2293,7 +2298,7 @@ complete non-retrieval body retains `lines` where no other field supplies its
|
|
|
2293
2298
|
navigable extent. None of these spellings changes acquisition, delivery,
|
|
2294
2299
|
curation, admission, or immutable evidence.
|
|
2295
2300
|
|
|
2296
|
-
Field absence carries defaults: `origin` is omitted for the owning model, `source` for the owning worker, and `status` for a routine 200. Dispositions always carry their lifecycle status, SEND its delivery status, KILL keeps an explicit 200, and every non-200 stays explicit. A present authored aside appears as `aside`. Every row's accounting follows {§packet-token-accounting}.
|
|
2301
|
+
Field absence carries defaults: `origin` is omitted for the owning model, `source` for the owning worker, and `status` for a routine 200. An emission row renders its author, the turn's producer, as its `origin` ({§emission-row}). Dispositions always carry their lifecycle status, SEND its delivery status, KILL keeps an explicit 200, and every non-200 stays explicit. A present authored aside appears as `aside`. Every row's accounting follows {§packet-token-accounting}.
|
|
2297
2302
|
|
|
2298
2303
|
Authored `metadata` retains its opaque ordered block strings under {§scheme-metadata-modifier}; COPY/MOVE pair them as `{from,to}`. Packet rendering does not interpret scheme options or discard malformed input from a failed operation.
|
|
2299
2304
|
|
|
@@ -2325,7 +2330,7 @@ Authored `metadata` retains its opaque ordered block strings under {§scheme-met
|
|
|
2325
2330
|
records the exact READ coordinates sent without controlling retention. Missing immutable bytes are an
|
|
2326
2331
|
internal integrity failure, never silently dropped content. No ejection message or permanent teaching is
|
|
2327
2332
|
added. These stable curation weights are not provider-token measurements ({§tokenomics-render-weight-budget}).
|
|
2328
|
-
- §packet-token-accounting Every row reports one `logTokens` charge on its H3 ({§log-wire-format}): its complete materialized H3, facts, visible body,
|
|
2333
|
+
- §packet-token-accounting Every row reports one `logTokens` charge on its H3 ({§log-wire-format}): its complete materialized H3, facts, visible body, selected native attachment, and the emission it delivers outside its record ({§emission-row}). The completed record is measured to a fixed point, including the accounting field itself. No `tokensBody`, `tokensMetadata`, or `tokensActive` field is serialized. Hidden text is not charged; metadata-only rows still have a reclaimable charge. Source/FIND-item `tokens` measure source content, not the observation's context footprint. A FIND's nonzero `itemsTokenTotal` weighs the complete matched set; a nonzero `returnedItemsTokenTotal` appears only when the returned page differs. All use stable curation weights, not provider tokens or dollars. Native component accounting follows {§packet-attachment-parts}; ordinary addressability and truthful errors follow {§log-wire-format}.
|
|
2329
2334
|
|
|
2330
2335
|
### §retrieval-packet-metadata READ/FIND packet metadata
|
|
2331
2336
|
|
|
@@ -2371,14 +2376,34 @@ single line past the end, a reversed range, empty content, a command's log row
|
|
|
2371
2376
|
|
|
2372
2377
|
### §turn-ops-entry The admitted turn program
|
|
2373
2378
|
|
|
2374
|
-
§turn-ops-log-curation A source-backed turn preserves its **exact admitted Plurnk program**, including ignored interstitial text, before dispatch. `turn_sources` records that source once, separately from the curatable log and optional provider evidence. Retention does not manufacture a log row. Ordinary READ creates a receipt governed by {§log-readable-projection}; curation of
|
|
2379
|
+
§turn-ops-log-curation A source-backed turn preserves its **exact admitted Plurnk program**, including ignored interstitial text, before dispatch. `turn_sources` records that source once, separately from the curatable log and optional provider evidence. Retention does not manufacture a log row; the one row an admitted emission gains is its announcement ({§emission-row}). Ordinary READ creates a receipt governed by {§log-readable-projection}; curation of either never changes the source.
|
|
2380
|
+
|
|
2381
|
+
### §emission-row The emission row
|
|
2382
|
+
|
|
2383
|
+
Every admitted emission is announced by one row of its own turn, so the transcript carries the
|
|
2384
|
+
worker's emissions in chronological place ({§packet-wire-envelope}) and the worker curates them
|
|
2385
|
+
like any other row.
|
|
2386
|
+
|
|
2387
|
+
| Surface | Contract |
|
|
2388
|
+
|---|---|
|
|
2389
|
+
| When | An inference turn that admitted at least one statement from its provider content, and turn zero's survey ({§worker-initialization-entry}). A programmatic batch, an empty turn ({§empty-turn}), a client operation and a rejected attempt ({§rejected-emission-entry}) announce nothing. |
|
|
2390
|
+
| Place | After the turn's inputs (arrivals, deltas, open-path READs) and before its reasoning NOTEs and operations, written after the selection snapshot ({§turn-ops-selection-snapshot}): the emission sits between what the worker had seen and what it caused. |
|
|
2391
|
+
| Row | A `_plurnk` READ of the turn's own source, `ops://<worker>/L/T`, with `attrs.kind="emission"` and the canonical leaf `/emission`: `### log:///L/T/S/emission → ops://<worker>/L/T · N`. It renders its author, the turn's producer, as `origin`, so a model's row carries none. It is no operation: no receipt, tool call or strike, and outside the op mix. |
|
|
2392
|
+
| Body | Frozen at announcement: the canonical rendering ({§statement-rendering}) of every admitted content statement, in order, each in a closed fence. Each body appears within the shared preview bound ({§body-projection}), `PLURNK_SERVICE_PREVIEW_LINES` and `PLURNK_SERVICE_PREVIEW_CHARS`. A longer body keeps its head, and its closing fence carries `<!-- Automatically truncated op body: READ (ops://<worker>/L/T) to retrieve in full -->`; nothing the harness writes enters a fence. Absent and empty bodies remain empty. All heading operands, scopes, metadata, patterns and asides remain. Free text and unadmitted forms are absent; a recovered native call ({§native-tool-calls}) appears as the operation it was read as; an operation whose receipt failed stays. Turn zero uses the same projection of its survey. No body text is inspected for nested operations. |
|
|
2393
|
+
| Sources and memory | Dispatch and immutable `ops://` sources retain complete bodies ({§turn-source-resources}); an explicit source READ returns them normally. The wire omits NOTE and WAIT blocks, whose own rows show them whole ({§body-projection}), so curating a NOTE row removes its text; an emission of only NOTE and WAIT delivers nothing, its row stands, and no assistant message follows it ({§packet-wire-envelope}); reasoning-only NOTEs never enter this projection. |
|
|
2394
|
+
| Stability | The projection is fixed from its first appearance, never aged or resized under budget pressure. Already frozen announcements and historical request captures are not rewritten. |
|
|
2395
|
+
| Presentation | Born folded: the record shows its header, and its body follows the record as the worker's assistant message. |
|
|
2396
|
+
| Accounting | `logTokens` charges the record and the emission the wire delivers, truncation asides included, never the omitted NOTE and WAIT blocks or the cut remainder ({§packet-token-accounting}). An explicit source READ has its own ordinary charge. |
|
|
2397
|
+
| Curation | Curated whole ({§log-kill-scope}): KILL retires it, and so does a scope covering every line (`<1,-1>`); on its exact coordinate a narrower scope is 422 `emission-curated-whole`, and a sweep whose scope would only trim it leaves it intact. |
|
|
2398
|
+
| Schema | Migration 12 admits `kind="emission"` only on this shape: one per turn, the turn's newest row when written, frozen, and curated whole. A database from before version 12 keeps its rows and gains no announcement. FORK copies it with the inherited turns, still naming its writer. |
|
|
2399
|
+
| Echoes | A worker that repeats the heading in its own text is tolerated ({§fabricated-log-entry}); the digest counts the echoes. A worker that copies the truncation aside onto its own closer keeps its body intact; the aside is outside text ({§outside-text}). |
|
|
2375
2400
|
|
|
2376
2401
|
### §turn-source-resources Immutable turn-source resources
|
|
2377
2402
|
|
|
2378
2403
|
| Surface | Contract |
|
|
2379
2404
|
|---|---|
|
|
2380
2405
|
| Identity | `ops://<worker>/<loop>/<turn>`, `reasoning://<worker>/<loop>/<turn>`, and `note://<worker>/<loop>/<turn>/<item>` name a worker in the current workspace and its durable coordinates. A note's item is its dispatched NOTE ordinal. The `outside` source ({§outside-text}) has no address: no `outside://` scheme exists, and it is reached only through `outside/event`, FORK and the digest. The worker authority is required and case-sensitive; userinfo, ports, and queries are invalid. Source identity never depends on the reading worker. `log:///` remains local; READ, FIND and KILL reject log authorities, userinfo, ports and queries with 400, never substitute the caller's log. |
|
|
2381
|
-
| Source | `ops` is exact admitted `text/vnd.plurnk
|
|
2406
|
+
| Source | `ops` is exact admitted `text/vnd.plurnk`, and its turn's emission row carries the preview-bounded projection ({§emission-row}); `reasoning` is `text/plain` containing the selected original provider reasoning or a non-model producer's authored rationale. Producer identity comes from the owning turn; a harness rationale is not provider evidence. The turn decides existence and the source decides content: a turn that exists but has no source of that kind reads as the ordinary empty resource (204, empty body), never a fabricated one; a worker or turn that does not exist is 404. |
|
|
2382
2407
|
| Notes | Each dispatched NOTE stores its exact literal body as an immutable `text/plain` source and returns its worker-qualified address. There may be multiple notes in a turn, from reasoning, content, or another producer. A missing note is 404, not an empty invented note. Sharing its URI uses ordinary SEND; the receiver deliberately READs it. NOTE itself sends no ambient update. |
|
|
2383
2408
|
| Retention | One ops source, one reasoning source and one outside source per turn; one note source per NOTE ordinal. An optional inference-call link records provenance. Source removal follows deletion of its owning turn, never log curation. |
|
|
2384
2409
|
| Operations | Ordinary scoped READ, FIND, content search and COPY from any named worker's source within the workspace. FIND accepts authority and path patterns, retaining complete worker-qualified identities in results and folder selectors. READ returns data and never executes it. Sources are read-only for every actor and have no edit hashes. |
|
|
@@ -2386,11 +2411,11 @@ single line past the end, a reversed range, empty content, a command's log row
|
|
|
2386
2411
|
| FORK | Sources copy with the inherited turns at identical loop/turn/item coordinates under the fork's own authority. Bytes and embedded source references are preserved verbatim; an explicit reference still names its original worker. Branch receipt curation is independent; neither branch can rewrite source evidence. |
|
|
2387
2412
|
| Forensics | Digest assistant artifacts read source directly, independently of receipt presence or curation. Original provider responses retain all attempts and opaque fields separately. |
|
|
2388
2413
|
|
|
2389
|
-
§rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program. The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, the canonical model-facing `/attempt` leaf, and the exact latest rejected response. The packet does not duplicate that identity as `kind` metadata. It is born durably body-suppressed and projected visibly only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
2414
|
+
§rejected-emission-entry A rejected provider response is not `turnOps`: it never became an admitted turn program, and no emission row announces it ({§emission-row}). The one bounded invalid-emission recovery item under {§emission-admission} has `attrs.kind="emissionAttempt"`, `origin="model"`, the canonical model-facing `/attempt` leaf, and the exact latest rejected response. The packet does not duplicate that identity as `kind` metadata. It is born durably body-suppressed and projected visibly only in the informed recovery packet; every other rejected attempt remains forensic-only.
|
|
2390
2415
|
|
|
2391
|
-
- §log-coordinate-hierarchy **Log coordinates are a hierarchical prefix; the trailing slash is optional** — a coordinate is `loop/turn/sequence`, and a PARTIAL coordinate selects its descendants: `log:///1` = loop 1's rows, `log:///1/2` = turn 1/2's rows, `log:///1/2/3` = the one row. A full coordinate is always three parts, so a one- or two-part path is unambiguously a prefix — the trailing slash is an optional alias (`log:///1/2` ≡ `log:///1/2/`), uniform with ```` ```READ (worker:///docs/) ````. A complete `[start-end]` segment in any numeric coordinate slot selects that inclusive decimal interval; brackets elsewhere retain ordinary path-glob meaning. Every rendered row appends one canonical model-facing leaf: the native operation name or invoked executor name, `/attempt` for a rejected emission. An executor leaf is derived from the durable submitted statement (its `runtime`), never an internal dispatch type or the current tool registry. Digits and punctuation in executor names remain part of the leaf. The leaf names identity rather than adding a resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive leaf is authoritative and a disagreement resolves 404. READ anchors use the canonical suffixed identity even when addressed by shorthand. Typed entry materialization therefore resolves as `/READ` while retaining its durable `EDIT` event ({§exec-entry-sink}). `log:///1/2/*` still selects the turn's item rows, while `log:///**/READ`, `log:///**/python3`, and `log:///**/
|
|
2392
|
-
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — KILL takes a concrete coordinate or a path-glob, and a **trailing slash or a partial coordinate means "the contents"** ({§log-coordinate-hierarchy}), like a folder-scoped FIND: ```` ```KILL (log:///1/2) <1,-1> ```` suppresses turn 1/2's bodies. A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2393
|
-
- §log-curation-set-selection **Row selection and body scope are independent** — target/glob and an optional heading pattern (```` ```KILL (log:///**) [{"pattern": "~stale"}] ````, every dialect a FIND over rows accepts) compose by intersection into the affected row set. An optional `<L>` or `<SL,EL>` then intersects each selected canonical body; it never paginates or changes the selected set. Thus ```` ```KILL (log:///**/READ) <17,-1> ```` may change long READs and no-op on short ones while reporting every selected row in `matched`.
|
|
2416
|
+
- §log-coordinate-hierarchy **Log coordinates are a hierarchical prefix; the trailing slash is optional** — a coordinate is `loop/turn/sequence`, and a PARTIAL coordinate selects its descendants: `log:///1` = loop 1's rows, `log:///1/2` = turn 1/2's rows, `log:///1/2/3` = the one row. A full coordinate is always three parts, so a one- or two-part path is unambiguously a prefix — the trailing slash is an optional alias (`log:///1/2` ≡ `log:///1/2/`), uniform with ```` ```READ (worker:///docs/) ````. A complete `[start-end]` segment in any numeric coordinate slot selects that inclusive decimal interval; brackets elsewhere retain ordinary path-glob meaning. Every rendered row appends one canonical model-facing leaf: the native operation name or invoked executor name, `/attempt` for a rejected emission, `/emission` for an admitted one's announcement ({§emission-row}). An executor leaf is derived from the durable submitted statement (its `runtime`), never an internal dispatch type or the current tool registry. Digits and punctuation in executor names remain part of the leaf. The leaf names identity rather than adding a resource level. Exact consumers tolerate the unsuffixed three-part shorthand; when supplied, the case-insensitive leaf is authoritative and a disagreement resolves 404. READ anchors use the canonical suffixed identity even when addressed by shorthand. Typed entry materialization therefore resolves as `/READ` while retaining its durable `EDIT` event ({§exec-entry-sink}). `log:///1/2/*` still selects the turn's item rows, while `log:///**/READ`, `log:///**/python3`, `log:///**/attempt`, and `log:///**/emission` deliberately filter canonical leaves. Executor outputs instead use workspace-wide claims such as `sh:///ab3d5678#stdout` ({§execution-output-identity}); their source operation has log coordinates, but resource lifetime and identity are independent of that observation. Error pointers, Problem instances, source attribution, and search use this same identity; client stream coordinates retain the numeric triple. Within a turn, sequence is arrival order. Inbound SEND rows publish before the program runs ({§message-arrival}); a turn receiving messages holds the first at `log:///L/T/1/SEND`, followed by further arrivals oldest first, then its emission's announcement, then the model's operations ({§packet-current-turn} names `L/T`).
|
|
2417
|
+
- §log-curation-folder-idiom **Log curation speaks the folder idiom; a zero-match sweep is a no-op success** — KILL takes a concrete coordinate or a path-glob, and a **trailing slash or a partial coordinate means "the contents"** ({§log-coordinate-hierarchy}), like a folder-scoped FIND: ```` ```KILL (log:///1/2) <1,-1> ```` suppresses turn 1/2's bodies and retires its emission ({§emission-row}). A **well-formed selection that matches nothing is 204 with `matched: 0`**; a successful sweep's rx carries `matched: N`. Parameterless KILL instead requests completion ({§kill-conclusion}).
|
|
2418
|
+
- §log-curation-set-selection **Row selection and body scope are independent** — target/glob and an optional heading pattern (```` ```KILL (log:///**) [{"pattern": "~stale"}] ````, every dialect a FIND over rows accepts) compose by intersection into the affected row set. An optional `<L>` or `<SL,EL>` then intersects each selected canonical body; it never paginates or changes the selected set. An emission row is curated whole instead: the scope retires it when it covers every line and otherwise leaves it ({§emission-row}). Thus ```` ```KILL (log:///**/READ) <17,-1> ```` may change long READs and no-op on short ones while reporting every selected row in `matched`.
|
|
2394
2419
|
|
|
2395
2420
|
§log-kill-meta-operation **A log KILL changes working context, never the underlying resources or execution history.** Receipt visibility depends on the target and result, not the producer, attribution, or age of the turn:
|
|
2396
2421
|
|
|
@@ -2614,7 +2639,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2614
2639
|
- §find-line-anchors **A FIND regex anchors each line**, as READ, EDIT and KILL do ({§read-pattern}, {§edit-pattern}): `^` and `$` are a line's ends in every FIND content match — over entries, log rows, turn sources and a binary channel's bytes — so ```` ```FIND (django/urls/resolvers.py) /^from|^import/ ```` locates the same import lines a READ with that pattern shows, never a false 204.
|
|
2615
2640
|
- §find-candidate-containment **One candidate's crash is that candidate's problem** — arbitrary member content can crash a mimetype handler mid-match (an unbalanced template partial crashed Readability and killed a 1,916-file FIND as a blank 500, #449). `Matcher.matchCandidates` contains a per-candidate handler throw: the candidate drops out exactly like unsupported content, the cause goes to daemon stderr, and only a FIND whose every candidate crashed reports a 415 whose Problem names the first crashing member and handler. The operation's other candidates always answer.
|
|
2616
2641
|
|
|
2617
|
-
- §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 — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. 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.
|
|
2642
|
+
- §find-scope-prefix-filter Filters entries within scope. A **bare** path is the exact entry unless the file scheme resolves a directory under {§file-find-directory}; an explicit **shell glob**, classified once by {§path-glob}, expands to a scope. Path globs use segment semantics: `*` and `?` never cross `/`; `**` does — in every spelling: a `**` glued to a name (`**.go`, `src/**.ts`) is matched as `**/*.go` / `src/**/*.ts`, never demoted to a one-level `*` the way a native matcher reads it. 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.
|
|
2618
2643
|
|
|
2619
2644
|
Resource-authority globs select authorities independently of the path scope.
|
|
2620
2645
|
Matching resources retain their full addresses through pattern matching,
|
|
@@ -2631,7 +2656,7 @@ Log history preserved — `log_entries` stores path tuple as text, not FK to `en
|
|
|
2631
2656
|
- §find-fulltext-selection Every matcher operates only over the candidate set selected by `(target)`; indexed matchers do not bypass that selection. `~query` passes the native FTS5 expression to SQLite and ranks matching candidates by ascending BM25, with resource identity breaking ties. Native BM25 uses the shared index's term statistics; candidate visibility, owner, channel and target filters determine which resources can be returned. The ordinary FIND pager selects resources for broad targets or match locations for exact targets: markerless search uses {§markerless-first-page}, `<N>` selects position N and `<N,M>` selects an inclusive range. Fractions are invalid result coordinates, not similarity thresholds. Results expose addressable matched text regions; neither cosine scores nor percentage similarity is invented. Native query-syntax failures return 400 with SQLite's diagnostic; database and implementation failures propagate.
|
|
2632
2657
|
- §fts-word-phrase **A word with inner punctuation is the phrase of its tokens.** FTS5 barewords hold only letters, digits, `_` and non-ASCII, so before the query reaches SQLite each word outside a quoted string or `NEAR(…)` group that is not a bareword (with optional leading `^` and trailing `*`) is quoted as a phrase: `~inherited-members` searches `"inherited-members"` — the adjacent tokens `inherited members` — instead of failing as `no such column: members`, and `c++`, `x.y`, `a/b` likewise. FTS5's own syntax passes untouched: `AND`/`OR`/`NOT`/`NEAR`, `+`, quoted phrases, parentheses, and column filters (a word containing `:`, `{` or `}`, or opening with `-`). A column-filter failure keeps SQLite's diagnostic and its recovery says what the filter is and gives the bare-word and `NOT` forms: `` `members:` and `-members` are FTS5 column filters, and the index has one column; to search for a word write it bare, as `~members`, and to exclude one write `NOT` between terms, as `~a NOT members`. ``
|
|
2633
2658
|
- §find-scoped-isolation Workspace + scheme scoped — no cross-workspace/cross-scheme leakage.
|
|
2634
|
-
- §find-result-projection **The
|
|
2659
|
+
- §find-result-projection **The resolved target selection determines the result unit; result cardinality never changes it** ({§find-result-unit}). Returns `FindResult { status, content, mimetype, results, range, matchingPathCount, matchLocationCount, itemsWeightTotal, returnedItemsWeightTotal }`:
|
|
2635
2660
|
|
|
2636
2661
|
| Target | Matcher | `range.unit` | Result rows |
|
|
2637
2662
|
|---|---|---|---|
|
|
@@ -2717,10 +2742,9 @@ same durable liveness.
|
|
|
2717
2742
|
| Fresh operation/parser failure, without an authored WAIT | Continue before any automatic parking. |
|
|
2718
2743
|
| Neither an authored WAIT nor an eligible completion request ({§kill-conclusion}) | Continue, regardless of earlier replies or live work. |
|
|
2719
2744
|
| Live work and either WAIT or an eligible completion request | Park the same loop; message arrival, child or stream settlement, or stream cadence wakes it. No final-answer body is delivered while joining. |
|
|
2720
|
-
| WAIT without live work | Continue; never invent a future wake.
|
|
2721
|
-
| Unanswered messages | Continue. |
|
|
2745
|
+
| WAIT without live work | Continue; never invent a future wake. Its row says only `Nothing is in flight. Continuing.`, however often the loop yields this way. |
|
|
2722
2746
|
| Unobserved operation results, failures, child results or stream conclusions | Continue; the next packet presents them. |
|
|
2723
|
-
| Eligible completion request with no
|
|
2747
|
+
| Eligible completion request with no unpublished arrivals, live work or unobserved results | Conclude successfully, whether or not it delivers an answer. |
|
|
2724
2748
|
|
|
2725
2749
|
An empty emission is handled by {§empty-turn}. Ordinary strikes, cycles and
|
|
2726
2750
|
execution limits remain independent. NOTE and successful targeted KILL do not themselves
|
|
@@ -2780,21 +2804,22 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2780
2804
|
outstanding condition. Valid sibling operations always execute. Only an admitted
|
|
2781
2805
|
KILL delivers its literal body through {§send-response-receipt}; a deferred body
|
|
2782
2806
|
remains forensic evidence, never a stored draft to replay automatically. An empty
|
|
2783
|
-
KILL concludes
|
|
2784
|
-
|
|
2807
|
+
KILL concludes silently even when an observed message has no delivered answer. It neither
|
|
2808
|
+
invents delivery nor replaces an earlier answer; immutable input and reply history remain intact.
|
|
2809
|
+
SEND, NOTE and targeted KILL never request successful
|
|
2785
2810
|
completion. New arrivals still guard the terminal transition atomically
|
|
2786
|
-
({§completion-defers-to-messages}); an arrival concurrent with
|
|
2787
|
-
|
|
2811
|
+
({§completion-defers-to-messages}); an unobserved arrival concurrent with completion
|
|
2812
|
+
keeps the loop running. No implicit successful exit exists.
|
|
2788
2813
|
- §outside-text **Text outside the operations is the turn's `outside` source: stored, weighed, never a row.**
|
|
2789
2814
|
The spans {§response-text} supplies are stored verbatim as one immutable `outside` turn source
|
|
2790
2815
|
per admitted emission, in source order joined by a blank line ({§turn-source-resources}); no
|
|
2791
2816
|
operation is minted for them, a repetitive or length-cut response stays one source, and nothing
|
|
2792
2817
|
filters what is stored. The model hears only the weight: the next packet's Notices section
|
|
2793
2818
|
carries `outside_text: N tokens emitted outside OPs. Discarded.`, N by {§tokenomics-agnostic-ruler};
|
|
2794
|
-
the text itself never enters a packet, is never delivered and never concludes.
|
|
2819
|
+
the text itself never enters a packet, not even inside its turn's emission ({§emission-row}), is never delivered and never concludes.
|
|
2795
2820
|
{§empty-turn} still strikes a turn that holds only text, with unchanged reasoning recovery
|
|
2796
2821
|
({§reasoning-empty-turn-read}), reply accounting and completion rules. A log-entry heading in
|
|
2797
|
-
outside text still rejects the attempt ({§fabricated-log-entry}); an unfenced operation line is
|
|
2822
|
+
outside text still rejects the attempt unless it echoes an emission row ({§fabricated-log-entry}); an unfenced operation line is
|
|
2798
2823
|
not response text ({§unfenced-operation}) and so never reaches the source; `KnownToxins` guards
|
|
2799
2824
|
only the read-back ({§reasoning-empty-turn-read}). Clients receive the text once through
|
|
2800
2825
|
`outside/event` ({§notifications-outside-event}, {§agui-outside-text}); FORK snapshots the
|
|
@@ -2804,10 +2829,11 @@ accounting and model-visible failure evidence remain separately owned by
|
|
|
2804
2829
|
{§response-text} alone owns which bytes are operations, quotations or outside text.
|
|
2805
2830
|
- §loop-answer **A loop's address is what it said.** READ `ops://<worker>/<loop>` resolves to
|
|
2806
2831
|
the latest reply the loop gave to the message that started it: the body of a SEND
|
|
2807
|
-
or accepted final KILL that answered that message. A
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2832
|
+
or accepted final KILL that answered that message. A failed terminal takes precedence over
|
|
2833
|
+
an earlier reply and retains its exact Problem, including {§terminal-evidence}. A running loop
|
|
2834
|
+
without a reply is 425; a concluded loop without one returns its terminal outcome, including
|
|
2835
|
+
successful silence. Completion never fabricates an answer or a missing-resource failure.
|
|
2836
|
+
`ops://<worker>/<loop>/<turn>`
|
|
2811
2837
|
remains that turn's emission. A concluded child's `loop_termination` row to its parent
|
|
2812
2838
|
READs this same loop resource. Witness: `test/intg/loop-answer.test.ts`.
|
|
2813
2839
|
- §empty-turn **No authored response operation is a recoverable turn, never completion.**
|
|
@@ -3030,13 +3056,15 @@ the workspace snapshot. Installed siblings form the immutable base:
|
|
|
3030
3056
|
they are discovered and probed at startup, and availability is cached.
|
|
3031
3057
|
Workspace Functionality providers may atomically overlay additional names under
|
|
3032
3058
|
{§module-workspace-capabilities}; a name has one owner within a workspace, while
|
|
3033
|
-
independent workspaces may use the same name.
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
`
|
|
3059
|
+
independent workspaces may use the same name. The fence name selects exactly
|
|
3060
|
+
that registered executable tool. Unknown tags are refused 400 with the
|
|
3061
|
+
advertised catalogue and are never reinterpreted as shell command words.
|
|
3062
|
+
A runtime unavailable after an ordinary probe is 501 with the probe `detail`.
|
|
3063
|
+
Typed configuration failures preserve the declaration, with no executor instance
|
|
3064
|
+
or output scheme; invocation returns the exact 503 configuration Problem
|
|
3065
|
+
({§configuration-repair-path}). No executor, including `sh`, is required for
|
|
3066
|
+
daemon startup. Internal constructor defects remain failures, not unavailable
|
|
3067
|
+
configuration verdicts.
|
|
3040
3068
|
|
|
3041
3069
|
For a family runtime, `ExecutorRegistry.toolRegistry(tag, workspaceId)`
|
|
3042
3070
|
validates the one executor-owned snapshot used by packet presentation,
|
|
@@ -3213,8 +3241,8 @@ Each layer uses the same value and masking rules. A worker's list includes works
|
|
|
3213
3241
|
defaults by reference with `origin: "workspace"`; worker overrides and masks remain
|
|
3214
3242
|
worker-owned. Enabling an inherited entry clears this layer's mask, not a mask in a
|
|
3215
3243
|
lower layer; an explicit local value can override that lower layer. A mask follows
|
|
3216
|
-
the name even when its lower-layer origin changes. Removing an override
|
|
3217
|
-
|
|
3244
|
+
the name even when its lower-layer origin changes. Removing an override restores the
|
|
3245
|
+
lower entry and its enabledness ({§configuration-definition-resolution}). Forking copies only worker state, not the workspace defaults. Workspace edits
|
|
3218
3246
|
affect subsequent launches, not existing processes or other workspaces. A shared
|
|
3219
3247
|
capability never acquires an invoking worker's overrides or ownership.
|
|
3220
3248
|
|
|
@@ -3414,11 +3442,19 @@ was said ({§methods-loop-run-fold-consistency}). `LoopPolicies.compose` then ma
|
|
|
3414
3442
|
once. `PLURNK_SERVICE_ATTENDED` answers an unstated attendance, and the attendance picks which
|
|
3415
3443
|
knob answers an unstated disposition: `PLURNK_SERVICE_PROPOSALS` for an attended loop,
|
|
3416
3444
|
`PLURNK_SERVICE_UNATTENDED_PROPOSALS` for an unattended one, whose vocabulary has no `review`.
|
|
3417
|
-
Every panel state is therefore lawful
|
|
3445
|
+
Every valid panel state is therefore lawful; an invalid knob is diagnosed at startup
|
|
3446
|
+
and refuses a loop that needs it ({§configuration-repair-path}). No code,
|
|
3418
3447
|
schema or column holds a default ({§operator-config-only-home}): `loops.policy` and
|
|
3419
|
-
`loops.max_turns` carry none, so every insert states both
|
|
3420
|
-
|
|
3421
|
-
|
|
3448
|
+
`loops.max_turns` carry none, so every insert states both. Client-authored administrative
|
|
3449
|
+
loops use the same composition.
|
|
3450
|
+
|
|
3451
|
+
§runtime-bookkeeping-policy **Runtime bookkeeping has no reviewer and cannot acquire
|
|
3452
|
+
new authority.** Its administrative loops explicitly state
|
|
3453
|
+
`{ attended: false, proposals: "reject" }`; this is a runtime invariant, not an
|
|
3454
|
+
interactive default. Generated reference publication and audit narration therefore
|
|
3455
|
+
do not depend on client policy configuration. Runtime-authored proposals do not
|
|
3456
|
+
use effect-policy auto-admission; bookkeeping proposals settle as failures through
|
|
3457
|
+
the ordinary proposal lifecycle, never wait for a client or auto-accept.
|
|
3422
3458
|
|
|
3423
3459
|
§loop-policy-effective-read `loops.policy` persists one complete immutable
|
|
3424
3460
|
`LoopPolicy`; every runtime policy read validates that snapshot before use.
|
|
@@ -3521,7 +3557,7 @@ No generator. SQLite-optimal: STRICT (3.37+), `INTEGER PRIMARY KEY` aliasing, ex
|
|
|
3521
3557
|
| §db-maintenance-optimize Statistics at shutdown | The daemon's last database step before the caller closes SQLite is `PRAGMA optimize` on the writer (`maintenance_optimize`), so `sqlite_stat1` reflects tables the connection planned against, bounded by SQLite's own analysis limit; a failure there is a reported shutdown error, never silent. Retention runs before it under the operator's policy ({§retention-policy}) and ends with a WAL truncation ({§db-space-reclamation}); no periodic `ANALYZE` runs. |
|
|
3522
3558
|
| §db-space-reclamation The daemon keeps its own file healthy | `PLURNK_SERVICE_AUTO_VACUUM` (`incremental` or `none`) names the mode the daemon keeps its file in. At start, before any drain, a database in another mode is converted (set the mode, one `VACUUM`, which rewrites the file and needs free disk about its size) and the journal says so with page counts before and after. Under `incremental`, every retention pass ends by stepping `PRAGMA incremental_vacuum` to completion once free pages reach `PLURNK_SERVICE_RECLAIM_MIN_FREE_BYTES` (0 = every pass), and reports `reclaimedPages`; below the floor, free pages stay for SQLite to reuse. Under `none` the file never shrinks and freed pages are reused. No operator step is involved beyond the knobs. The WAL stays bounded by SQLite's automatic checkpoint (#764). |
|
|
3523
3559
|
| §content-store Every body is stored once | `contents` holds each settled body once, addressed by its SHA-256, however many channels, workspaces, forks or derivations carry it; rows are immutable. `entry_channel_rows` points a settled channel at its body and keeps an active stream's body as a private buffer until it settles, when it is interned. Every reader and writer uses the `entry_channels` view, whose `INSTEAD OF` triggers intern bodies, refuse a bound `content_hash` that is not the content's, and write each column group only when it changed, so a search attachment is never a representation write. SQLite counts no changes for a view, so a write that must know whether its channel exists returns the channel's name; an outer join cannot flatten the view, so the two statements that need one read `entry_channel_rows` and `contents` directly. `derivation_fts` is an external-content index over `derivation_texts` (a derivation joined to its body); `derivations.content_id` names the indexed text, and the triggers in `_entry-fts.sql` move the index with it and forget it on delete. A body no channel holds and no derivation indexes is collected by retention under `PLURNK_SERVICE_COLLECT_CONTENTS`. Witnesses: `test/intg/retention.test.ts`, `test/intg/entries.test.ts`, `test/intg/fulltext-index.test.ts`. |
|
|
3524
|
-
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon
|
|
3560
|
+
| §retention-policy Retention is the operator's policy; information is kept by default | `Retention` (`src/server/Retention.ts`, statements in `Retention.sql`) reads ten knobs from `.env.defaults` once at daemon start (the two storage knobs are {§db-space-reclamation}) and runs four set statements in dependency order — on `PLURNK_SERVICE_RETENTION_INTERVAL_MS` cadence while the daemon runs (0 = shutdown only) and once more at shutdown before `PRAGMA optimize`. `PLURNK_SERVICE_RETAIN_PACKET_TURNS` (-1 = every packet) and `PLURNK_SERVICE_RETAIN_PACKET_MS` (-1 = no age limit) retire a completed turn's packet composition (`turn_sections`, {§packet-items}) once it is beyond the newest N packet-bearing turns of its loop or older than the age; the turn, its bag, its log rows and its accounting stay, and an open turn is never retired. `PLURNK_SERVICE_COLLECT_PACKET_ITEMS` collects items no composition references. `PLURNK_SERVICE_COLLECT_CONTENTS` collects stored bodies nothing holds ({§content-store}), after the collectors that release them. `PLURNK_SERVICE_COLLECT_DERIVATIONS` collects derivations no channel, turn source, or log row cites — superseded editions — with their symbols (cascade) and their full-text shadow (`derivations_delete_fts`, a process trigger beside the FTS statements, on every delete path). `PLURNK_SERVICE_RETAIN_RESPONSE_TURNS` and `PLURNK_SERVICE_RETAIN_RESPONSE_MS` retire a settled call's response body (`model_call_responses`) once it is beyond the newest N body-bearing calls of its loop or its turn is older than the age; the call's identity, failure, capacity, admission and accounting stay, and the digest renders such a call request-only. The durable record is kept; packets and response bodies — transient evidence — age out, so a daemon left running for months stops growing (#788). An invalid policy withholds retention, including storage conversion and shutdown collection, and reports its configuration diagnostic without blocking the client ({§configuration-repair-path}). Storage I/O failures remain ordinary startup or shutdown failures. Witness: `test/intg/retention.test.ts`. |
|
|
3525
3561
|
|
|
3526
3562
|
- DDL = storage truth; JSON Schemas = wire truth. They are allowed to differ where ergonomics demand.
|
|
3527
3563
|
- §entry-identity-no-null **Identity components are never NULL.** `(workspace_id, scheme, authority, pathname)` is a unique key. `workspace_id` references the workspace directly with cascading deletion. Namespace schemes use empty authority; resource schemes retain their canonical authority. File members use nonempty `scheme="file"` and render as bare paths. Registration refuses `storedScheme: null`.
|
|
@@ -3667,11 +3703,14 @@ and is ignored rather than resolved against the working directory.
|
|
|
3667
3703
|
|
|
3668
3704
|
| Class | Base | Plurnk member |
|
|
3669
3705
|
|---|---|---|
|
|
3670
|
-
| Configuration | `$XDG_CONFIG_HOME` (default `~/.config`) | `plurnk/.env`, `plurnk/AGENTS.md` |
|
|
3706
|
+
| Configuration | `$XDG_CONFIG_HOME` (default `~/.config`) | `plurnk/.env`, `plurnk/AGENTS.md`, plurnk-only MCP definitions `plurnk/mcp.json`, Agent Skills `plurnk/skills/<name>/SKILL.md`, and Agent Plugins `plurnk/plugins/<plugin>/` |
|
|
3671
3707
|
| Durable user data | `$XDG_DATA_HOME` (default `~/.local/share`) | `plurnk/plurnk.db` and SQLite sidecars |
|
|
3672
3708
|
| Persistent operational state | `$XDG_STATE_HOME` (default `~/.local/state`) | On-demand workspace/module directories ({§module-workspace-directory}). |
|
|
3673
3709
|
| Reproducible cache | `$XDG_CACHE_HOME` (default `~/.cache`) | Reserved; no directory is created without an owned artifact. |
|
|
3674
3710
|
| Shared global Agent Skills | User home | `.agents/skills/<name>/SKILL.md` |
|
|
3711
|
+
| Shared global MCP definitions | User home | `.agents/mcp.json` |
|
|
3712
|
+
| Shared global Agent Plugins | User home | `.agents/plugins/<plugin>/` ({§agent-plugins-hosting}) |
|
|
3713
|
+
| A plugin's `PLUGIN_DATA` | `$XDG_DATA_HOME` | `plurnk/plugins/<plugin>/` |
|
|
3675
3714
|
|
|
3676
3715
|
§state-root **A private daemon has one root.** `PLURNK_SERVICE_STATE_ROOT` (absolute; a leading
|
|
3677
3716
|
`~/` expands; a relative value fails hard by name) replaces the data, state, cache and runtime homes
|
|
@@ -3693,15 +3732,32 @@ ordinary precedence; XDG variables themselves require absolute paths.
|
|
|
3693
3732
|
|---------:|------------------------------------|-----------------------------------------------------------|
|
|
3694
3733
|
| 1 | Assembled package `.env.defaults` | Set-if-unset floor; one owner per key. |
|
|
3695
3734
|
| 2 | `$XDG_CONFIG_HOME/plurnk/.env` | User-level ambient configuration. |
|
|
3696
|
-
| 3 |
|
|
3697
|
-
| 4 | `--
|
|
3698
|
-
| 5 |
|
|
3699
|
-
| 6 |
|
|
3700
|
-
|
|
3735
|
+
| 3 | `--config=<path>` | Singular service-owned explicit file. |
|
|
3736
|
+
| 4 | `--env-file*` | Repeatable explicit files; later selected files win. |
|
|
3737
|
+
| 5 | Initial shell environment | Preserved over every file layer. |
|
|
3738
|
+
| 6 | Derived service CLI flags | Assigned last. |
|
|
3739
|
+
|
|
3740
|
+
A working directory's `.env` configures that directory's application, never plurnk (#926); a
|
|
3741
|
+
project's variables reach its commands through the workspace environment ({§workspace-env}).
|
|
3701
3742
|
|
|
3702
3743
|
Node's pre-script env-file form and the executable's post-script form share the same later-file-wins ordering. `--env-file-if-exists` skips an absent file without changing the order of selected files.
|
|
3703
3744
|
|
|
3704
|
-
§operator-config-env-defaults **Every package owns its knobs —
|
|
3745
|
+
§operator-config-env-defaults **Every package owns its knobs — one assembled floor.**
|
|
3746
|
+
|
|
3747
|
+
| Source | Panel | Admission |
|
|
3748
|
+
|---|---|---|
|
|
3749
|
+
| Platform capability package | `.env.defaults` at the package root | `@plurnk/*` or a `plurnk` package field; {§plugin-trust-boundary} |
|
|
3750
|
+
| Agent Plugin native extension | `ai.plurnk/.env.defaults` | The winning daemon-wide plugin in {§agent-plugins-hosting}, a valid native declaration, and the same trust gate |
|
|
3751
|
+
|
|
3752
|
+
Root and trust flags apply before collection. A project plugin contributes no native panel.
|
|
3753
|
+
Non-module native panels follow their npm-only family discovery ({§plugin-manifest-read});
|
|
3754
|
+
a plain-folder declaration does not suppress an installed capability's panel.
|
|
3755
|
+
The file travels with its code and is its configuration reference. All admitted files compose
|
|
3756
|
+
one floor, applied set-if-unset beneath operator sources. `plurnk-service config defaults`
|
|
3757
|
+
renders those same owner-labelled files, preserving comments and optional declarations without
|
|
3758
|
+
persisting another copy or exposing effective values. Duplicate key ownership fails naming both
|
|
3759
|
+
owners. Invalid optional native panels are diagnosed and prevent that extension from loading;
|
|
3760
|
+
they do not block the remaining floor or the repair path ({§configuration-repair-path}).
|
|
3705
3761
|
|
|
3706
3762
|
§operator-config-only-home **The cascading environment is the only home for a choice.** The principle and its reasons are ARCHITECTURE.md's (*Configuration authority*); this is what `scripts/env-surface-policy.mjs` enforces in `root:lint`, over the source Git tracks:
|
|
3707
3763
|
|
|
@@ -3733,6 +3789,34 @@ or provider request. The seeded `.env`, first-run diagnostic, service help, and
|
|
|
3733
3789
|
missing-model recovery all signpost `plurnk-service config defaults` as the
|
|
3734
3790
|
complete installed option catalog.
|
|
3735
3791
|
|
|
3792
|
+
§operator-config-offline-validation **`config check` and runtime use the same
|
|
3793
|
+
owning configuration readers, with different failure boundaries.** An offline
|
|
3794
|
+
check rejects invalid configuration with a nonzero exit. Runtime contains
|
|
3795
|
+
optional-family errors according to {§configuration-repair-path}. Failure
|
|
3796
|
+
names the offending variable or file/entry and retains its cause.
|
|
3797
|
+
|
|
3798
|
+
| Owner | Offline validation |
|
|
3799
|
+
|---|---|
|
|
3800
|
+
| Core | Model selection, file-creation/effect/loop policy, members definitions and controls, skill-fetch settings and root selection |
|
|
3801
|
+
| MCP | Whole definitions from the environment and selected files (cwd is the project for this check), future-alias controls, catalog settings, timeouts, retry pacing and registry URL |
|
|
3802
|
+
| A2A | Whole outbound definitions and controls, timeout/diagnostic bounds, configured inbound exposure |
|
|
3803
|
+
| Schedule | Whole definitions and controls, recurrence syntax, time zone and preview count |
|
|
3804
|
+
| Hooks | Command/argument/event configuration and delivery bounds |
|
|
3805
|
+
|
|
3806
|
+
Disabled definitions and controls without a resource are validated, not skipped.
|
|
3807
|
+
Checking creates no database, starts no process or listener, arms no schedule,
|
|
3808
|
+
and contacts no provider or endpoint. Symbolic credential references remain
|
|
3809
|
+
symbolic: availability belongs to workspace preparation, not offline validation.
|
|
3810
|
+
|
|
3811
|
+
First-run seeding publishes the complete private configuration directory atomically.
|
|
3812
|
+
Concurrent initializers adopt the winning seed; a failed initializer removes only
|
|
3813
|
+
its own staging directory. An existing operator directory is never reseeded.
|
|
3814
|
+
|
|
3815
|
+
§systemd-user-unit The service package ships `plurnk.service` as an example
|
|
3816
|
+
systemd user unit. Installation and enablement are explicit operator actions;
|
|
3817
|
+
package installation performs neither. The template documents executable-path
|
|
3818
|
+
and environment adjustments instead of introducing a service-management command.
|
|
3819
|
+
|
|
3736
3820
|
Model selection uses one selector vocabulary in `ProviderRegistry` ({§provider-instantiation}). `PLURNK_MODEL_<alias>=<provider>/<model-id>` optionally declares a friendly route and tuning scope; `PLURNK_MODEL=<selector>` selects either that alias or an exact provider/model route. `PLURNK_MODEL_CHILD=<selector>` uses the same vocabulary for the default child provider; unset means inherit the spawning loop's provider. Operator selections and alias declarations live in `.env`, not `.env.defaults`.
|
|
3737
3821
|
|
|
3738
3822
|
Each knob's value lives on its panel and nowhere else (`plurnk-service config defaults` prints them all); this table says what the service's knobs mean.
|
|
@@ -3853,7 +3937,7 @@ the policy renders in exactly one packet section. Every other tier runs the
|
|
|
3853
3937
|
test cascade, so shipped-default regressions are otherwise invisible by
|
|
3854
3938
|
construction.
|
|
3855
3939
|
|
|
3856
|
-
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, ambient MCP
|
|
3940
|
+
§operator-config-real-model-profile **Real-model gate profile.** `plurnk-core/.env.test` is committed source and is the single shared profile for live, demo, and the candidate daemon used by benchlets. Live/demo load it after operator files; the candidate daemon loads it below its inherited environment. Direct shell/benchmark overrides win in both paths. Its exact allowlist is limited to gate-wide service posture that is identical on every machine: complete catalog orientation, automatic Git membership when the operator ceiling permits Git, ambient operator-file docs/packet notes cleared, the operator's installed skills and plugins left unread ({§agent-roots}), ambient MCP/A2A and schedules default disabled, and `PLURNK_EXECS_QUESTION=0` for unattended runs. The ordinary executor switch removes the question tool and its teaching; an explicit override can opt into an attended drill. The drivers also project per-alias `ENABLED=0` overrides for named MCP, A2A, schedule, membership, and skill resources in the operator's config file. Definitions remain inspectable; explicit shell/benchmark controls win. Mock-tier bootstrap clears these ambient families before loading its fixture floor. No operator file is rewritten. Configuration with a narrower or variable owner stays outside it:
|
|
3857
3941
|
|
|
3858
3942
|
| Owner | Configuration |
|
|
3859
3943
|
|---|---|
|
|
@@ -3936,36 +4020,55 @@ proceeds. `setup` is the readiness boundary for every capability registered
|
|
|
3936
4020
|
with Core: recovery may demand a workspace provider before `start`. For a
|
|
3937
4021
|
pre-bound client interface, requests remain unavailable until `start`; every
|
|
3938
4022
|
other module opens its module-owned exterior ingress only after recovery. No
|
|
3939
|
-
registered capability may depend on exterior ingress.
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
discovery
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
4023
|
+
registered capability may depend on exterior ingress. A module, and any distinct
|
|
4024
|
+
lifetime object returned by `start`, may implement the following phases:
|
|
4025
|
+
|
|
4026
|
+
| Phase | Obligation |
|
|
4027
|
+
|---|---|
|
|
4028
|
+
| `stop()` | Reject new ingress, stop timers and other producers, and settle owned work that can emit core events. Keep event subscriptions and resources needed for settlement alive. |
|
|
4029
|
+
| `close()` | Unsubscribe observers and release remaining resources after producer settlement; await admitted notification deliveries. Do not start new core work. |
|
|
4030
|
+
|
|
4031
|
+
Both phases are optional and idempotent; repeated calls join the same work.
|
|
4032
|
+
Core tracks a module before `setup` so partially acquired resources are released
|
|
4033
|
+
even if setup fails. A returned object identical to its module is tracked once.
|
|
4034
|
+
|
|
4035
|
+
§module-discovery **Daemon modules compose through their shared lifecycle.**
|
|
4036
|
+
|
|
4037
|
+
| Source | Declaration | Lifetime |
|
|
4038
|
+
|---|---|---|
|
|
4039
|
+
| Platform capability package | `package.json#plurnk` with `kind: "module"` and `module` | Daemon-wide |
|
|
4040
|
+
| Agent Plugin | `plugin.json#extensions.ai.plurnk` with `kind: "module"` and a `module` path under `ai.plurnk/` | Daemon-wide; npm and selected user roots only |
|
|
4041
|
+
| Project Agent Plugin | Portable components only | Workspace-scoped; native code is not imported |
|
|
4042
|
+
|
|
4043
|
+
The export is one DaemonModule object or no-argument factory. Standard bundles follow
|
|
4044
|
+
{§agent-plugins-hosting} source order, then other installed module packages load in package-name
|
|
4045
|
+
order. All trusted modules register before setup. The service's explicit AG-UI, hooks and MCP
|
|
4046
|
+
composition is never duplicated. Untrusted modules are reported and not imported. Invalid
|
|
4047
|
+
declarations, unavailable module files and configuration errors during construction are diagnosed
|
|
4048
|
+
at the affected native extension; healthy siblings remain available. A factory validates startup
|
|
4049
|
+
configuration before `setup` acquires resources. Failures after registration begins follow
|
|
4050
|
+
{§module-lifecycle} cleanup, not a partial-registration fallback.
|
|
4051
|
+
An invalid module object, factory result or lifecycle member is an implementation contract failure,
|
|
4052
|
+
not configuration, and fails loudly. Native capabilities register through their owning public
|
|
4053
|
+
interfaces and release registrations during {§module-lifecycle} resource teardown.
|
|
3955
4054
|
|
|
3956
4055
|
§module-shutdown-order `Daemon.stop()` first rejects new capability demand and
|
|
3957
|
-
aborts proposals, branches, derivations, and worker scopes. It
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
4056
|
+
aborts proposals, branches, derivations, and worker scopes. It begins module
|
|
4057
|
+
`stop()` calls in reverse registration order without serially awaiting them,
|
|
4058
|
+
so every producer is asked to stop even if another stalls. Core settles drains,
|
|
4059
|
+
capability publications, module producers, streaming producers, derivations,
|
|
4060
|
+
mimetypes, schemes, and the final worker-settlement barrier while observers
|
|
4061
|
+
remain subscribed. Only then does it begin and join module `close()` calls in
|
|
4062
|
+
reverse order. Failures do not skip later phases and join one shutdown aggregate.
|
|
4063
|
+
The supervisor owns each asynchronous cancellation and wake task from
|
|
3963
4064
|
acceptance through settlement, including immediately acknowledged and explicitly
|
|
3964
4065
|
awaited cancellation; a task failure participates in the shutdown aggregate.
|
|
3965
4066
|
After asynchronous selection, the supervisor rechecks shutdown before creating
|
|
3966
4067
|
a drain or installing a timer; parked-loop wake mutations also recheck worker
|
|
3967
4068
|
cancellation under {§worker-lifecycle-durable-disposition}.
|
|
3968
|
-
The database
|
|
4069
|
+
The database remains available through observer closure and final maintenance.
|
|
4070
|
+
The shared deadline bounds every phase, including observer delivery; forced
|
|
4071
|
+
shutdown may therefore lose notifications and reports the unfinished phase.
|
|
3969
4072
|
|
|
3970
4073
|
§crash-only-stop The settle sequence is deadline-bounded
|
|
3971
4074
|
(`PLURNK_SERVICE_STOP_TIMEOUT_MS`, default 30000): past the deadline each wait
|
|
@@ -3979,21 +4082,22 @@ backstop, not the exit.
|
|
|
3979
4082
|
```mermaid
|
|
3980
4083
|
flowchart LR
|
|
3981
4084
|
stop[Begin stop] --> abort[Abort core producers]
|
|
3982
|
-
stop -->
|
|
4085
|
+
stop --> moduleStop[Begin reverse module stop]
|
|
3983
4086
|
abort --> drains[Settle worker drains]
|
|
3984
|
-
drains --> joined[Settle module
|
|
3985
|
-
|
|
4087
|
+
drains --> joined[Settle module producers]
|
|
4088
|
+
moduleStop --> joined
|
|
3986
4089
|
joined --> producers[Settle streaming producers]
|
|
3987
4090
|
producers --> resources[Dispose derivations,<br/>mimetypes, and schemes]
|
|
3988
4091
|
resources --> settlement[Settle cancellations and wakes]
|
|
3989
|
-
settlement -->
|
|
4092
|
+
settlement --> observers[Close observers and resources]
|
|
4093
|
+
observers --> database[Maintain and release database]
|
|
3990
4094
|
```
|
|
3991
4095
|
|
|
3992
4096
|
| Setup function | Contract |
|
|
3993
4097
|
|---|---|
|
|
3994
4098
|
| `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. |
|
|
3995
4099
|
| `registerScheme(name, handler)` | Adds one process-wide addressable scheme handler; scheme readiness and model-facing capability publication remain core-owned. |
|
|
3996
|
-
| §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. |
|
|
4100
|
+
| §module-action-registration `registerModuleAction({ name, scope, residency, 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. `residency` is explicitly `required` or `none`: only the former acquires workspace capabilities and reconciles worker documents. Worldless actions require `none`. 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. |
|
|
3997
4101
|
| §module-workspace-provider `registerWorkspaceCapabilityProvider(namespaceOwner, provider)` | Registers one extension-unique Functionality provider. `activate({ workspaceId, retain })` reconstructs the workspace snapshot; idempotent `deactivate({ workspaceId })` releases process resources. Core coalesces demand and supplies residency leases for work that outlives its caller. |
|
|
3998
4102
|
| §module-workspace-state `readWorkspaceModuleState(workspaceId, namespaceOwner)` | Reads one nullable JSON state value per workspace and provider. Core owns storage and lifecycle; the provider owns its schema. Store symbolic credential references, not copied secrets. A worker-scoped family's coordinator reads and replaces the same shape per worker in `worker_module_state` ({§functionality-scope}). |
|
|
3999
4103
|
| `readWorkspaceEnvironment(workspaceId)` | Captures the workspace env layer ({§workspace-env}) and returns its composer. No argument uses admitted host values; a supplied environment supplies a module's reference-resolution context. Both apply the same captured values and masks, without worker overrides. |
|
|
@@ -4043,7 +4147,7 @@ A conflicting alias is rejected explicitly; it never produces a hidden second
|
|
|
4043
4147
|
definition for the submitting client or worker.
|
|
4044
4148
|
|
|
4045
4149
|
§module-workspace-residency **Persistence is not residency.** Model execution,
|
|
4046
|
-
capability-aware operations,
|
|
4150
|
+
capability-aware operations, module actions declaring required residency, and retained provider work
|
|
4047
4151
|
lease the workspace's Functionality. Boot, workspace or worker creation,
|
|
4048
4152
|
attachment, listing, naming, idle clients, and parked state alone do not.
|
|
4049
4153
|
After the last lease releases, `PLURNK_SERVICE_WORKSPACE_WARM_MS` and
|
|
@@ -4073,10 +4177,10 @@ registry. Deleting the workspace cascades its state; worker lifecycle does not.
|
|
|
4073
4177
|
## Workspace Functionality
|
|
4074
4178
|
|
|
4075
4179
|
§functionality-coordinator **One coordinator owns the common lifecycle.**
|
|
4076
|
-
|
|
4180
|
+
Functionality families are adapters beneath
|
|
4077
4181
|
`list | discover | add | enable | disable | remove`. State and mutations
|
|
4078
|
-
serialize per workspace and family.
|
|
4079
|
-
`workspace.<family>.<verb>` and model manager executors invoke the same
|
|
4182
|
+
serialize per workspace and family. Scope-bound client actions
|
|
4183
|
+
`workspace.<family>.<verb>` / `worker.<family>.<verb>` and model manager executors invoke the same
|
|
4080
4184
|
coordinator. Families do not invent another management grammar, proposal
|
|
4081
4185
|
policy, or hotload path.
|
|
4082
4186
|
|
|
@@ -4084,12 +4188,98 @@ Retryability describes the actual failed condition, not its numeric status.
|
|
|
4084
4188
|
|
|
4085
4189
|
| Verb | Common contract |
|
|
4086
4190
|
|---|---|
|
|
4087
|
-
| `list` | Project definitions, origin, enabledness, and preparation outcome: disabled, active, unavailable with its Problem, or authorization-required. No credential values. |
|
|
4191
|
+
| `list` | Project definitions, ownership (`origin`), winning configuration source (`provenance`), enabledness, and published preparation outcome: disabled, dormant, active, unavailable with its Problem, or authorization-required. No credential values. |
|
|
4088
4192
|
| `discover` | Return inert candidates. Never install, persist, enable, or execute them. |
|
|
4089
|
-
| `add` | Admit and persist a
|
|
4193
|
+
| `add` | Admit and persist a local definition, prepare it, and enable it atomically. It may override an inherited definition. Reapplying the same local definition enables it idempotently (200); a different local definition for that alias fails 409 without replacing it. |
|
|
4090
4194
|
| `enable` | Publish an available definition; retry preparation if unavailable. |
|
|
4091
4195
|
| `disable` | Withdraw live capability; retain its definition and saved results. |
|
|
4092
|
-
| `remove` |
|
|
4196
|
+
| `remove` | Forget the locally owned definition and its enabledness override. Restore any inherited definition with its inherited enabledness. Inherited definitions cannot be removed at this scope. Saved results remain. |
|
|
4197
|
+
|
|
4198
|
+
§configuration-definition-resolution **Named resource definitions replace whole;
|
|
4199
|
+
independent behavior controls remain independent.** Source readers and scope
|
|
4200
|
+
overlays apply the same boundary:
|
|
4201
|
+
|
|
4202
|
+
| Value | Resolution |
|
|
4203
|
+
|---|---|
|
|
4204
|
+
| Named definition | Select the complete definition from the highest-precedence source or scope declaring that alias. Omitted fields, arrays and nested objects never inherit from a lower definition. |
|
|
4205
|
+
| Environment resource declaration | Use {§resource-environment}: absence inherits, an empty definition is invalid, and an explicit enabledness switch disables without erasing the definition. |
|
|
4206
|
+
| Definition validity | Validate the selected definition against its family's schema. Missing required fields are errors, not requests to fill from a lower definition; rejected live changes preserve the previous publication. |
|
|
4207
|
+
| Independently declared behavior control | Resolve its own value through its cascade. An enabledness override does not copy or patch the definition it controls. |
|
|
4208
|
+
| Local definition removal | Remove this scope's definition and enabledness override. Restore the current inherited definition and enabledness, or leave no entry if none exists. Do not persist a replacement or disabling mask; subsequent inherited changes remain effective. Restoration follows the same preparation and publication failure policy as other mutations ({§functionality-publication}). |
|
|
4209
|
+
|
|
4210
|
+
§configuration-provenance **Inspection names the winning definition's input, not
|
|
4211
|
+
its owner or runtime.** `provenance` uses the same `{kind, source, reference?}`
|
|
4212
|
+
shape as discovery candidates. Source readers contribute it; the coordinator
|
|
4213
|
+
preserves it through inheritance, enabledness changes, and every readiness state.
|
|
4214
|
+
|
|
4215
|
+
| Definition source | Inspection |
|
|
4216
|
+
|---|---|
|
|
4217
|
+
| Assembled environment | `kind: environment`, `source`: exact definition key; never its value or an inferred dotenv filename. |
|
|
4218
|
+
| Discovered skill root | `kind: file`, `source`: the winning `SKILL.md` path. |
|
|
4219
|
+
| Standalone MCP file | `kind: file`, `source`: the winning `mcp.json` path; `reference`: its entry's JSON Pointer. |
|
|
4220
|
+
| Local workspace/worker definition or host-provided tree with no configuration input | No fabricated provenance; `origin` identifies ownership and the family definition describes the resource. |
|
|
4221
|
+
| Local override | Replaces inherited provenance with the local definition; removal restores the current inherited provenance. |
|
|
4222
|
+
|
|
4223
|
+
Only the winning definition's source is reported. Shadowed definitions, secrets,
|
|
4224
|
+
and environment-file loading history are not tracked. Source metadata is derived
|
|
4225
|
+
on inspection, not persisted in the local overlay or used as runtime identity.
|
|
4226
|
+
|
|
4227
|
+
§configuration-repair-path **Invalid optional configuration cannot remove the
|
|
4228
|
+
agent's repair environment.** Capability owners reject typed operator input
|
|
4229
|
+
errors. The launcher and shared coordinator contain them at their respective
|
|
4230
|
+
composition boundaries, not arbitrary exceptions:
|
|
4231
|
+
|
|
4232
|
+
| Boundary | Outcome |
|
|
4233
|
+
|---|---|
|
|
4234
|
+
| Optional startup integration (hooks, hosted A2A, observability) cannot be configured | Withhold that integration and retain its exact configuration diagnostic. Activate the client interface and unrelated capabilities; never invent a replacement setting. |
|
|
4235
|
+
| Invalid default model or child selector | Retain its diagnostic at startup; keep client inspection and explicit selection available. A request relying on the invalid selector fails with `daemon:configuration/configuration-invalid` (503), naming its key. Never substitute another model or silently inherit a child model. |
|
|
4236
|
+
| Model construction or endpoint verification fails | Reject selection/use before inference or committing the selection. Keep the client available; a later selection can retry or choose another route. |
|
|
4237
|
+
| Invalid effect, file-creation, or loop-default policy | Retain the startup diagnostic. Reject the affected operation or unresolved loop policy as `daemon:configuration/configuration-invalid` (503); no guessed admission policy, external effect, or orphan approval wait. Independent operations and explicitly supplied valid loop policies remain usable. |
|
|
4238
|
+
| Invalid retention configuration | Withhold collection and automatic storage conversion, including shutdown collection. Preserve stored evidence and expose the diagnostic; do not substitute a deletion policy. |
|
|
4239
|
+
| Invalid packet configuration or retired capacity knobs | Retain the startup diagnostic and keep client inspection available. Reject affected packet construction before inference with `daemon:configuration/configuration-invalid` (503). Never guess a capacity or projection setting. |
|
|
4240
|
+
| Invalid executor construction/probe configuration | Keep the installed declaration and its diagnostic, but no executable instance or output scheme. Other executors and ordinary READ/EDIT remain usable. Invoking the unavailable tag returns its exact 503 configuration Problem. |
|
|
4241
|
+
| Invalid execution scheduling, input, or scratch configuration | Diagnose at startup and validate on the affected execution path before admission, stream creation, or external effects. Scratch configuration applies only to resource-backed execution; inline programs remain independent. Never substitute concurrency, timeout, or directory settings. |
|
|
4242
|
+
| Invalid model-alias catalog | Fail catalog inspection explicitly; omit its unavailable snapshot rather than report an empty catalog. Exact provider/model selection does not depend on aliases. |
|
|
4243
|
+
| Offline `config check` | Validate the same inputs without activating integrations; an invalid setting remains a nonzero failure. |
|
|
4244
|
+
| Family configuration cannot be resolved | Keep its manager available, identify the configuration failure in its generated documentation, and preserve durable definitions. Do not publish the family's operational capabilities or pretend its catalog is empty. Other families and ordinary model work remain usable. |
|
|
4245
|
+
| Inspection or mutation of an unresolved family | Return the exact configuration Problem, naming the key and required correction, through both client and model paths. No silent source fallback or change to stored settings. |
|
|
4246
|
+
| Invalid live mutation | Reject atomically and preserve the preceding publication. |
|
|
4247
|
+
| Previously valid family becomes invalid | Withdraw its operational capabilities at normal publication, then release the old snapshot. Keep the manager and diagnostic. |
|
|
4248
|
+
| Configuration source is corrected or removed | Inspection resolves the current source immediately, even in the repairing turn; a previous source-resolution error cannot override a successful read. Not-yet-published definitions remain dormant. Normal publication restores capabilities; inspection does not activate them. |
|
|
4249
|
+
| Preparation configuration is corrected | The published preparation failure remains until ordinary preparation succeeds. Successful source inspection alone does not establish runtime readiness. Environment-file edits follow their ordinary process lifetime, not an implicit reload. |
|
|
4250
|
+
| Internal invariant, state, or implementation failure | Preserve the exception; never reclassify it as an operator configuration error. |
|
|
4251
|
+
|
|
4252
|
+
Client discovery and passive synchronization report startup diagnostics through the
|
|
4253
|
+
existing Notice channel, even without a usable model. The first turn of a drain, and a changed diagnostic
|
|
4254
|
+
thereafter, reports unresolved configuration to both client and model. An
|
|
4255
|
+
unchanged diagnostic is not repeated every turn. Operation failures remain Problems.
|
|
4256
|
+
|
|
4257
|
+
§functionality-inspection **Inspection is not demand.** `list` and `discover` do not
|
|
4258
|
+
acquire residency, join preparation, reconcile worker documents, or extend warm
|
|
4259
|
+
retention. An enabled definition no resident publication has prepared is `dormant`: every one while
|
|
4260
|
+
the family is cold, and one that arrived or changed out of band until the next turn publishes it
|
|
4261
|
+
({§functionality-hotload}). During replacement the preceding publication remains authoritative;
|
|
4262
|
+
the candidate is never presented as active. A published outcome belongs to the
|
|
4263
|
+
complete definition prepared, not merely its alias. Cooling leaves durable definitions
|
|
4264
|
+
inspectable. Mutations and protocol continuations retain their residency rules.
|
|
4265
|
+
|
|
4266
|
+
§functionality-preparation-visibility **Preparation is workspace activity, not
|
|
4267
|
+
model context.** The coordinator owns a current `FunctionalityPreparationActivity`
|
|
4268
|
+
per preparing family. `workspacePreparationStatus(workspaceId)` returns that same
|
|
4269
|
+
state without demand; `workspace/preparation` broadcasts
|
|
4270
|
+
`{ workspaceId, preparation: [...] }` whenever it changes.
|
|
4271
|
+
|
|
4272
|
+
| Boundary | Visible state |
|
|
4273
|
+
|---|---|
|
|
4274
|
+
| Preparation begins | Family, `phase: preparing`, `alias: null`, UTC `since` |
|
|
4275
|
+
| Adapter calls `progress(alias)` | Enabled alias being prepared; a fresh `since` |
|
|
4276
|
+
| Prepared candidate enters publication | `phase: publishing`, `alias: null` |
|
|
4277
|
+
| Commit, rejection, or rollback settles | Family removed; empty array means no preparation |
|
|
4278
|
+
|
|
4279
|
+
Preparation reports neither definitions nor credentials, does not alter the
|
|
4280
|
+
publication contract, and creates no log entries or model Notices. Published
|
|
4281
|
+
failures retain their exact Problems in `list`. Concurrent consumers share the
|
|
4282
|
+
workspace activity; a client disconnect does not clear another consumer's work.
|
|
4093
4283
|
|
|
4094
4284
|
§functionality-adapter **An adapter owns protocol truth.** It declares its
|
|
4095
4285
|
family, namespace owner, definition schema, contributed defaults, discovery,
|
|
@@ -4099,16 +4289,66 @@ family declares, at admission, in the service projection, and on persisted
|
|
|
4099
4289
|
state, so an environment variable's name is an alias exactly as a skill name
|
|
4100
4290
|
is. Admission distinguishes explicit client
|
|
4101
4291
|
actions from model operations where the family contract requires it
|
|
4102
|
-
({§members-model-scope}). Preparation
|
|
4292
|
+
({§members-model-scope}). Preparation receives each complete definition with optional
|
|
4293
|
+
adapter-owned interpretation context. Context is source semantics, not policy or provenance;
|
|
4294
|
+
it participates in runtime identity and hot-load comparisons, is never projected as configuration
|
|
4295
|
+
or persisted into a workspace override, and cannot survive replacement by a local definition.
|
|
4296
|
+
Removing that override restores the current inherited definition and context together.
|
|
4297
|
+
Descriptive provenance alone does not change runtime identity. Preparation returns runtimes, documents, per-alias
|
|
4103
4298
|
outcomes, and a snapshot with `commit`/`abort`. Successful publication commits;
|
|
4104
4299
|
failure aborts; cooling tears down. Protocol continuations remain ordinary
|
|
4105
4300
|
module actions. Optional `forget` releases an installed or provisioned
|
|
4106
|
-
definition before removal; failure rejects removal
|
|
4301
|
+
definition before removal; failure rejects removal. The
|
|
4107
4302
|
seam's shapes — the identity a verb acts under, its options, definition
|
|
4108
4303
|
sources, outcomes, preparation, the prepared result and the family handle —
|
|
4109
4304
|
are declared once in `plurnk-contracts` and imported by core and every
|
|
4110
4305
|
module; core adds only its own face of the seam, the runtime registration a
|
|
4111
4306
|
resident family prepares and the scheme facet it may expose.
|
|
4307
|
+
An adapter may expose current partial-source `configurationNotices`; these join the ordinary
|
|
4308
|
+
workspace diagnostics without preventing independently valid definitions from preparing.
|
|
4309
|
+
|
|
4310
|
+
§functionality-hotload **Out-of-band state is admitted before the next turn.** An adapter whose
|
|
4311
|
+
`available` reads state that changes outside the daemon, such as skill roots ({§skills-hotload}) or
|
|
4312
|
+
installed plugins ({§agent-plugins-hosting}), implements `refreshIfChanged`. Turn admission calls it
|
|
4313
|
+
for every family under the workspace gate before packet assembly. The family handle's `refresh` with
|
|
4314
|
+
`ifChanged` republishes a resident family only when the enabled definitions it would prepare differ
|
|
4315
|
+
from the ones its publication prepared. The coordinator makes that comparison because it alone knows
|
|
4316
|
+
what it published, so a change `list` saw first is still published at the next turn. A family whose
|
|
4317
|
+
definitions do not capture its published content, such as a skill's files, republishes
|
|
4318
|
+
unconditionally when that content changed. An unchanged family dispatches nothing.
|
|
4319
|
+
|
|
4320
|
+
§agent-plugins-hosting **Installed Agent Plugins are found like skills.** A workspace's plugins are
|
|
4321
|
+
the immediate child directories of its project's `.agents/plugins`, then
|
|
4322
|
+
`$XDG_CONFIG_HOME/plurnk/plugins` (plurnk alone), then `~/.agents/plugins` (every agent), loaded and
|
|
4323
|
+
validated by `@plurnk/plurnk-agent-plugins` ({§agent-plugins-roots}), followed by standard plugin
|
|
4324
|
+
bundles in the installed npm graph. An earlier source shadows a later plugin of the same manifest
|
|
4325
|
+
name, regardless of distribution or directory name. Native discovery uses that same cascade with
|
|
4326
|
+
the project root omitted; workspace discovery includes it. A plugin's `PLUGIN_DATA` is
|
|
4327
|
+
`$XDG_DATA_HOME/plurnk/plugins/<name>/<sha256(canonical-root)>`, kept across in-place updates and
|
|
4328
|
+
moved with a state root ({§state-root}). Distinct installations never share data by name alone;
|
|
4329
|
+
workspaces referencing the same canonical installation share its data. Modules receive a workspace's plugins,
|
|
4330
|
+
in precedence order, through the setup seam's `readWorkspacePlugins`, with one signature that changes
|
|
4331
|
+
exactly when a plugin, its manifest, its MCP configuration, or its skills change
|
|
4332
|
+
({§functionality-hotload}), and the roots the workspace has. This read-only source loader does
|
|
4333
|
+
not install or delete plugins. MCP's own lifecycle is independent ({§mcp-definitions}).
|
|
4334
|
+
|
|
4335
|
+
Portable components use each family's existing management and publication path. Within a source
|
|
4336
|
+
scope, standalone definitions precede bundled components; nearer scopes precede farther scopes,
|
|
4337
|
+
with npm last. Complete environment definitions override those inputs, followed by workspace
|
|
4338
|
+
definitions and enabledness. Inspection names the winning component file as plugin provenance.
|
|
4339
|
+
Removing a workspace override restores inheritance; it never deletes the installed bundle.
|
|
4340
|
+
Current plugin-source diagnostics join the workspace's configuration notices before inference.
|
|
4341
|
+
|
|
4342
|
+
§agent-roots **A daemon reads the roots `PLURNK_SERVICE_ROOTS` names.** A comma list drawn from
|
|
4343
|
+
`project`, `plurnk` and `global`, nearest first, selects which Agent Skills, Agent Plugins and standalone MCP roots a
|
|
4344
|
+
daemon reads; the default names all three. The real-model gate profile selects
|
|
4345
|
+
its discovery roots explicitly ({§operator-config-real-model-profile}), so
|
|
4346
|
+
operator roots do not shape a gate. Root selection does not restrict explicit
|
|
4347
|
+
source definitions. Skills mutations change workspace bindings, not these roots
|
|
4348
|
+
({§skills-functionality}); MCP mutations likewise remain workspace-owned. The module setup seam's
|
|
4349
|
+
`workspaceConfigurationDirectories` supplies selected `<project>/.agents`,
|
|
4350
|
+
`$XDG_CONFIG_HOME/plurnk`, and `~/.agents` directories in precedence order, omitting
|
|
4351
|
+
project when no project is bound. Modules own their file formats; core owns discovery roots.
|
|
4112
4352
|
|
|
4113
4353
|
An adapter may expose a `scheme` facet beneath its family's runtime namespace
|
|
4114
4354
|
({§runtime-resource-binding}). A facet claims a path subtree and is the scheme's
|
|
@@ -4128,9 +4368,10 @@ operator's ceiling ({§exec-env-scoped}, service origin) precede workspace defau
|
|
|
4128
4368
|
worker overrides ({§workspace-env}). `add` takes
|
|
4129
4369
|
the name as the alias and `{ "value": "…" }` as the definition, used verbatim with no
|
|
4130
4370
|
interpolation. `disable` withholds a name in the selected scope while retaining it;
|
|
4131
|
-
`remove` forgets a locally-owned entry and
|
|
4132
|
-
|
|
4133
|
-
|
|
4371
|
+
`remove` forgets a locally-owned entry and restores any same-name inherited value
|
|
4372
|
+
and enabledness ({§configuration-definition-resolution}). Use `disable` to keep
|
|
4373
|
+
an inherited name out of subsequent launches. Definitions from a lower layer
|
|
4374
|
+
cannot be removed in the current scope.
|
|
4134
4375
|
|
|
4135
4376
|
`list` projects effective values with their origin. Values are shown: the ceiling is the security
|
|
4136
4377
|
boundary, not the projection, and any admitted name is already readable by every command the
|
|
@@ -4539,6 +4780,7 @@ adding a loop to it. LOOK text anchors resolve through the same
|
|
|
4539
4780
|
| §notifications-loop-proposal `loop/proposal` | contracts-owned `ProposalProjection` | Dispatch pauses on a durable 202 proposal. `disposition` is the sole authority for whether a client presents review UI; live and reconnect share {§proposal-projection}. |
|
|
4540
4781
|
| §notifications-loop-interaction `loop/interaction` | contracts-owned `ClientInteractionProjection` | An operation is paused on client input. Live delivery and reconnect discovery share {§client-interactions}; workspace scope remains the event envelope. |
|
|
4541
4782
|
| §notifications-workspace-created `workspace/created` | `{ id, name, projectRoot }` | A workspace is created. This is the only current global event. |
|
|
4783
|
+
| `workspace/preparation` | `{ workspaceId, preparation: FunctionalityPreparationActivity[] }` | Workspace capability preparation changes; snapshot and clearing semantics follow {§functionality-preparation-visibility}. |
|
|
4542
4784
|
| §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 initiating actor used for conversation routing, never entry ownership or access control. `target` is the canonical resource URI. Optional numeric coordinates identify the causal log item, independently of that URI. 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 by canonical workspace address. |
|
|
4543
4785
|
| §notifications-stream-concluded `stream/concluded` | `{ entryId, workerId, target, subscriptionId, scheme, result, summary, wakeAction, loop_seq?, turn_seq?, sequence? }` | A subscription closes. `workerId` identifies the initiating actor; `target` is the canonical resource URI. Optional numeric fields identify the causal log item, never parsed from `target`. Exact result truth is preserved. `wakeAction` reports `wake-pending` before settlement, `no-op-active-loop` when work is already executing, `no-loop`, or `skipped-aborted`/`skipped-cancelled` for an aborted worker scope. A pending wake predicts neither execution nor recipient count; subsequent ordinary loop events report actual progress and completion. |
|
|
4544
4786
|
| §notifications-notice-event `notice/event` | `{ workerId, loopId, notice: Notice }` | A transient observation or progress notice occurs. `workerId` owns loop activity; only workspace derivation progress uses `null` with `loopId=0`. It cannot alter durable history, scheduling, recovery, or model-visible failure truth. |
|
|
@@ -4596,23 +4838,23 @@ flowchart LR
|
|
|
4596
4838
|
|
|
4597
4839
|
### §packet-wire-envelope The wire envelope
|
|
4598
4840
|
|
|
4599
|
-
The packet reaches the provider under the roles the model was tuned on, its bytes unchanged:
|
|
4841
|
+
The packet reaches the provider as a transcript, under the roles the model was tuned on, its bytes unchanged:
|
|
4600
4842
|
|
|
4601
4843
|
| Message | Role | Content |
|
|
4602
4844
|
|:--|:--|:--|
|
|
4603
4845
|
| 1 | `system` | the system slot, as rendered |
|
|
4604
|
-
| 2 … | `user` | the
|
|
4605
|
-
|
|
|
4606
|
-
| last | `user` | the
|
|
4607
|
-
|
|
4608
|
-
Only role boundaries are added
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4614
|
-
|
|
4615
|
-
|
|
4846
|
+
| 2, 4, … | `user` | the user slot's text up to and including the record of the next placed emission that delivers text ({§emission-row}); the first opens with `## Log` |
|
|
4847
|
+
| 3, 5, … | `assistant` | that row's frozen emission projection less its NOTE and WAIT blocks, as the worker's own message ({§emission-row}) |
|
|
4848
|
+
| last | `user` | the records after the last emission delivered, then the remaining user sections in {§packet-cache-monotone} order; native parts ride here ({§packet-attachment-parts}) |
|
|
4849
|
+
|
|
4850
|
+
Only role boundaries are added: joined by blank lines, the user messages are the user slot's
|
|
4851
|
+
bytes, in record order. An emission is placed exactly when its row is present in the final log
|
|
4852
|
+
section, so curation governs the transcript: a KILLed emission row, or one a trusted transform
|
|
4853
|
+
removed ({§packet-plugin-transform}), takes its emission with it, and a log without emission rows
|
|
4854
|
+
is one user message. An emission of only NOTE and WAIT delivers nothing, so its record runs on
|
|
4855
|
+
into the next user message. The Worker block and the status clump always follow the log, so a
|
|
4856
|
+
request never ends on an emission, and the projection refuses one that would. The digest's packet
|
|
4857
|
+
artifacts record the sections, and `.wire.json` the messages ({§share-packet-names}).
|
|
4616
4858
|
|
|
4617
4859
|
### §packet-cache-monotone Default order and cache locality
|
|
4618
4860
|
|
|
@@ -4624,7 +4866,7 @@ Conditional absence never reorders the surviving default sections.
|
|
|
4624
4866
|
| 2 | system | `system-policy` | Operator policy; empty content is omitted on the wire. |
|
|
4625
4867
|
| 3 | system | `inject` | Present only when operator notes are configured. |
|
|
4626
4868
|
| 4 | user | `log` | Append-mostly model-visible history; the first user section, so the cached prefix ends inside it. |
|
|
4627
|
-
| 5 | user | `worker` | `Worker`: `{"path": "worker://alice", "parent": <address or null>, "loop": L, "turn": T
|
|
4869
|
+
| 5 | user | `worker` | `Worker`: `{"path": "worker://alice", "parent": <address or null>, "loop": L, "turn": T}`, the actor and the coordinate this packet's response becomes ({§packet-current-turn}). |
|
|
4628
4870
|
| 6 | user | `delegation` | `Delegation`: per-turn `{workers, streams}` pointers; always present, each list `[]` when empty ({§packet-empty-sections}). |
|
|
4629
4871
|
| 7 | user | `errors` | Per-turn failure pointers; empty content is omitted. |
|
|
4630
4872
|
| 8 | user | `notices` | Per-turn observations; empty content is omitted. |
|
|
@@ -4635,7 +4877,9 @@ Conditional absence never reorders the surviving default sections.
|
|
|
4635
4877
|
|
|
4636
4878
|
The order favors prefix-cache locality where semantics permit: the definition
|
|
4637
4879
|
and privileged policy lead operator notes, while the append-mostly
|
|
4638
|
-
log leads the volatile user-status clump.
|
|
4880
|
+
log leads the volatile user-status clump. An emission sits at the row that announces it
|
|
4881
|
+
({§packet-wire-envelope}), so the reusable prefix runs through every retained emission, and
|
|
4882
|
+
retiring one breaks the prefix at its row like any other curation. It does **not** claim that every system byte is
|
|
4639
4883
|
immutable or that the complete packet is globally monotone in volatility:
|
|
4640
4884
|
operator notes and policies can change. Trust is a separate
|
|
4641
4885
|
admission rule. The system slot contains trusted control-plane material;
|
|
@@ -4651,6 +4895,8 @@ Each initial or returned list passes the schemes-owned validator, including
|
|
|
4651
4895
|
unique-name enforcement, before the next transformer or renderer. Each
|
|
4652
4896
|
transformer may inspect the section content and add, remove, or reorder
|
|
4653
4897
|
sections. It receives no separate engine, database, actor, or request context.
|
|
4898
|
+
Emission placement reads the final log section, so a transform that removes an
|
|
4899
|
+
emission row's record removes its emission from the transcript ({§packet-wire-envelope}).
|
|
4654
4900
|
|
|
4655
4901
|
This is strictly a trusted in-process seam, admitted through the common plugin
|
|
4656
4902
|
trust gate; an external client action cannot invoke it. Whole-list transformation is
|
|
@@ -4664,13 +4910,13 @@ time of measurement.
|
|
|
4664
4910
|
|
|
4665
4911
|
| Fact | Owner and unit | Time | Contract |
|
|
4666
4912
|
|:-----|:---------------|:-----|:---------|
|
|
4667
|
-
| Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies,
|
|
4913
|
+
| Core curation weight | `contentWeight = ceil(chars/2)` over channel content, canonical log bodies, rendered packet slots, and placed emissions | Write/build | Stable, model-independent pressure and curation savings; never a tokenizer claim. |
|
|
4668
4914
|
| §tokenomics-context-envelope-admission Provider input capacity | Provider model limits and configured output reservation, in provider tokens | Before every logical request | `min(maxInputTokens, contextWindow - outputBudget)` over the known terms. The provider alone measures the complete request and admits, defers, or rejects it. |
|
|
4669
4915
|
| Provider generation envelope | Provider response grant and optional reasoning subset, in provider tokens | Before every logical request | The reservation includes hidden reasoning; its strict reasoning subset is never additive. The response grant follows {§provider-flexed-allowance}. |
|
|
4670
4916
|
| Provider usage and cost | Provider-reported input/output/cache/reasoning tokens and monetary evidence | After every physical request | Durable physical-request forensics under {§provider-usage}; never curation state or a preflight estimate. |
|
|
4671
4917
|
|
|
4672
4918
|
- §tokenomics-weight-stored-at-write **Curation weight, stored at write.** `entry_channels.weight` weighs the complete channel content. `log_entries.weight` weighs the complete canonical `LogBody` content before coordinate and packet presentation; persistence `tx`/`rx` envelopes contribute nothing merely by existing, and proposal settlement recomputes the value when the canonical result changes. Bodyless rows therefore weigh zero. The stored number is a stable content-depth measurement, not a provider-token prediction. `entry_channels.lines` is the channel's line count beside it, a stored generated column SQLite keeps on every write as the persisted mirror of {§logical-line-count} (a trailing newline terminates the last line; empty content has none), so a catalog lists extent without reading bodies.
|
|
4673
|
-
- §tokenomics-render-weight-budget **Packet curation budget.** `logTokensTotal` measures the *complete assembled packet* after section transforms and readout substitution; it is not a sum of log-row `logTokens` fields. Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and the displayed total equals the stored request weight. Receipt, FIND-item, pressure-inventory, total, and ceiling figures all use the same curation ruler. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
|
|
4919
|
+
- §tokenomics-render-weight-budget **Packet curation budget.** `logTokensTotal` measures the *complete assembled packet* after section transforms and readout substitution, its rendered slots and the emissions it places ({§emission-row}); it is not a sum of log-row `logTokens` fields. Core measures minimum-width probes, monotonically expands fields that do not fit, then right-aligns final values into those widths; final substitution is length-invariant and the displayed total equals the stored request weight. Receipt, FIND-item, pressure-inventory, total, and ceiling figures all use the same curation ruler. A `SUM` of stored content weights measures a different artifact and cannot substitute for packet render weight.
|
|
4674
4920
|
- §tokenomics-calibrated-readout **Convert capacity, never content costs.** Before packet assembly, Core obtains the answering model's last five settled emission responses pairing a measured packet weight with a provider-reported prompt count. The conversion factor is `sum(reported) / sum(weight)`; fewer than three samples use 1. `logTokensMax = floor(inputCapacity / factor)` converts provider capacity into curation units. Zero means no whole curation unit fits; unknown input capacity remains `null`. The built packet captures this allowance once for its readout, pressure inventory, overflow admission, and persisted client gauge. Later responses cannot change that packet's allowance. Samples are model-keyed, not worker-local; a model with no samples starts at 1. Calibration never changes stored weights, rendered receipt costs, or the immutable request history ({§tokenomics-agnostic-ruler}).
|
|
4675
4921
|
- §tokenomics-window-partition **One capacity derivation; no service-side token budget.** The provider owns model limits, the configured output reservation, and each call's response grant. Its resolved `inputCapacity` supplies the physical denominator exposed to clients and the boundary conversion into curation units ({§tokenomics-calibrated-readout}). Core shapes context in curation units; provider request-shaped evidence alone admits or rejects physical I/O. `PLURNK_SERVICE_PROMPT_BUDGET`, `PLURNK_SERVICE_SAFETY`, and the additive reasoning/completion reserve knobs are retired; local and custom deployments tune context window, total output budget, optional reasoning subset, and prompt-projection percentage at their owning layers.
|
|
4676
4922
|
- §tokenomics-prompt-projection-share **Prompt projection is stable packet policy.**
|
|
@@ -4876,7 +5122,7 @@ leaves the request-only record, while rejected exchanges remain in their
|
|
|
4876
5122
|
| Turn state | `turns.packet` (the bag) + `turn_sections` rows |
|
|
4877
5123
|
| ----------------------------- | ----------------------------------------------- |
|
|
4878
5124
|
| No admitted model request (including initialization and local capacity rejection) | SQL `NULL`, no rows |
|
|
4879
|
-
| Request assembled | `{ weight, attributions }` + the sections as items |
|
|
5125
|
+
| Request assembled | `{ weight, attributions }` + the sections as items; the emissions it placed stay in their rows ({§emission-row}) |
|
|
4880
5126
|
| Response admitted | `{ weight, attributions, assistant, assistantRaw }` + the sections as items |
|
|
4881
5127
|
|
|
4882
5128
|
§packet-items **Sections are rows over content-addressed items; the bag never holds them.**
|
|
@@ -4932,6 +5178,7 @@ consumer reconstructs a name. A name that cannot be a file name, or two turns sh
|
|
|
4932
5178
|
|----------|--------------|-----------|
|
|
4933
5179
|
| `<stem>.assistant.md` | The turn has an `ops` source | Exact `turn_sources.content`, independent of log rows |
|
|
4934
5180
|
| `<stem>.system.md`, `<stem>.user.md` | The turn stored a provider request | Stored text sections projected through `PacketWire`; native parts are not Markdown |
|
|
5181
|
+
| `<stem>.wire.json` | The turn stored a provider request | The request's text messages in order, its worker's emission rows placed ({§packet-wire-envelope}); `<stem>.wire.invalid.json` names a stored log that cannot be projected |
|
|
4935
5182
|
| `digest.json` turn `attachments` | Every turn | Stored native attachment descriptors; `[]` means a request without attachments, `null` means no valid stored request. Selection is not proof of provider acceptance. |
|
|
4936
5183
|
| `<stem>.assistantRaw.json` | The request has an admitted provider response | Stored opaque provider response |
|
|
4937
5184
|
| `<stem>.response.md`, attempt artifacts | The request received no admitted response | Stored request and attempt state |
|
|
@@ -5053,7 +5300,7 @@ ordered set exactly once. Recovery retries complete the same queued loop and nev
|
|
|
5053
5300
|
mint duplicate work. Output withholding preserves readable arrival rows; explicit
|
|
5054
5301
|
KILL follows the ordinary log contract.
|
|
5055
5302
|
|
|
5056
|
-
§completion-defers-to-messages **Conclusion does not cross an
|
|
5303
|
+
§completion-defers-to-messages **Conclusion does not cross an unobserved arrival.** The end-of-program check includes messages that arrived during inference. The final database transition atomically requires every inbox message to be published and the loop's observed wake revision to equal its worker's current revision. Answer delivery is not this barrier. An arrival that wins the race continues the current loop; one admitted after conclusion belongs to a new loop. Orphan recovery preserves messages accepted before an independently forced termination.
|
|
5057
5304
|
|
|
5058
5305
|
§packet-catalog **Catalogs are query results, not packet state.** The packet
|
|
5059
5306
|
stores no materialized manifest. Complete and one-level entry directories,
|
|
@@ -5151,11 +5398,25 @@ retain distinct contracts and lifetimes.
|
|
|
5151
5398
|
|
|
5152
5399
|
§digest-wire-line **Wire health aggregated.** Each worker summary renders a `Wire:` line — total physical provider requests, error-outcome count, and the error percentage when nonzero. Provider-level failures are absorbed by retries below the packet stream, so without this aggregate a rate-limit storm is invisible in every summary while the model's experience stays clean.
|
|
5153
5400
|
|
|
5154
|
-
§digest-cache-ledger **
|
|
5401
|
+
§digest-cache-ledger **Measured cache reuse and estimated prompt overlap are separate.**
|
|
5402
|
+
|
|
5403
|
+
| Projection | Meaning |
|
|
5404
|
+
|---|---|
|
|
5405
|
+
| `digest.json` provider-request `cachedTokens`, `inputTokens` | Exact provider-reported cache reads and input tokens; absent quantities remain `null`, reported zero remains zero. Every physical request counts, including retries and first requests of new loops. Stored packet availability is irrelevant to these counters. |
|
|
5406
|
+
| Turn `cache=<cached>/<input>` | Sum each measured quantity over that turn's requests. If any request omits a quantity, that sum is `?`. |
|
|
5407
|
+
| Workspace `Cache: <cached> of <input> reported input tokens read from cache (<pct>%) over <n> requests` | Sum only requests reporting both counters. Percentage is cache reads / input, rounded to one decimal; zero input is `n/a`. Requests missing either counter are counted separately as `missing input or cache usage (excluded)`. |
|
|
5408
|
+
| `digest.json` provider-request `adjacentPrefixTokensEstimate` | Optional loop-local diagnostic: the longest common character prefix with the preceding request, weighted under {§tokenomics-agnostic-ruler} as a share of the current stored prompt, multiplied by reported input tokens. First request: `0`; missing current/preceding packet or current input: `null`. Empty prompts have zero overlap. |
|
|
5409
|
+
|
|
5410
|
+
The prefix estimate uses the stored emission packet's wire message order, roles
|
|
5411
|
+
and content. A BARE request's input is not that packet; its prefix estimate and
|
|
5412
|
+
the following request's comparison are unknown. The estimate is
|
|
5413
|
+
neither provider tokenization nor a cache ceiling, and never supplies a cache-ratio
|
|
5414
|
+
denominator. Caching across loops or against other provider-resident prefixes
|
|
5415
|
+
does not make the measured counters inconsistent.
|
|
5155
5416
|
|
|
5156
5417
|
§digest-edit-census **Every model EDIT by the form it authored, how it landed, and whether it came back.** For each worker the digest reads every model-authored EDIT row and classifies the form from the row's stored marker and the durable statement's pattern: `hash` (one anchor), `line` (one line number), `range` (two marks), `insert` (the zero-width `<L,1,L,1>` form, {§zero-width-column-one-insert}), `column` (any other four-mark region), `prepend` / `append` (`<0>` / `<-1>`), `offset` (a tolerated anchor offset, {§anchor-offset}), `pattern` (a selection matcher), `whole` (no marker: a creation when it lands 201). It counts the EDITs, those refused (status ≥ 400), and the *revisits*: an EDIT of a path the same worker had edited within its previous two model turns — the shape of a repair without the claim of one. Each worker summary renders `EDITs: <n> · <form>=<count>… · refused=<k> · revisits=<r>` (`(no edits)` for none); `digest.json` carries the census as `edit_census` on every worker and stamps every EDIT log entry with its `edit_form` and `edit_revisit`. A form is a fact about what was written, never about intent; the bench sheet reads the counts as friction and leaves the judgement to the reader.
|
|
5157
5418
|
|
|
5158
|
-
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log event with its initial and current projection, causal `source`, and structured `attrs`; every exact log-KILL target effect; the exact Problem on every failed row; each loop's exact terminal result, settlement time, scheduled due time, recurring interval, and recurrence lineage; and every ordered physical provider request. Programs still produce chronological `assistant.md` artifacts after every READ receipt is KILLed; source is independent of log curation. Each stored packet validates independently: one malformed historical packet remains exact raw evidence with its complete validation error chain and never prevents healthy turns from being projected. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. A worker's Cost line names how many settled requests carry no usage at all (errored or aborted exchanges) — their server-side spend is unrecorded rather than silently priced as zero. The reasoning chronology distinguishes readable reasoning content from provider-reported reasoning usage: when tokens were reported but no readable content was returned, it states both facts instead of implying that no reasoning occurred. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and durable resource coordinates render back to their authority form. Its human Markdown waterfall groups identical per-turn op outcomes and typed `entry_materialized` narrations, reporting the exact count and sequence span (`xN (seq A-B)`). Grouping keys include source and the complete target, so distinct causes, authorities, or channels never collapse. Thus amplification is conspicuous without making the diagnostic artifact itself pathological; valid packet files remain byte-identical records of what the model saw.
|
|
5419
|
+
§digest-forensic-fidelity **Forensic fidelity and cardinality.** The digest's machine-readable JSON preserves every log event with its initial and current projection, causal `source`, and structured `attrs`; every exact log-KILL target effect; the exact Problem on every failed row; each loop's exact terminal result, settlement time, scheduled due time, recurring interval, and recurrence lineage; and every ordered physical provider request. Programs still produce chronological `assistant.md` artifacts after every READ receipt is KILLed; source is independent of log curation. Each worker summary's `Emissions:` line counts its announced emission rows, those the worker KILLed, and the headings it echoed ({§emission-row}); the op mix leaves the announcements out. Each stored packet validates independently: one malformed historical packet remains exact raw evidence with its complete validation error chain and never prevents healthy turns from being projected. Accounting on broader rows is the shared exact derivation from that ledger, never a second stored fact. A worker's Cost line names how many settled requests carry no usage at all (errored or aborted exchanges) — their server-side spend is unrecorded rather than silently priced as zero. The reasoning chronology distinguishes readable reasoning content from provider-reported reasoning usage: when tokens were reported but no readable content was returned, it states both facts instead of implying that no reasoning occurred. The human Markdown waterfall shows a present causal source and may preview only the Problem detail because it remains a triage projection, not the machine record. Targets reconstruct the model-visible address, including hostname, port, serialized query, and fragment; an authority-bearing URL must never degrade from `https://host/path` to `https:///path`, and durable resource coordinates render back to their authority form. Its human Markdown waterfall groups identical per-turn op outcomes and typed `entry_materialized` narrations, reporting the exact count and sequence span (`xN (seq A-B)`). Grouping keys include source and the complete target, so distinct causes, authorities, or channels never collapse. Thus amplification is conspicuous without making the diagnostic artifact itself pathological; valid packet files remain byte-identical records of what the model saw.
|
|
5159
5420
|
|
|
5160
5421
|
Unrecognized actionless log rows are retained and labelled as such, not
|
|
5161
5422
|
interpreted as executable turnOps or allowed to prevent the remaining digest.
|
|
@@ -5197,7 +5458,7 @@ USD, and token totals across every physical exchange the turn paid for, failed
|
|
|
5197
5458
|
calls included. It is the shared exact derivation from the ledger, never a second
|
|
5198
5459
|
stored fact, so a live watcher accrues running loop cost per turn (#465).
|
|
5199
5460
|
|
|
5200
|
-
§notice-content-offset-pointer **Content-offset position.** A non-fatal diagnosis on an accepted emission (for example `grammar_unenforced` or `parse_advisory`) carries `position: { type: "content-offset", line, column }` into the model's exact `ops://<worker>/<loop>/<turn>` source. A bounded hard parse error becomes a durable failed operation whose Problem Details preserve its line, column, source, and parser-owned diagnostic. Hard errors that make the frame untrustworthy remain only with their rejected forensic attempt.
|
|
5461
|
+
§notice-content-offset-pointer **Content-offset position.** A non-fatal diagnosis on an accepted emission (for example `grammar_unenforced` or `parse_advisory`) carries `position: { type: "content-offset", line, column }` into the model's exact `ops://<worker>/<loop>/<turn>` source; the transcript shows that turn's canonical emission instead ({§emission-row}), so the position names a line of the source, which READ of its address shows. A bounded hard parse error becomes a durable failed operation whose Problem Details preserve its line, column, source, and parser-owned diagnostic. Hard errors that make the frame untrustworthy remain only with their rejected forensic attempt.
|
|
5201
5462
|
|
|
5202
5463
|
### Executable tool resources
|
|
5203
5464
|
|
|
@@ -5272,8 +5533,10 @@ enable | disable | remove`, `workspace.members.<verb>` for the client,
|
|
|
5272
5533
|
```` ```members (<verb>) ```` for the model — for what the model may see, exactly as they do for skills and
|
|
5273
5534
|
MCP servers. A definition is one gitignore-style glob, `{ glob }`, relative to the project
|
|
5274
5535
|
root; a leading `!` excludes matching members, and an exclusion wins over every inclusion.
|
|
5275
|
-
|
|
5276
|
-
rides the definition
|
|
5536
|
+
Admission provenance (`service-configuration`, `client-action`, `model-proposal`)
|
|
5537
|
+
rides the definition to enforce {§members-model-scope}; configuration-source
|
|
5538
|
+
provenance belongs to the shared inspection projection ({§configuration-provenance}).
|
|
5539
|
+
The alias is a short name, suggested from the glob. `list` shows each
|
|
5277
5540
|
definition with what it resolved to — `include` or `exclude`, the pattern, the members it
|
|
5278
5541
|
admits or removes (count and a bounded sample), and for a model's inclusion the matches the
|
|
5279
5542
|
repository's ignore rules refused — so the model sees what its glob did and adapts.
|
|
@@ -5282,10 +5545,12 @@ repository's ignore rules refused — so the model sees what its glob did and ad
|
|
|
5282
5545
|
untracked, absent); a glob previews what `add` would include or exclude. Names only, never
|
|
5283
5546
|
content; nothing is added.
|
|
5284
5547
|
|
|
5285
|
-
§members-configuration *Available definitions.*
|
|
5286
|
-
(`!glob` excludes)
|
|
5287
|
-
|
|
5288
|
-
|
|
5548
|
+
§members-configuration *Available definitions.* `PLURNK_MEMBERS_<alias>=<glob>`
|
|
5549
|
+
declares one service-origin rule (`!glob` excludes). The shared naming and
|
|
5550
|
+
enabledness dialect is {§resource-environment}; `PLURNK_MEMBERS_ENABLED` supplies
|
|
5551
|
+
the panel default and `PLURNK_MEMBERS_<alias>_ENABLED` overrides one rule.
|
|
5552
|
+
An empty glob or a bare `!` fails validation. Controls may precede their rule;
|
|
5553
|
+
they are validated without creating a definition ({§resource-environment}).
|
|
5289
5554
|
|
|
5290
5555
|
§members-model-scope *The model's authority.* A model's `add` is admitted against
|
|
5291
5556
|
`PLURNK_SERVICE_MEMBERS_MODEL_SCOPE` in the file-creation lattice `none < root <
|
|
@@ -5317,53 +5582,98 @@ verbs are these verbs.
|
|
|
5317
5582
|
§skills-functionality **Agent Skills are one workspace Functionality family.**
|
|
5318
5583
|
Core registers the `skills` family with the coordinator ({§functionality-coordinator});
|
|
5319
5584
|
its adapter owns protocol truth for standard Agent Skills and nothing else. A
|
|
5320
|
-
definition is `SkillDefinition
|
|
5321
|
-
`
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5585
|
+
definition is `SkillDefinition`: the standard skill `name`, its `source`, and
|
|
5586
|
+
optional Git `ref`/resolved `commit` ({§skills-sources}). Only a host-provided
|
|
5587
|
+
resource tree omits `source`. Source location is not mutation ownership: standard
|
|
5588
|
+
project/global roots are read-only configuration inputs; live changes belong to
|
|
5589
|
+
the workspace. Plurnk neither installs into nor deletes from those roots.
|
|
5325
5590
|
|
|
5326
5591
|
*Available definitions.* The filesystem is the only truth about installation:
|
|
5327
|
-
every `<root>/<name>/SKILL.md` directory under the project then
|
|
5328
|
-
root is one service-origin definition,
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
({§functionality-state}); a disabled skill stays client-visible and
|
|
5332
|
-
model-facing trace.
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
skill
|
|
5346
|
-
|
|
5592
|
+
every `<root>/<name>/SKILL.md` directory under the project, plurnk, then global
|
|
5593
|
+
root is one service-origin definition, a nearer root shadowing a farther one and
|
|
5594
|
+
all of them shadowing host-provided trees by name. Each filesystem definition
|
|
5595
|
+
names its actual source directory. The workspace's durable state owns
|
|
5596
|
+
enablement ({§functionality-state}); a disabled skill stays client-visible and
|
|
5597
|
+
leaves no model-facing trace.
|
|
5598
|
+
|
|
5599
|
+
§skills-configuration **Skills use the shared definition cascade.**
|
|
5600
|
+
|
|
5601
|
+
| Layer, low to high | Definition source |
|
|
5602
|
+
|---|---|
|
|
5603
|
+
| Service | Host-provided trees |
|
|
5604
|
+
| Standard locations | Global, plurnk, then project roots selected by {§agent-roots} |
|
|
5605
|
+
| Cascading environment | `PLURNK_SKILLS_<name>={"name":"<name>","source":"…","ref"?:"…"}` replaces the complete definition |
|
|
5606
|
+
| Live workspace | `skills (add)` creates a workspace definition through {§functionality-coordinator} |
|
|
5607
|
+
|
|
5608
|
+
`PLURNK_SKILLS_ENABLED` supplies default enabledness; `<name>_ENABLED` overrides
|
|
5609
|
+
it independently ({§resource-environment}). The decoded environment alias must
|
|
5610
|
+
equal the standard skill name, including digit-leading and Unicode names.
|
|
5611
|
+
Blank definitions are invalid; controls may precede a definition. Environment
|
|
5612
|
+
validation checks shape, names, remote URL rules, and Git-only `ref` without
|
|
5613
|
+
fetching or opening a source; `commit` is service-recorded, not an input.
|
|
5614
|
+
|
|
5615
|
+
*Discovery is inert.* `discover {source}` lists the standard skills one source
|
|
5616
|
+
carries, each a candidate with `source` provenance and the exact definition to
|
|
5617
|
+
add; it never installs, persists, or enables. Agent Skills have no standard
|
|
5618
|
+
registry, so `discover {query}` is 400 `query-unsupported`, naming the source
|
|
5619
|
+
forms. Client `configuration` contributes nothing and is refused with 400.
|
|
5620
|
+
|
|
5621
|
+
*Admission.* `add {alias, definition}` requires `alias = name` and a `source`;
|
|
5622
|
+
the workspace definition may shadow a service skill of the same name. Relative
|
|
5623
|
+
sources require a project root; absolute sources work in headless workspaces.
|
|
5624
|
+
A local source is recorded as its absolute path.
|
|
5625
|
+
A git source records the `commit` its `ref` names at admission, or its default
|
|
5626
|
+
branch's when no `ref` is given; `ref` belongs to git sources, and a supplied
|
|
5627
|
+
`commit` is refused because the service records it. The family's aliases use
|
|
5628
|
+
the standard skill-name grammar ({§agent-skills-name}), including digit-leading
|
|
5629
|
+
and Unicode names, rather than the coordinator's generic default.
|
|
5347
5630
|
|
|
5348
5631
|
*Preparation.* For each enabled alias the adapter selects the host-provided
|
|
5349
|
-
tree
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5632
|
+
tree or resolves the complete source definition ({§skills-sources}). Local
|
|
5633
|
+
folders and their `SKILL.md` files remain live references, including supporting
|
|
5634
|
+
resources and symlink retargeting. Git/archive sources are materialized only
|
|
5635
|
+
inside {§module-workspace-directory}; different workspaces and complete source
|
|
5636
|
+
definitions cannot reuse each other's materializations accidentally.
|
|
5637
|
+
The first fetched Git/archive copy remains stable across enable, cooling, and
|
|
5638
|
+
restart until the complete source definition changes. In particular, an
|
|
5639
|
+
operator-configured symbolic Git ref is not an implicit update subscription.
|
|
5356
5640
|
Each admitted skill requires standard `name` and `description` frontmatter
|
|
5357
5641
|
with `name` matching its directory. A missing, uninstallable, or invalid skill
|
|
5358
|
-
is `unavailable` with its exact Problem (
|
|
5359
|
-
|
|
5360
|
-
|
|
5642
|
+
is `unavailable` with its exact Problem ({§problems-functionality}) under the
|
|
5643
|
+
coordinator's failure policy ({§functionality-model-mutation}); one bad skill
|
|
5644
|
+
never fails the family.
|
|
5645
|
+
|
|
5646
|
+
Removal follows {§skills-remove}.
|
|
5361
5647
|
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5648
|
+
§skills-sources **A source is a git remote, a folder, or a file, read with standard tools.**
|
|
5649
|
+
Fetching runs nothing it fetched. Materialized copies cannot contain references
|
|
5650
|
+
outside their skill; live resources retain {§agent-skills-directory} containment.
|
|
5651
|
+
|
|
5652
|
+
| Source | How it is read |
|
|
5653
|
+
|---|---|
|
|
5654
|
+
| Git remote: a full `https://` or `ssh://` URL, or `user@host:path` | `git ls-remote` resolves the ref at admission; preparation shallow-clones it with hooks and submodules off, and a checkout at any other commit is 409 `source-moved` |
|
|
5655
|
+
| Folder: absolute, `~/`, or relative to the project root | Live reference; standard directory/name matching applies |
|
|
5656
|
+
| A file named `SKILL.md` | Live reference to its skill directory and supporting resources |
|
|
5657
|
+
| A `.zip`, `.tar`, `.tgz`, `.tar.gz`, `.tar.bz2`, `.tar.xz`, or `.tar.zst` archive | Unpacked into private staging with `unzip` or `tar`; a lone top-level directory is the source's root |
|
|
5658
|
+
|
|
5659
|
+
Any other scheme, plain `http`, `owner/repo` shorthand, and an https URL carrying
|
|
5660
|
+
credentials are refused with `source-invalid` or `source-missing`: shorthand names no
|
|
5661
|
+
forge, and a recorded source is listed to every client. Git runs with the
|
|
5662
|
+
operator's configuration, credentials, and SSH agent, never plurnk's secrets, and
|
|
5663
|
+
never prompts; `PLURNK_SERVICE_SKILLS_FETCH_TIMEOUT_MS` bounds each fetch. The
|
|
5664
|
+
retired vendor-installer knobs (`PLURNK_SERVICE_SKILLS_CLI`, `_CLI_TIMEOUT_MS`,
|
|
5665
|
+
`_REGISTRY_URL`, `_REGISTRY_LIMIT`, `_REGISTRY_TIMEOUT_MS`) make the skills family
|
|
5666
|
+
unavailable when set, each naming what replaced it ({§configuration-repair-path}).
|
|
5667
|
+
|
|
5668
|
+
A source's skills are the directories holding a `SKILL.md`, found by walking
|
|
5669
|
+
from its root without entering `.git` or a skill already found. A fetched skill at
|
|
5670
|
+
the root is named by its frontmatter ({§agent-skills-name}); local references and
|
|
5671
|
+
directories below the root use the standard folder rule. A source with `plugin.json` at its root is an
|
|
5672
|
+
Agent Plugin and is refused with 422 `source-is-plugin`, so its skills keep the
|
|
5673
|
+
plugin's identity. Materialization copies the named skill beside its workspace-owned destination
|
|
5674
|
+
and renames it to `<root>/<name>`; a copy that holds a link out of the skill, or
|
|
5675
|
+
anything but files, directories, and inward links, is refused with 422
|
|
5676
|
+
`source-unsafe` and leaves nothing behind.
|
|
5367
5677
|
|
|
5368
5678
|
§skills-resources **A skill is a resource tree, not a rewritten document.**
|
|
5369
5679
|
The family exposes enabled, available {§agent-skills-tree} sources through
|
|
@@ -5380,13 +5690,13 @@ serialized URI address the same resource, not separate skill identities.
|
|
|
5380
5690
|
| `READ (skill://<name>/SKILL.md)` | Original frontmatter and Markdown, unchanged; relative links remain relative to the source layout. |
|
|
5381
5691
|
| `READ` / `FIND` below the authority | References, scripts, and assets retain their source paths and ordinary pattern, channel, byte, and multimodal semantics. Acquisition observes current source contents, including disappearance. |
|
|
5382
5692
|
| ```` ```runtime (skill://<name>/scripts/program.ext) ```` | Ordinary resource execution and proposal policy; preserve the native file and its siblings under {§exec-source-temporary}. Discovery and READ never execute scripts. |
|
|
5383
|
-
| Model mutation | Read-only; no EDIT, SEND, or KILL of
|
|
5693
|
+
| Model mutation | Read-only; no EDIT, SEND, or KILL of skill resources. Manage definitions and enablement through ```` ```skills ````. |
|
|
5384
5694
|
| Disable / unavailable / remove | Withdraw the authority from new resource access and discovery. Existing log receipts remain historical evidence. |
|
|
5385
5695
|
| WORK / FORK | Use the same workspace Functionality, not copied definitions or resource caches. |
|
|
5386
5696
|
|
|
5387
5697
|
Explicit skill URIs address these resources; bare operation paths still address
|
|
5388
5698
|
project files, with no implicit current-skill directory. Source resolution follows
|
|
5389
|
-
{§agent-skills-directory}, including
|
|
5699
|
+
{§agent-skills-directory}, including symlinked skill directories and containment of references.
|
|
5390
5700
|
An uninstalled Git skill is not manufactured by repository detection.
|
|
5391
5701
|
|
|
5392
5702
|
§plurnk-skill **Plurnk's own reference is an ordinary service-provided skill.**
|
|
@@ -5399,24 +5709,22 @@ same {§operator-config-env-defaults} renderer as the operator command, never th
|
|
|
5399
5709
|
effective environment. Native chapter files retain their owners and locations;
|
|
5400
5710
|
runtime-generated bytes have no invented disk location. Disable/enable,
|
|
5401
5711
|
shared workspace visibility, and project/global shadowing use the ordinary Skills lifecycle.
|
|
5402
|
-
Service-provided skills are not installer targets;
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
coordinator
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
installed or removed by any other tool is discoverable in the first subsequent
|
|
5419
|
-
model turn while an unchanged set dispatches nothing. The model manages skills
|
|
5712
|
+
Service-provided skills are not installer targets; removal follows {§skills-remove}.
|
|
5713
|
+
|
|
5714
|
+
§skills-remove **`remove` forgets the workspace binding, not the source.**
|
|
5715
|
+
It withdraws that definition and restores any inherited definition and enabledness
|
|
5716
|
+
({§functionality-coordinator}). External folders are never deleted. Fetched
|
|
5717
|
+
materializations remain workspace-owned operational state, reusable only for the
|
|
5718
|
+
same complete definition; they confer no availability without a definition.
|
|
5719
|
+
Inherited definitions cannot be removed here; their enabledness can be overridden.
|
|
5720
|
+
|
|
5721
|
+
§skills-hotload **Skills placed out of band are admitted at the next turn** ({§functionality-hotload}). The
|
|
5722
|
+
family keeps one signature of the discovered roots, configured definitions, source locations, and `SKILL.md` sources
|
|
5723
|
+
per resident workspace, read before a publication loads the skills it describes. Turn admission
|
|
5724
|
+
recomputes it under the workspace gate before packet assembly: a changed signature republishes the
|
|
5725
|
+
family, and an unchanged one republishes only when the skills the coordinator would publish differ
|
|
5726
|
+
from the published ones. A skill installed, edited or removed by any other tool is therefore
|
|
5727
|
+
discoverable in the first subsequent model turn, while an unchanged set dispatches nothing. The model manages skills
|
|
5420
5728
|
only through the generated ```` ```skills ```` family
|
|
5421
5729
|
({§functionality-model-projection}); it is never taught a package manager.
|
|
5422
5730
|
|
|
@@ -5875,6 +6183,7 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5875
6183
|
|---|---:|---|
|
|
5876
6184
|
| `service-starting` | 503 | The PLURNK service owns this listener but has not admitted its client interface yet. |
|
|
5877
6185
|
| `configuration-unsupported` | 400 | Environment discovery reads this installation's declared configuration; client configuration contributes nothing. |
|
|
6186
|
+
| `configuration-invalid` | 503 | The owning configuration reader's diagnostic, naming the invalid key. Recovery: Correct the named configuration input. Other capabilities remain available. |
|
|
5878
6187
|
| `name-reserved` | 400 | '*alias*' is plurnk's own: PLURNK_* configuration and provider credential names never reach a subprocess. |
|
|
5879
6188
|
| `value-invalid` | 400 | '*alias*' needs a string value. |
|
|
5880
6189
|
| `env-invalid` | 400 | `env` must be an object of string values; '*name*' is not a name a shell can export. |
|
|
@@ -5882,14 +6191,19 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5882
6191
|
| `headless` | 409 | The workspace has no project root, so there are no file members. Recovery: Open the workspace on a project root. |
|
|
5883
6192
|
| `definition-invalid` | 400 | A members definition is { glob }: a gitignore-style pattern, `!glob` to exclude; a skill definition names an installable skill. |
|
|
5884
6193
|
| `model-scope` | 403 | The model may not change membership here: the members scope is none. Recovery: `git add` the file so git tracks it, or ask the operator to add it (/members add) or raise PLURNK_SERVICE_MEMBERS_MODEL_SCOPE. |
|
|
5885
|
-
| `
|
|
5886
|
-
| `
|
|
5887
|
-
| `
|
|
5888
|
-
| `
|
|
6194
|
+
| `query-unsupported` | 400 | Agent Skills have no standard registry to search; discover takes a source: a git remote as a full https or ssh URL, a folder, a lone SKILL.md, or a zip or tar archive. |
|
|
6195
|
+
| `source-invalid` | 400 | '*source*' is not a valid git remote URL; an https source carries no credentials (git's credential helper supplies them); is not a source: a git remote is a full https or ssh URL; is relative, and this workspace has no project root to resolve it against; or is neither a folder, a SKILL.md, nor a zip or tar archive. |
|
|
6196
|
+
| `source-missing` | 404 | No folder or file is at '*source*' (; owner/repo shorthand names no forge, so give the repository's full https or ssh URL). |
|
|
6197
|
+
| `source-unreadable` | 422 | '*source*' cannot be read: *cause*; or '*path*' could not be unpacked: *reason*. |
|
|
6198
|
+
| `source-unreachable` | 502 | git could not reach '*remote*', or fetch it (at '*ref*'): *reason*. |
|
|
6199
|
+
| `ref-missing` | 404 | '*remote*' has no branch or tag '*ref*', or names no default branch. |
|
|
6200
|
+
| `source-moved` | 409 | '*source*' *ref* now names *current*; this skill was added at *commit*. Recovery: Remove the skill and add it again to take the current commit. |
|
|
6201
|
+
| `source-is-plugin` | 422 | '*source*' is an Agent Plugin; install it as a plugin, so its skills keep the plugin's identity and servers. |
|
|
6202
|
+
| `source-unsafe` | 422 | '*path*' links outside its skill, or is neither a file, a directory, nor an inward link. |
|
|
6203
|
+
| `skill-not-found` | 404 | '*source*' carries no Agent Skill named '*name*'. |
|
|
6204
|
+
| `skill-ambiguous` | 409 | '*source*' carries *count* skills named '*name*'. |
|
|
5889
6205
|
| `alias-mismatch` | 400 | Alias '*alias*' must equal the skill name '*name*'. |
|
|
5890
|
-
| `
|
|
5891
|
-
| `source-required` | 400 | Adding '*alias*' requires the standard installer source that provides it. |
|
|
5892
|
-
| `uninstall-failed` | 502 | Agent Skill '*name*' could not be removed from its *scope* root: *cause*. |
|
|
6206
|
+
| `source-required` | 400 | Adding '*alias*' requires the source that provides it. |
|
|
5893
6207
|
| `workspace-not-found` | 404 | Workspace *id* does not exist. |
|
|
5894
6208
|
| `state-not-json` | 400 | Worker module state is not JSON-serializable. |
|
|
5895
6209
|
| `workspace-busy` | 409 | Workspace *id* is running an operation or another capability change. Recovery: Settle the current operation and retry the capability change. |
|
|
@@ -5902,9 +6216,8 @@ Every Problem code core mints is named here under its family ({§problem-error-c
|
|
|
5902
6216
|
| `loop-policy-invalid` | 400 | An unattended loop cannot hold a proposal for review: nobody is present to answer. Recovery: State proposals accept or reject, or attend the loop. |
|
|
5903
6217
|
| `scope-cancelled` | 499 | The worker scope was cancelled: *reason*. |
|
|
5904
6218
|
| `range-not-satisfiable` | 416 | `Range <0,-1>` starts at 0, which is not a line; lines are numbered from 1. Recovery: Write `<1,-1>` to trim every line of the body; `KILL (log:///…/READ)` with no scope retires the item. |
|
|
5905
|
-
| `
|
|
5906
|
-
| `
|
|
5907
|
-
| `skill-missing` | 404 | Agent Skill '*alias*' is not installed under its *scope* root, or is not provided by this service. |
|
|
6219
|
+
| `install-failed` | 500 | Agent Skill '*name*' could not be placed under *root*: *cause*. |
|
|
6220
|
+
| `skill-missing` | 404 | Agent Skill '*alias*' is not provided by this service. |
|
|
5908
6221
|
| `skill-invalid` | 422 | Agent Skill '*alias*' is not a valid standard skill: *cause*. |
|
|
5909
6222
|
|
|
5910
6223
|
§pinned-wording-core **Pinned wording.** Verbatim sentences tests pin: each is contract, and a change here is a change of contract.
|