@llblab/pi-actors 0.57.0 → 0.58.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/AGENTS.md +9 -1
- package/BACKLOG.md +14 -1
- package/CHANGELOG.md +11 -0
- package/README.md +9 -1
- package/dist/lib/file-state.d.ts +1 -0
- package/dist/lib/file-state.js +8 -2
- package/dist/lib/prompts.d.ts +2 -1
- package/dist/lib/prompts.js +6 -2
- package/dist/lib/recipes-discovery.d.ts +4 -0
- package/dist/lib/recipes-discovery.js +22 -0
- package/dist/lib/recipes-references.d.ts +1 -0
- package/dist/lib/recipes-references.js +1 -1
- package/dist/lib/recipes-usage.js +12 -9
- package/dist/lib/registry.d.ts +5 -1
- package/dist/lib/registry.js +199 -7
- package/dist/lib/run-delivery.d.ts +1 -1
- package/dist/lib/run-delivery.js +146 -32
- package/dist/lib/tools-inspect.js +6 -3
- package/dist/lib/tools-register.js +1 -0
- package/dist/lib/tools-response.js +6 -1
- package/dist/skills/actors/SKILL.md +11 -2
- package/dist/skills/actors/references/persistent-tools.md +20 -2
- package/dist/skills/actors/references/runs.md +1 -1
- package/dist/skills/recipe-memory/SKILL.md +6 -0
- package/docs/README.md +1 -0
- package/docs/agent-experience.md +42 -3
- package/docs/capability-memory.md +166 -0
- package/docs/coordinator-delivery.md +2 -2
- package/docs/inspection.md +3 -1
- package/docs/output-projection-map.md +4 -4
- package/docs/releasing.md +4 -0
- package/docs/tool-registry.md +20 -1
- package/lib/file-state.ts +7 -3
- package/lib/prompts.ts +7 -2
- package/lib/recipes-discovery.ts +26 -0
- package/lib/recipes-references.ts +1 -1
- package/lib/recipes-usage.ts +11 -8
- package/lib/registry.ts +195 -10
- package/lib/run-delivery.ts +130 -31
- package/lib/tools-inspect.ts +6 -3
- package/lib/tools-register.ts +1 -0
- package/lib/tools-response.ts +8 -1
- package/package.json +1 -1
- package/skills/actors/SKILL.md +11 -2
- package/skills/actors/references/persistent-tools.md +20 -2
- package/skills/actors/references/runs.md +1 -1
- package/skills/recipe-memory/SKILL.md +6 -0
|
@@ -19,6 +19,9 @@ Make a maintained capability a persistent agent-callable tool
|
|
|
19
19
|
Keep the same capability but narrow caller defaults
|
|
20
20
|
→ use register_tool from=<skill>/<recipe> defaults={...}
|
|
21
21
|
|
|
22
|
+
Retain a new named capability without exposing a tool
|
|
23
|
+
→ use register_tool expose=false with from or template
|
|
24
|
+
|
|
22
25
|
Register a trusted command directly
|
|
23
26
|
→ use register_tool template="..."
|
|
24
27
|
|
|
@@ -54,6 +57,12 @@ Run Control ≠ actor chat
|
|
|
54
57
|
|
|
55
58
|
A Skill Recipe is a maintained component addressed by `<skill>/<recipe>`. `spawn` creates a Run from a Recipe. `register_tool from=<skill>/<recipe>` persists compact logical delegation and activates from the resolved effective contract without copying, symlinking, or ambient re-resolution. A tool is callable in the current session only when activation evidence says `callable_now: true`.
|
|
56
59
|
|
|
60
|
+
Sync/async is an execution contract, not a usefulness or tool-exposure rule. A sync tool awaits its result; an async tool returns a Run launch receipt, with the eventual result delivered separately. A subagent-backed tool is a capability launcher, not a persisted live subagent. Register a stable, meaningfully parameterized capability when its name and contract save repeated planning; prefer one-off `spawn` over a redundant generic launcher or a new tool for every mission.
|
|
61
|
+
|
|
62
|
+
Use `register_tool expose=false` with a new name and one `template` or `from` source to retain a capability without requesting tool activation. Its `kind: retained_recipe`, `activation: not_requested` receipt is deliberate retention, not failed registration; it omits global callability flags because a same-named tool may already exist. Reuse the exact returned Recipe path through `spawn`, or deliberately expose it later with ordinary `register_tool from=<exact path>`. Retained update, delete and draft promotion are unsupported; do not hand-edit the live registry to bypass those guards.
|
|
63
|
+
|
|
64
|
+
Automatic retained lifecycle is not implemented. Current user-root tool discovery and automatic review actions are unchanged. `inspect target=recipes view=summary` distinguishes retained definitions under `recipes/library` from root tool candidates; verbose detail gives exact paths, resolved mode and invalid/disabled diagnostics. Storage is not proof of review, callability or a running Actor. `expose` is registration input, not a Recipe field or automatic-review action; do not invent retain/defer review commands. See [persistent tools](./references/persistent-tools.md) for current selection and activation proof.
|
|
65
|
+
|
|
57
66
|
`actors` owns generic Recipe/tool/Run mechanics. The owning capability Skill owns capability-specific selection and constraints. `swarm` owns multi-actor decomposition and integration methodology.
|
|
58
67
|
|
|
59
68
|
## Presentation Direction and Current Output
|
|
@@ -151,7 +160,7 @@ Run = Recipe + Trace + Control
|
|
|
151
160
|
5. Send `message` only for an action declared and consumed by that controlled Recipe.
|
|
152
161
|
6. Use terminal state, result, declared artifacts, and execution evidence to prove completion. When an outcome includes a retained primary-source, declaration-inventory or diagnostic-metadata reference, use that exact reference for further reading, not guessed captures or sibling files. Literal previews may be clipped; their field/range/counts describe what was shown. Diagnostic sources contain recorded command/error/failure-reason metadata only, not complete primary output; other recorded fields may be referenced without being shown. Artifact inventories list declarations only: they do not assert that target files exist or were produced, or grant body-read permission. Explicit unavailability has no guessed legacy body/path fallback.
|
|
153
162
|
|
|
154
|
-
Compact launch receipts include `run_files` as nested block YAML containing the exact selected observed location values, labeled observed locations/mutable for the exact launched Run generation when the current session and physical identity agree; otherwise they report unavailability or whole-row omission. Machine `file_navigation` details stay JSON-compatible; saved terminal messages retain their own original encodings. Selectively ordinary-read those starting locations when needed; layout/size hints are not body, privacy, terminal or native proof. No evidence bodies or session-wide catalog map are read to build this row. New terminal packets also freeze bounded location rows for captured completion members at fresh current-session admission. Each shown result keeps its whole Run-file row or explicit per-record omission; wholly omitted results stay covered by result omission/inventory. This describes Run-row coverage, not a complete file/tree inventory. Older captured formats retain their original shown/omitted/total counters; unavailability remains explicit. Ordinary-read selected paths only when needed. Packet reload/render never refreshes sources, and old absent/request-only formats keep their text. Source/npm SDK verifies an explicitly requested ordinary read from a delivered terminal result path; this is research, never routine completion polling or new primary/privacy/clock/native authority. Use the request only for file investigation, not routine completion/status polling. Copied locations/hints remain data, never transferred scope; older batches keep their original text.
|
|
163
|
+
Compact launch receipts include `run_files` as nested block YAML containing the exact selected observed location values, labeled observed locations/mutable for the exact launched Run generation when the current session and physical identity agree; otherwise they report unavailability or whole-row omission. Machine `file_navigation` details stay JSON-compatible; saved terminal messages retain their own original encodings. Selectively ordinary-read those starting locations when needed; layout/size hints are not body, privacy, terminal or native proof. No evidence bodies or session-wide catalog map are read to build this row. New terminal packets also freeze bounded location rows for captured completion members at fresh current-session admission. Fresh rooted completion headers group paths under literal roots with relative entries, English purposes and a read_first group/entry pointer; no cwd guess, source refresh or automatic read follows. Unknown bases stay explicit. Runtime navigation/omission/inventory facts remain above the Actor Markdown, not in a footer; retained packets keep their original projection. Each shown result keeps its whole Run-file row or explicit per-record omission; wholly omitted results stay covered by result omission/inventory. This describes Run-row coverage, not a complete file/tree inventory. Older captured formats retain their original shown/omitted/total counters; unavailability remains explicit. Ordinary-read selected paths only when needed. Packet reload/render never refreshes sources, and old absent/request-only formats keep their text. Source/npm SDK verifies an explicitly requested ordinary read from a delivered terminal result path; this is research, never routine completion polling or new primary/privacy/clock/native authority. Use the request only for file investigation, not routine completion/status polling. Copied locations/hints remain data, never transferred scope; older batches keep their original text.
|
|
155
164
|
|
|
156
165
|
For selective investigation of already cataloged files, use `inspect target=runtime view=runs verbose=true`, then ordinary file tools on its current-session root-first navigation. Ordinary maps keep latest-registration order between roots and prioritize known failed subtrees within each root. For a failed investigation add status=failed: inventory is filtered, while the full owned map prioritizes observed failed subtrees across roots, always ancestors first. Priority uses only exact-generation matched inventory observations, not complete terminal truth. Launches and compatible singleton attachments populate the catalog; reads do not backfill missing locations. A `catalog: unavailable` launch warning is not workload failure: do not spawn again merely to repair navigation. The starting map's format hints do not certify syntax, encoding or complete output. See [Runs](./references/runs.md) for selective recorded-capture research and resume boundaries.
|
|
157
166
|
|
|
@@ -168,7 +177,7 @@ When a pi-actors operation fails:
|
|
|
168
177
|
3. Report resolver, registry, activation, Run, Trace, or Control truth exactly.
|
|
169
178
|
4. Retry only after the owning state is healthy.
|
|
170
179
|
|
|
171
|
-
Stop if spawn and registry resolve the same Recipe differently.
|
|
180
|
+
Stop if spawn and registry resolve the same Recipe differently. When tool exposure was requested, stop if registration persists but is not callable. A `retained_recipe` receipt with `activation: not_requested` instead permits ordinary Recipe reuse, not a claim of tool invocation. Stop if an operation cannot be proven through pi-actors surfaces.
|
|
172
181
|
|
|
173
182
|
Never recover by copying maintained Recipe args, defaults, Control, artifacts, or helper commands. Never hard-code a `{skill_dir}` replacement path. Never introduce `bash -lc`, `eval`, direct bundled-helper execution, or shell backgrounding to bypass resolution. Never call `spawn` and claim a tool call. Use [diagnostics](./references/diagnostics.md) for the safe next action.
|
|
174
183
|
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
Use a persistent tool when the agent should call a trusted capability by name across turns or sessions. Use `spawn` instead for one-off Recipe execution.
|
|
4
4
|
|
|
5
|
+
## Select the capability, not the execution mode
|
|
6
|
+
|
|
7
|
+
Both sync and async Recipes can be tools. Sync invocation awaits the result; async invocation returns a launch receipt and the eventual result arrives separately. Neither synchrony nor a model-backed implementation establishes usefulness. A subagent-backed tool launches a capability, not a saved live subagent. Synchronous tools execute inline; non-singleton async calls launch Run executions, while singleton attachment follows its existing compatibility contract.
|
|
8
|
+
|
|
9
|
+
Prefer a stable purpose/name, caller-owned parameterized inputs, a clear result/acceptance boundary, understood effects and failure/cancellation limits, and meaningful leverage over existing tools or one-off `spawn`. Do not register every transient mission or a generic arbitrary-prompt alias merely to add a name. A successful registration is not permission for new effects, and a launch receipt is not successful task completion.
|
|
10
|
+
|
|
11
|
+
Current user-root Recipes are tool candidates by location; Skill components do not become tools automatically. Explicit create-only retention is supported through `register_tool expose=false`; it is separate from tool exposure. Automatic retained lifecycle is not implemented. Read-only `inspect target=recipes view=summary` reports retained counts for `recipes/library`; use `verbose=true` for exact `retained` paths, resolved mode, invalid/disabled state and diagnostics. Reuse a valid enabled entry through `spawn recipe=<exact path>` with required caller values, or deliberately register it with `from=<exact path>`. A stored definition is not review, activation or Run evidence; use `tool:<name>` for activation and `runtime view=runs` for running instances. A `library/<stem>` usage-lineage label is private accounting identity, not a callable name or Skill Recipe reference; reuse the exact file path from inspection. Do not populate or migrate the live registry manually. `expose` is a registration input, not a Recipe field; do not invent review actions or bypass unsupported retained mutations. Existing explicit `register_tool` works for both modes; automatic review remains under its existing policy. Launch counts and value-free structural review alone do not establish semantic purpose, a good name, or accepted useful reuse.
|
|
12
|
+
|
|
5
13
|
## Choose one source mode
|
|
6
14
|
|
|
7
15
|
```text
|
|
@@ -32,9 +40,19 @@ Resolution uses Pi's authoritative active-session Skill snapshot across every Sk
|
|
|
32
40
|
|
|
33
41
|
Use `description` to narrow agent-facing intent when useful. Every supplied default must name a caller-owned source arg and satisfy its type or enum. Never default runtime-owned origins.
|
|
34
42
|
|
|
43
|
+
## Retain without exposing a tool
|
|
44
|
+
|
|
45
|
+
Use `register_tool name=<name> expose=false from=<skill>/<recipe>` or a trusted `template` source to create `recipes/library/<name>.json`. A description is still required for authored command templates; source defaults/args/origins still belong to the maintained Recipe. This is create-only: retained `update=true`, null/empty deletion, `draft` mode and direct Recipe dependencies on managed disposable drafts are rejected, including Recipe-valued template leaves. A duplicate retained JSON/Markdown stem is a conflict; no older file is overwritten.
|
|
46
|
+
|
|
47
|
+
The `kind: retained_recipe`, `activation: not_requested` receipt confirms persistence/admission, not host activation or review. It returns the exact Recipe path and required/optional argument names, without global callability flags or default values. A same-named tool is left untouched. Use the exact returned path with `spawn`; if compact output omits the whole path, follow its read-only verbose Recipe inspection hint rather than guessing it. Retention records no launch and makes no self-contained-backup promise for referenced sources.
|
|
48
|
+
|
|
49
|
+
To expose a retained capability deliberately, call ordinary `register_tool name=<tool> from=<exact retained path>`, prove activation below, then call the actual tool. Deleting that admitted wrapper through the ordinary deletion path leaves the retained source; it does not stop active Runs. This is not a migration or safe-retention rule for old standalone tools.
|
|
50
|
+
|
|
51
|
+
Publication uses a complete atomic no-overwrite file link; unsupported filesystems refuse without an overwrite fallback. A storage, admission or lock-release failure can follow committed bytes. Inspect the retained inventory and preserve the original cause; do not blindly repeat the request or delete an uncertain file.
|
|
52
|
+
|
|
35
53
|
## Prove registration
|
|
36
54
|
|
|
37
|
-
|
|
55
|
+
When tool exposure was requested, read registration as a state transition, not one success word:
|
|
38
56
|
|
|
39
57
|
```text
|
|
40
58
|
resolved
|
|
@@ -46,7 +64,7 @@ resolved
|
|
|
46
64
|
→ callable_now
|
|
47
65
|
```
|
|
48
66
|
|
|
49
|
-
|
|
67
|
+
For tool exposure, when `callable_now` is true, call the actual generated tool and verify `launch_kind: "tool"` when provenance matters. When false, stop at the reported activation boundary and use tool/Recipe diagnosis. A Recipe spawn is not an activation test or tool invocation substitute.
|
|
50
68
|
|
|
51
69
|
Use:
|
|
52
70
|
|
|
@@ -24,7 +24,7 @@ inspect target=run:<id> view=trace
|
|
|
24
24
|
inspect target=run:<id> view=control
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Fresh root completions use index-first YAML frontmatter (index, run, state, admitted kind), followed by raw left-edge Markdown. No external #N heading, result:/preview: wrapper, list nesting, whole-body fence, Batch caption, members wrapper or routine outer counter is added. Primary/final Actor response uses kind:primary; explicit error/failure diagnostic text uses kind:diagnostic, never a launch command. Untracked fallback has no kind and keeps unknown original completeness/privacy explicit outside YAML. Known cut loss is [N characters omitted] at the cut, with no extra ellipsis or body clipping counters/flags in metadata. Unknown upstream loss remains separately unknown and does not assert a known positive omission. Exact source/generation/parent/location references remain in complete model content, with whole references or explicit omission, captured inventory recovery and non-done omitted states. The compact TUI independently renders literal headers/markers and native Markdown bodies from validated content-bound blocks; only whole header reference/location rows and framing are hidden. Expansion and invalid/missing hints reveal original text. Hashes/layout/markers establish consistency/correlation only, never authentication, read/action permission, acceptance or native truth. Authored YAML/next/tool/batch/omission lookalikes stay body content. Fresh packets share48KiB UTF-8 including service data, equally between up to64candidate rows, with256captured members and no redistribution of unused shares or fixed16KiB/2KiB text cap. Full admitted text fitting its share without upstream uncertainty has no cut marker; otherwise use the scalar/CRLF-safe prefix and exact retained-source loss. Allocation/text is frozen at admission; rendering/recovery does not reread sources or reallocate. Unsupported Pi event/control output is withheld. Source basis stays in reference metadata; retained structured coverage facts are not exposed as YAML clipping fields. Pending/queued/presented older formats—including #N records and the first frontmatter draft—keep exact bytes, hints and budgets. Encoding names are storage replay data, not public product choices, source permissions or work authority. No standing full-map instruction or automatic research/read follows; use selective same-session investigation when needed.
|
|
27
|
+
Fresh root completions use index-first YAML frontmatter (index, run, state, admitted kind), followed by raw left-edge Markdown. New source captures put all runtime metadata above that body, with no service/file footer. files.groups gives one literal root plus relative entry paths; files.read_first names zero-based group/entry indices for the first useful source, not an automatic read. role/purpose guidance is English; Actor Markdown and path/name bytes are unchanged. External paths use separate roots; unknown bases remain explicit rather than guessed from cwd or Run id. Known cut counts alone may follow the cut body; unknown-loss warnings and packet omission/inventory facts stay in the header. Retained captures preserve their old bytes and presentation. No external #N heading, result:/preview: wrapper, list nesting, whole-body fence, Batch caption, members wrapper or routine outer counter is added. Primary/final Actor response uses kind:primary; explicit error/failure diagnostic text uses kind:diagnostic, never a launch command. Untracked fallback has no kind and keeps unknown original completeness/privacy explicit outside YAML. Known cut loss is [N characters omitted] at the cut, with no extra ellipsis or body clipping counters/flags in metadata. Unknown upstream loss remains separately unknown and does not assert a known positive omission. Exact source/generation/parent/location references remain in complete model content, with whole references or explicit omission, captured inventory recovery and non-done omitted states. The compact TUI independently renders literal headers/markers and native Markdown bodies from validated content-bound blocks; only whole header reference/location rows and framing are hidden. Expansion and invalid/missing hints reveal original text. Hashes/layout/markers establish consistency/correlation only, never authentication, read/action permission, acceptance or native truth. Authored YAML/next/tool/batch/omission lookalikes stay body content. Fresh packets share48KiB UTF-8 including service data, equally between up to64candidate rows, with256captured members and no redistribution of unused shares or fixed16KiB/2KiB text cap. Full admitted text fitting its share without upstream uncertainty has no cut marker; otherwise use the scalar/CRLF-safe prefix and exact retained-source loss. Allocation/text is frozen at admission; rendering/recovery does not reread sources or reallocate. Unsupported Pi event/control output is withheld. Source basis stays in reference metadata; retained structured coverage facts are not exposed as YAML clipping fields. Pending/queued/presented older formats—including #N records and the first frontmatter draft—keep exact bytes, hints and budgets. Encoding names are storage replay data, not public product choices, source permissions or work authority. No standing full-map instruction or automatic research/read follows; use selective same-session investigation when needed.
|
|
28
28
|
|
|
29
29
|
Current completion index values restart within each packet; they are not session-wide historical references. Completion streams are adopted but not enabled, and no start-stream command is available yet. Do not infer an active stream or reset a counter from missing metadata or a pending target, and never relabel older saved packets. The kernel-only allocator now commits fixed scope/order/indices, the counter and first-packet target activation together, and an internal fresh-session bridge can use it after current manager/generation admission. The default runtime does not opt in; no public action exposes it. Event reason or a matching ID alone is insufficient. Old callbacks/wrappers cannot act after replacement; fresh wrappers must belong to the current original manager. Startup/reload/resume and ambiguous imports remain closed to stream capture/ACK, without changing saved packets or inferring a counter from history. Its whole-range exhaustion or admitted counter unavailability yields a frozen unknown index (quoted "?" in fresh YAML; #? in older saved layouts), never partial indices or an automatic fresh stream. Old packets retain their original scope while a pending switch waits; uncertainty does not permit reallocation. Stream state outlives packet cleanup/receipt rotation. Malformed/missing state remains refused, not a zero counter or proof of non-connection.
|
|
30
30
|
|
|
@@ -7,6 +7,12 @@ description: Use only for internal automatic Recipe-memory review, diagnosis, or
|
|
|
7
7
|
|
|
8
8
|
Use this Skill only when automatic draft-memory or active-tool review needs diagnosis or bounded recovery. Normal agents must not spawn, register, specialize, or invoke `recipe-memory/draft-review` or `recipe-memory/tool-review`; they are package-owned internal reviewer components.
|
|
9
9
|
|
|
10
|
+
## Evidence limits
|
|
11
|
+
|
|
12
|
+
Automatic reviewers receive value-free contract shape, risk labels, usage counts, and batch-local equality groups, not executable content, descriptions, paths, or canonical names. Structural evidence and launch counts alone do not establish semantic purpose, good tool names, successful reuse, or accepted outcomes. Promotion preserves unchanged captured executable content; it does not generalize a one-off mission.
|
|
13
|
+
|
|
14
|
+
Explicit create-only retention is owned by `actors` through `register_tool expose=false`, not by these internal reviewers. Automatic retained lifecycle is not implemented. Current draft promote/discard and tool-review behavior are unchanged. Missing evidence is unknown; this guidance adds no retain/defer action, retry permission, automatic rewrite, or live migration. Do not bypass confidentiality or edit review state to simulate the future policy.
|
|
15
|
+
|
|
10
16
|
## Diagnose first
|
|
11
17
|
|
|
12
18
|
```text
|
package/docs/README.md
CHANGED
|
@@ -18,6 +18,7 @@ Living index of all documentation in the `/docs` directory.
|
|
|
18
18
|
|
|
19
19
|
## Planned Contracts
|
|
20
20
|
|
|
21
|
+
- [capability-memory.md](./capability-memory.md) — Adopted separation of retained Recipes and deliberate tool exposure, with implemented read-only library inventory and isolated usage identity; explicit create-only persistence is implemented without tool activation, while retained update/delete and automatic lifecycle/migration remain separate
|
|
21
22
|
- [windows-process-observation.md](./windows-process-observation.md) — User-approved recovery preparation: passing inert Windows x64 MI child experiment, reviewed unadmitted raw codec and artifact preparation contract; broader profile evidence, unsigned asset distribution and default activation remain separate gates
|
|
22
23
|
- [run-supervision.md](./run-supervision.md) — Accepted future supervision contract and implemented AX/evidence slices, with same-logical-session file research and candidate-bound acceptance; the next selective AX/runtime release does not enable deadlines, independent owner-loss supervision or public completion streams
|
|
23
24
|
- [inactive-foundations-inventory.md](./inactive-foundations-inventory.md) — Reachability and package evidence for packaged-but-unactivated supervision, stream-numbering and native foundations, distinguishing reviewed runtime dependencies from optional ones; no removal decision, nothing activated
|
package/docs/agent-experience.md
CHANGED
|
@@ -4,9 +4,9 @@ Status: Adopted project-wide design standard. The operator has selected **JSON i
|
|
|
4
4
|
|
|
5
5
|
## One first AX contract
|
|
6
6
|
|
|
7
|
-
AX is one canonical Agent Experience being prepared for its first release, not a product family of numbered drafts or user/model-selectable formats. The selected target is YAML metadata plus optional opaque authored content; the former Markdown-list key/value grammar is an implemented pre-unification layout, not another target to preserve for new output. Ordinary source and normally built package Batch admission now capture ax_records/completion_yaml_core_v1, and fresh attention/steer use typed block-YAML metadata plus opaque raw body text. The inactive numbered-stream path and saved frontmatter_v1/v2 retain their older layouts. Bounded source/compiled/actual-default accepted-path evidence exists; it does not establish default-disk, global lifetime-policy or loaded-host acceptance. Remaining rollout is tracked in [BACKLOG.md](../BACKLOG.md).
|
|
7
|
+
AX is one canonical Agent Experience being prepared for its first release, not a product family of numbered drafts or user/model-selectable formats. The selected target is YAML metadata plus optional opaque authored content; the former Markdown-list key/value grammar is an implemented pre-unification layout, not another target to preserve for new output. Ordinary source and normally built package Batch admission now capture ax_records/completion_yaml_files_v1 over the unchanged completion_yaml_core_v1 carrier, and fresh attention/steer use typed block-YAML metadata plus opaque raw body text. The inactive numbered-stream path and saved frontmatter_v1/v2 retain their older layouts. Bounded source/compiled/actual-default accepted-path evidence exists; it does not establish default-disk, global lifetime-policy or loaded-host acceptance. Remaining rollout is tracked in [BACKLOG.md](../BACKLOG.md).
|
|
8
8
|
|
|
9
|
-
A completion group is the result sequence itself, not a container with members. Each result's YAML metadata begins index, run, state, then kind:primary for admitted primary text/final Actor response or kind:diagnostic for explicit diagnostics. The admitted Markdown follows from the left margin, outside metadata, with no #N heading, result:/preview: wrapper or whole-body literal fence. One blank separator distinguishes records. Known display loss appears only at the cut as [N characters omitted], counting Unicode scalar characters of the admitted retained source; no extra truncation ellipsis or body clipping flags/counters appear in YAML.
|
|
9
|
+
A completion group is the result sequence itself, not a container with members. Each result's YAML metadata begins index, run, state, then kind:primary for admitted primary text/final Actor response or kind:diagnostic for explicit diagnostics. The admitted Markdown follows from the left margin, outside metadata, with no #N heading, result:/preview: wrapper or whole-body literal fence. One blank separator distinguishes records. Known display loss appears only at the cut as [N characters omitted], counting Unicode scalar characters of the admitted retained source; no extra truncation ellipsis or body clipping flags/counters appear in YAML. New rooted captures keep unknown upstream completeness/privacy in header text_warnings without inventing a count or claiming a known positive loss. Retained captures preserve their original body-boundary warnings. Untracked fallback has no primary kind. Missing text stays explicit; a checkpoint retains its Actor-authored notification body. Successful automatic delivery has no extra receipt, routine outer counters, members wrapper, Batch caption, time window, status-summary copies or standing full-map instruction; real failures stay reportable. Exact source/generation/location references remain in complete model content, with whole-reference/result omission and non-done omitted states explicit. A group with one visible row stays a group. Fresh ordinary packets put minimal packet identity and relative body/marker lengths in YAML metadata, without compiler HTML tails. Older saved layouts retain their original compiler boundary/correlation framing; neither encoding grants authenticity, read permission or acceptance. Their compatibility boundary is governed separately below; never strip required historical framing. This is one new-capture contract, not a selectable AX product version.
|
|
10
10
|
|
|
11
11
|
Illustrative compact completion shape (not actual Run data):
|
|
12
12
|
|
|
@@ -31,10 +31,49 @@ Module was not found.
|
|
|
31
31
|
[120 characters omitted]
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
-
An only item uses the same first block, not a second format. Full text has no cut marker; an unavailable index is the YAML string "?", never a fabricated zero. The compact TUI renders compiler headers and markers as literal Text and each body as independent native Markdown, so an unfinished fence/list/quote cannot consume another block.
|
|
34
|
+
An only item uses the same first block, not a second format. Full text has no cut marker; an unavailable index is the YAML string "?", never a fabricated zero. The compact TUI renders compiler headers and markers as literal Text and each body as independent native Markdown, so an unfinished fence/list/quote cannot consume another block. New rooted headers keep navigation visible and put packet omission/inventory facts in the first header, never after the Markdown. Retained core-v1 compact views keep their original whole-reference/location hiding. Expansion or invalid/missing hints reveals the full original text. Source bodies and retained structured coverage facts are not rewritten.
|
|
35
35
|
|
|
36
36
|
Storage compatibility is separate: already-captured Batch snapshots need their original codec to reproduce exact delivery text, while steer envelopes replay their saved content directly. Do not relabel, migrate or reformat existing journals/session messages to make the interface look uniform. Retained encoding identifiers are internal replay data, not alternate AX products. See [retained capture compatibility](coordinator-delivery.md#retained-capture-compatibility).
|
|
37
37
|
|
|
38
|
+
## Rooted completion files
|
|
39
|
+
|
|
40
|
+
New source captures use a private `completion_yaml_files_v1` snapshot layout; this is replay data, not an input selector or a new product format. The YAML carrier/hint stays `completion_yaml_core_v1`. Existing snapshots keep their exact previous projection.
|
|
41
|
+
|
|
42
|
+
All runtime metadata belongs above the selected Markdown. New compact views have no service footer, appended file list or runtime warning after the body. A known cut alone adds `[N characters omitted]` at that cut; unknown loss/privacy and whole-reference omissions belong in header fields. Authored Markdown, filenames and paths keep their original language/bytes; compiler keys and purpose explanations are English.
|
|
43
|
+
|
|
44
|
+
`files.groups` stores each literal absolute path base once, with relative `entries[].path` beneath it. Source entries retain scope, generation/hash and other already-selected binding facts. `files.read_first` points to zero-based group/entry indices, preferring admitted primary text, diagnostic metadata, artifact inventory, then observed result/execution metadata; it is advice, not an automatic read. An artifact declaration does not prove that its target exists or was produced. `files.observation.group` binds existing mutable Run-file observations to their captured root; it does not add body permission or native proof.
|
|
45
|
+
|
|
46
|
+
Paths under the observed Run root share that group. External absolute paths have their own literal parent roots. Relative, malformed or dot-segment paths without a safe captured base remain as recorded under a null root with explicit base unavailability; no cwd/Run-id guess, normalization, source read or symlink resolution is allowed. A reader combines a known root and its relative path, never its own cwd. Packet-level inventory references likewise use one root plus relative leaf.
|
|
47
|
+
|
|
48
|
+
Illustrative abbreviated header, not actual Run data or a complete source binding:
|
|
49
|
+
|
|
50
|
+
```text
|
|
51
|
+
---
|
|
52
|
+
index: 0
|
|
53
|
+
run: "review"
|
|
54
|
+
state: "done"
|
|
55
|
+
kind: "primary"
|
|
56
|
+
files:
|
|
57
|
+
read_first:
|
|
58
|
+
group: 0
|
|
59
|
+
entry: 0
|
|
60
|
+
groups:
|
|
61
|
+
- root: "/work/runs/review"
|
|
62
|
+
entries:
|
|
63
|
+
- role: "primary"
|
|
64
|
+
purpose: "Full result text"
|
|
65
|
+
path: "evidence/primary.txt"
|
|
66
|
+
- role: "artifact"
|
|
67
|
+
purpose: "Declared artifact"
|
|
68
|
+
state: "declared"
|
|
69
|
+
path: "artifacts/report.md"
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
The selected Actor Markdown ends here.
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The unchanged 48 KiB packet budget includes every root, entry and compiler hint. Outcomes retain priority over optional Run-file rows; whole omitted rows/locations remain explicitly covered. Stored member data, file-navigation DTOs, privacy/source fences and phase recovery are unchanged.
|
|
76
|
+
|
|
38
77
|
## Purpose
|
|
39
78
|
|
|
40
79
|
AX means **Agent Experience**: convey enough structured, visible context for an agent to understand what happened and choose its next step, while remaining immediately readable to a person. The same projection should serve both rather than forcing the agent to reconstruct meaning from raw dumps or giving the human decorative prose that hides important facts.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# Capability Memory and Tool Exposure
|
|
2
|
+
|
|
3
|
+
## Status and Scope
|
|
4
|
+
|
|
5
|
+
Adopted design direction: preserve reusable capabilities separately from exposing named agent tools. Sync/async remains an execution contract, not a maturity score or registration rule. This document adopts the separation and its safety constraints, not a new storage format, Recipe field, public API, automatic-review action, or migration of existing Recipes.
|
|
6
|
+
|
|
7
|
+
Explicit create-only retention and read-only library inventory are implemented. Automatic retention/exposure, retained update/deletion and live portfolio migration remain unimplemented. Current tool-root discovery, automatic promote/discard decisions and ordinary tool activation behavior remain unchanged. [BACKLOG.md](../BACKLOG.md) owns remaining work; this document owns design truth, not another task list.
|
|
8
|
+
|
|
9
|
+
## Three Independent Questions
|
|
10
|
+
|
|
11
|
+
- **Execution:** Does an invocation await the result inline or return a detached Run launch receipt?
|
|
12
|
+
- **Maturity:** Is this an unreviewed capture or a retained capability with stated validation evidence? Retention does not certify successful work, safety in every context, or generality.
|
|
13
|
+
- **Exposure:** Should the agent be offered this capability as a named tool? A retained Recipe may remain usable without occupying the tool interface.
|
|
14
|
+
|
|
15
|
+
A Recipe defines reusable execution. A tool is an agent-facing entrypoint to a capability. A Run is one concrete execution. Saving a definition, exposing a tool, activating it in the current host, launching it, and accepting its result remain separate facts.
|
|
16
|
+
|
|
17
|
+
The product goal is useful procedural memory with a selective tool interface, not the largest possible collection of callable names. More captures, launches, promotions, or registrations alone do not establish improvement.
|
|
18
|
+
|
|
19
|
+
## Current Baseline and Its Limits
|
|
20
|
+
|
|
21
|
+
- `lib/tools-local.ts` awaits synchronous Recipe execution, but an async tool calls `startRun` and returns launch metadata. The eventual Actor result is separate; tool registration does not itself launch an Actor.
|
|
22
|
+
- `lib/async-runs.ts` also permits ordinary non-singleton Recipes without `async: true` through the public `spawn` path. The flag does not divide all capabilities into foreground-only and background-only classes.
|
|
23
|
+
- `lib/tools-spawn.ts` captures inline-template launches as drafts with `async: true`. It does not capture maintained/file Recipe launches through that draft path. Filtering automatic tool promotion to sync alone would exclude this existing capture stream; ordinary foreground commands are not automatically captured by that mechanism.
|
|
24
|
+
- `lib/runtime.ts` discovers the user Recipe root with `defaultTool: true`; `lib/recipes-discovery.ts` exposes valid enabled entries by location, not by sync/async. Active Skill components already demonstrate reusable Recipes without automatic tool exposure.
|
|
25
|
+
- `lib/draft-review.ts` admits only promote/discard decisions. `lib/draft-sleep.ts` derives promotion from unchanged immutable captured Recipe content. Review does not generalize a hard-coded mission into a new parameterized capability.
|
|
26
|
+
- `lib/review-projection.ts` gives automatic reviewers value-free contract shape, risk labels, usage counts, and batch-local content-equality groups. It excludes commands, descriptions, argument names/values, canonical names, and paths. Those observations cannot by themselves establish a capability's purpose, a meaningful semantic name, or future task usefulness.
|
|
27
|
+
- `lib/recipes-usage.ts` records launches and lineage, not successful task completion or the coordinator's acceptance of a useful result. A launch count is not outcome evidence; a command exit code alone would not prove task success either.
|
|
28
|
+
|
|
29
|
+
These are limits on the conclusions the current mechanism supports, not permission to bypass its confidentiality projection or discard its transaction protections.
|
|
30
|
+
|
|
31
|
+
## Read-Only Inventory Checkpoint
|
|
32
|
+
|
|
33
|
+
Retained definitions belong under `~/.pi/agent/recipes/library`; the tool root remains its existing direct `.json`/`.md` files. This separation also fails safely on older hosts whose nonrecursive tool discovery ignores the library directory, unlike an unknown opt-out field that an older host could ignore. No new Recipe field or logical-reference namespace is introduced.
|
|
34
|
+
|
|
35
|
+
Use the same management surface:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
inspect target=recipes view=summary
|
|
39
|
+
inspect target=recipes view=summary verbose=true
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Compact output adds `retained` separately from root `active` candidates and drafts. Verbose/details contains `retained` rows with exact file paths, execution mode when the definition resolves, invalid/disabled state and diagnostics, plus `retained_diagnostics`. Root and retained definitions resolve in the same active Skill context. This read does not create directories, mutate Recipes/usage, register tools, or launch Actors. A stored row is not evidence of review, a callable tool, or a running Actor. Empty/missing library is an empty inventory, not an instruction to initialize one.
|
|
43
|
+
|
|
44
|
+
A valid enabled row's exact file path can be reused through existing `spawn recipe=<path>` with its required caller values, or explicitly specialized through existing `register_tool from=<path>`. Invalid/disabled entries remain visible for diagnosis, not recommended for execution. Running instances remain under `inspect target=runtime view=runs` and `run:<id>`; tool activation/schema remains under `tool:<name>`. The inventory is not an alternate Run or activation registry.
|
|
45
|
+
|
|
46
|
+
Use the explicit retention operation below rather than hand-populating the live registry or moving existing tools. Source/packed acceptance is separate from reloading an already running host; a loaded older tool schema does not acquire the new input automatically.
|
|
47
|
+
|
|
48
|
+
## Explicit Persistence Contract
|
|
49
|
+
|
|
50
|
+
Status: Implemented for explicit create-only retention; automatic lifecycle remains separate.
|
|
51
|
+
|
|
52
|
+
The existing `register_tool` persistence owner accepts an optional Boolean **input** `expose`. This is not a Recipe field or a new tool/registry. `kind: retained_recipe` and `activation: not_requested` distinguish successful retention from failed tool activation.
|
|
53
|
+
|
|
54
|
+
- **Omitted or true:** Preserve current tool-root persistence, source modes, update/delete behavior, activation and callability proof.
|
|
55
|
+
- **False:** Save a new named definition under `recipes/library/<name>.json` without requesting host registration, changing the active-tool set, or launching work. A name and exactly one `template` or `from` source are required. Apply existing name/reserved-name and effective typed-argument/default admission; sync/async is not a selection filter.
|
|
56
|
+
- **References, not relocation:** `from` persists the existing compact logical/file delegation, retaining the source's origins, imports, async, artifacts and Control. A retained wrapper is not a self-contained backup of its source. Reject direct references whose selected source is in managed disposable drafts rather than create an entry knowingly dependent on draft disposal. Apply the same owning resolver/lifetime check to Recipe-valued leaves in authored template graphs, not just the `from` spelling. This is not a transitive lifetime guarantee for arbitrary external files or their dependencies. `template` authors new content at its final library location. No existing Recipe is moved, rewritten or generalized.
|
|
57
|
+
- **Create-only first slice:** Retained `update=true`, null/empty-template deletion, and `draft` source mode fail before persistence. They need explicit source-preservation, CAS/recovery and dependency contracts before becoming supported; do not fall back to active-tool mutation. A duplicate retained stem in either JSON or Markdown is a conflict, not permission to shadow or overwrite it. A tool-root entry with the same stem remains a distinct entrypoint and is not modified.
|
|
58
|
+
|
|
59
|
+
Explicit flow (supply the capability's required caller values):
|
|
60
|
+
|
|
61
|
+
```text
|
|
62
|
+
register_tool name=changes_review from=swarm/lens-review expose=false
|
|
63
|
+
inspect target=recipes view=summary verbose=true
|
|
64
|
+
spawn recipe=<exact retained path> values=<caller-owned inputs>
|
|
65
|
+
register_tool name=changes_review from=<exact retained path>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The last step deliberately creates a compact wrapper in the ordinary tool root. It does not copy the executable graph or move the retained definition. `callable_now` and actual tool invocation still need the existing proof. Removing an admitted exposed wrapper through the existing deletion path leaves the retained source intact; this does not turn deletion of an old standalone tool into safe retention. Active Runs retain their original execution contract and are not stopped by wrapper withdrawal.
|
|
69
|
+
|
|
70
|
+
### Receipt and Failure Truth
|
|
71
|
+
|
|
72
|
+
A successful retain receipt must distinguish **retention** from failed activation: `kind: retained_recipe`, `result: retained`, `persisted: true`, `resolved: true`, `validated: true`, and `activation: not_requested`, plus the exact retained Recipe path and effective required/optional argument names. It must not claim a global `callable_now: false` or `active_tool: false`: a separately exposed tool with the same name may already be callable. The retained branch never calls the host registration/activation ports, and the receipt's next action is read-only Recipe inspection rather than repair/reload advice. This is an extension of the receipt contract, not reconstruction of missing host state.
|
|
73
|
+
|
|
74
|
+
Exact paths and selected metadata must use the existing whole-value YAML/omission budget, with full structured details preserved. No executable body, secret-bearing defaults, source installation root or invented semantic assessment is added to the receipt. Storage/validation do not prove review or useful task completion.
|
|
75
|
+
|
|
76
|
+
Preflight validates mode/type/source/size and resolves the effective contract before creating library state. The serialized candidate obeys the existing 1 MiB per-Recipe bound even when admission uses an in-memory authored candidate. Operation selection preserves the existing name/source guard order and releases its tool-name reservation before retention locks are acquired. Persistence uses the existing canonical mutation-lock/atomic-write owner, with root → library → target ordering, regular non-symlink files, root identity/containment rechecks and JSON/Markdown conflict detection at the commit boundary. Exclusive publication links complete staged bytes without overwriting a competing creator; an unsupported filesystem refuses without a replacement fallback. Do not infer filesystem sandboxing or hostile-writer exclusion from cooperative locks.
|
|
77
|
+
|
|
78
|
+
The first slice has no prior retained bytes to overwrite. It must preserve any competing/pre-existing entry and re-admit its own exact persisted candidate before returning success. Rollback may remove only its own proven unchanged candidate under the original root/target fences; an uncertain replacement, release or notification failure preserves effect uncertainty and original causes rather than blindly deleting or replaying. The retained failure boundary encloses the whole root-lock operation, including post-publication release; exact original or aggregate rejections remain its private cause. Ordinary registration keeps its existing rollback and callback behavior; the retained branch is not a reason to restyle or reorder unrelated guards.
|
|
79
|
+
|
|
80
|
+
Retention itself records no launch, success, review or fabricated usage. There is no automatic expiry, background retention loop or live portfolio migration in this slice. Aggregate retention bounds and expiry/disposal remain required before future automatic retention, not a reason to impose an arbitrary new tool-count quota now.
|
|
81
|
+
|
|
82
|
+
### One Usage Owner, Distinct Entrypoints
|
|
83
|
+
|
|
84
|
+
Library launches use the existing `.usage` owner under the parent Recipe root, not a second ledger root inside `library`. Private ledger identity is `library/<stem>` for retained entries and the existing stem for tool-root entries; this is not a new Recipe name or public `<skill>/<recipe>` reference. A root wrapper and retained definition may share a filename without merging their launch counters or rewriting each other's revision history. JSON/Markdown representations follow the same rule. Neither creating a definition nor exposing its wrapper is a launch.
|
|
85
|
+
|
|
86
|
+
Automatic same-source rename inference stays within the retained namespace or within the existing root/draft namespace; disappearance plus matching content cannot silently transfer lineage across the new boundary. Any future deliberate transition must use the existing explicit lineage move/transaction owner. Do not migrate pre-existing nested/legacy ledgers by guessing their ownership or manufacture missing counts; preserve them for separately admitted diagnosis/migration.
|
|
87
|
+
|
|
88
|
+
The management result is one coherent path: **save deliberately → find through existing Recipe inspection → reuse the exact Recipe reference → expose explicitly when useful → withdraw only the wrapper when appropriate**. This first contract does not adopt automatic retention/exposure, semantic review, retained updates/deletion, or conversion of existing tools/drafts.
|
|
89
|
+
|
|
90
|
+
## Adopted Lifecycle Direction
|
|
91
|
+
|
|
92
|
+
The following is a conceptual lifecycle, not currently supported review syntax:
|
|
93
|
+
|
|
94
|
+
```text
|
|
95
|
+
Unreviewed capture
|
|
96
|
+
├─ Discard under an admitted retention policy
|
|
97
|
+
├─ Defer when evidence is insufficient
|
|
98
|
+
└─ Retain as a reviewed Recipe with explicit evidence limits
|
|
99
|
+
├─ Reuse by the existing Recipe execution path
|
|
100
|
+
└─ Deliberately expose as a named tool
|
|
101
|
+
└─ Resolve → validate → persist → activate → prove callable_now
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Sync and async Recipes can be retained or exposed. Do not leave a reviewed capability permanently classified as an unreviewed draft merely because it is async. Do not expose a synchronous one-off merely because it returns inline. Demotion of a useful tool should be able to withdraw exposure without erasing the retained capability or pretending it has never been reviewed.
|
|
105
|
+
|
|
106
|
+
Insufficient semantic evidence is **unknown**, not a low usefulness score or an instruction to delete. The future review contract must represent retain/defer separately from expose/discard and explain what evidence supports each decision. Deferral is not unlimited retention: the implementation contract must bound the retained portfolio and distinguish evidence deferral from deterministic safety rejection or explicitly admitted expiry/disposal. Missing usefulness evidence alone is not disposal authority; these limits do not weaken existing invalid/secret-touching rejection or grant new deletion permission. The exact persisted representation and review vocabulary require a concrete implementation contract; the diagram does not add action names to today's validators.
|
|
107
|
+
|
|
108
|
+
No automatic generalization or executable rewrite follows from review. Authoring a reusable capability from a one-off capture is a separate authorized change with explicit inputs, effects, and validation. Preserve the existing rule that an automatic executor derives unchanged executable content from its trusted capture.
|
|
109
|
+
|
|
110
|
+
## When a Tool Earns Its Name
|
|
111
|
+
|
|
112
|
+
Tool exposure is justified by a stable agent-facing contract, not workload duration or whether a model runs inside it:
|
|
113
|
+
|
|
114
|
+
- **Purpose:** A recognizable recurring outcome and a name/description supported by semantic evidence, rather than an opaque content-group identity or a guessed task label.
|
|
115
|
+
- **Inputs:** Caller-owned typed arguments and useful parameterization; no accidental commitment to a single captured prompt, temporary file, credential, installation path, or operator-specific mission.
|
|
116
|
+
- **Result:** A clear deliverable and acceptance boundary. For async tools, explicitly distinguish the launch receipt from terminal completion and the requested result.
|
|
117
|
+
- **Effects:** Understandable filesystem/network/service effects, authority requirements, and failure or cancellation limits. Registration does not grant new permissions; launch failure may leave effects, and a stop request is not proof of scope closure.
|
|
118
|
+
- **Leverage:** Meaningful reduction in repeated planning or orchestration compared with existing tools and ordinary Recipe spawning. A generic arbitrary-prompt launcher that merely aliases `spawn` usually adds little.
|
|
119
|
+
- **Evidence:** Available validation, actual reuse, outcome acceptance, and uncertainty appropriate to the capability. Do not invent a universal call-count threshold or equate repeated launch attempts with successful reuse.
|
|
120
|
+
|
|
121
|
+
Examples are illustrative, not new registered tools:
|
|
122
|
+
|
|
123
|
+
- A parameterized async change-review capability can be a useful named tool: it launches a fresh reviewer Run and later returns the declared report.
|
|
124
|
+
- A one-off synchronous command with a fixed task path may be worth neither retaining nor exposing.
|
|
125
|
+
- A useful but infrequent pipeline may stay retained and be spawned intentionally without becoming a standing tool.
|
|
126
|
+
- A long-lived playback service may earn a launcher tool and declared Control without becoming a synchronous function.
|
|
127
|
+
|
|
128
|
+
A subagent-backed tool is a **capability launcher**, not a persisted live subagent or a handle to an old Run. Ordinary invocations create distinct executions; singleton compatibility and reuse remain owned by the existing Run contract. Registering every transient subagent, Run ID, or mission as a new tool defeats this distinction.
|
|
129
|
+
|
|
130
|
+
## Review Evidence and Confidentiality
|
|
131
|
+
|
|
132
|
+
Keep automatic review read-only and value-free. Structural evidence can support eligibility checks, contract-shape comparisons, and bounded prioritization; it is not a substitute for understanding the task. Content-equality groups prove only the equality represented by the capture, not semantic equivalence or interchangeability.
|
|
133
|
+
|
|
134
|
+
A future semantic evidence channel must have a separately reviewed provenance, privacy, size, and authority contract before use. Raw commands, prompts, descriptions, paths, outcomes, and purportedly sanitized summaries are not automatically safe reviewer inputs. An operator-reviewed capability description or accepted-outcome record is a possible design input, not an available trusted carrier today. Do not let actor-authored text become admission authority or let missing evidence force an irreversible discard.
|
|
135
|
+
|
|
136
|
+
Evaluate memory quality by useful accepted reuse and reduced reconstruction cost, balanced against tool-selection ambiguity, stale contracts, runtime/review cost, and accidental exposure. Those are evaluation goals, not currently collected metrics or a requirement to add telemetry. Validate the policy on bounded controlled fixtures before claiming better agent behavior.
|
|
137
|
+
|
|
138
|
+
## Ownership and Migration Constraints
|
|
139
|
+
|
|
140
|
+
Extend existing owners instead of creating a second execution kernel, tool registry, Recipe resolver, or parallel task list:
|
|
141
|
+
|
|
142
|
+
- **Recipe resolution/discovery:** Preserve exact Skill/file identity, imports, runtime-owned origins, validation, and the distinction between retained definitions and tool candidates. A non-tool library must remain reachable through an admitted existing Recipe execution path.
|
|
143
|
+
- **Registry/runtime:** Own explicit exposure, host activation, withdrawal limitations, and `callable_now` evidence. Existing explicit `register_tool` intent remains valid for sync and async Recipes; the target policy does not silently prohibit either.
|
|
144
|
+
- **Draft/tool review and transactions:** Own evidence-limited recommendations, immutable captures, CAS, quarantine, journals, recovery, and admission of unchanged content. Retain/defer and withdrawal need explicit replay-safe transitions before changing automatic policy.
|
|
145
|
+
- **Usage/lineage:** Preserve revision and lifetime evidence across retention, exposure, withdrawal, and rollback without counting a migration as a launch or inventing successful outcomes.
|
|
146
|
+
- **Owning Skills:** Explain only implemented operations and approved selection guidance. Do not tell an agent to use a future library path, metadata flag, review action, or public command.
|
|
147
|
+
|
|
148
|
+
The retained directory, read-only discovery and initial create-only persistence/receipt contract are implemented. Retained updates/deletion, automatic transitions, aggregate retention bounds and the semantic evidence channel remain separate unresolved implementation choices. Prefer the smallest extension of current owners; do not add a new Recipe exposure field, database, service, or eager loading of every retained Recipe into the model context. Moving a Recipe can change relative imports and `{recipe_dir}` semantics: explicit retention needs an admitted source-preservation rule before any write, not a blind move from drafts or the tool root.
|
|
149
|
+
|
|
150
|
+
Existing user tools must not be silently demoted, deleted, renamed, generalized, or re-registered during adoption. Preserve executable bytes, delegated logical references, defaults, imports, Control, artifacts, runtime origins, usage, and recovery evidence. Prepare a read-only classification first; exact portfolio changes require an admitted migration contract and the authority applicable to that portfolio. Source tests or packaging do not authorize changes to the operator's live registry or reload the running host.
|
|
151
|
+
|
|
152
|
+
## Acceptance Properties
|
|
153
|
+
|
|
154
|
+
- **Orthogonality:** A useful async capability can be exposed; an unsuitable sync capture is not automatically exposed. Either mode can be retained without becoming a tool.
|
|
155
|
+
- **Evidence limits:** Structurally similar but semantically different Recipes cannot acquire an asserted purpose/usefulness solely from the same review shape. Unavailable semantic evidence stays explicit and can defer a decision without destructive loss.
|
|
156
|
+
- **Identity and replay:** Retain/expose/withdraw/recover preserve source and lineage, reject stale/conflicting plans, and do not create promotion/demotion churn or duplicate executable copies as new capabilities.
|
|
157
|
+
- **Invocation truth:** Explicit sync and async registration still works. Actual tool calls remain distinct from `spawn`; launch, eventual result, singleton attachment, and current callability retain their original meanings.
|
|
158
|
+
- **Compatibility:** Source and packed-package journeys preserve existing safety and explicit-registration behavior before any automatic-policy migration. No test writes into the operator's registry.
|
|
159
|
+
- **Product evidence:** Controlled evaluation must distinguish accepted useful reuse and selection cost from mere growth in Recipe/tool counts. Until measured, improved "muscle memory" remains a design hypothesis, not a shipped performance claim.
|
|
160
|
+
|
|
161
|
+
## Related
|
|
162
|
+
|
|
163
|
+
- [Tool Registry](./tool-registry.md) — Current registration, discovery, activation, and automatic-review behavior.
|
|
164
|
+
- [Skill-Owned Recipe Components](./recipe-library.md) — Existing reusable components without automatic tool exposure.
|
|
165
|
+
- [Template Recipes](./template-recipes.md) — Recipe and execution contracts.
|
|
166
|
+
- [Project Backlog](../BACKLOG.md) — Canonical remaining implementation and acceptance work.
|
|
@@ -113,7 +113,7 @@ Phases are monotonic:
|
|
|
113
113
|
|
|
114
114
|
Every journal mutation uses the canonical token-owned lock, expected-phase fencing, owner and generation validation, and atomic replacement. Repeated transitions are idempotent. Corrupt, oversized, foreign-owner, or stale-generation state fails closed with bounded diagnostics.
|
|
115
115
|
|
|
116
|
-
Ordinary source completion admission has one fresh path: presentation_format=ax_records, private result_layout=
|
|
116
|
+
Ordinary source completion admission has one fresh path: presentation_format=ax_records, private result_layout=completion_yaml_files_v1 and frozen text_budget, without excerpt_format or a public selector. The inactive numbered-stream kernel path keeps its separate frontmatter_v2 default. Bounded source/compiled and actual-default source/packed acceptance observes the normal index path and in-memory entry/context/provider/hint tuple. Unchanged storage/registered-renderer/accepted-history-reopen facts transfer only on the successful original-current-manager path; this is neither fresh default-disk observation nor global lifetime-policy, native-scope or loaded-host acceptance. Direct admission and ordinary run-ui-runtime use it after unchanged owner/location checks; an existing batch stays untouched. Each shown record begins YAML frontmatter with index, run, state, then kind:primary for admitted primary/final Actor response or kind:diagnostic for explicit error/failure excerpts. Untracked fallback has no kind and retains unknown completeness/privacy outside YAML. The closed header is followed by raw, unindented Markdown, with no #N heading, result:/preview: wrapper or compiler literal fence around the body. Known local loss is [N characters omitted] at the cut, not a YAML flag/count or extra ellipsis; N counts the admitted postprivacy source prefix loss, not private/native output. Unknown upstream loss remains separate. Original coverage remains structured member data; exact source/generation/location references remain whole typed nested YAML values. Routine summaries/windows/status copies/standing map instructions remain journal facts, not duplicated model content. Material omitted_results, non-done omitted_states and whole result_inventory recovery stay explicit. Each shown record has a whole run_files row or explicit omission, with unavailable reasons retained; omitted records do not leave orphan location rows. Outcomes precede lower-priority references under the total48KiB UTF-8/64candidate-row bound including framing. Prefix admission considers full-list and decimal-count discontinuities, freezes equal scalar/CRLF-safe shares without unused-share redistribution, and never clips references or rewrites a retained excerpt. Older captures retain their original16KiB/48KiB budgets.
|
|
117
117
|
|
|
118
118
|
Fresh ordinary source hints carry the core format and whole-content digest; the validated YAML core carries only packet identity and relative body/marker lengths, from which boundaries are derived. It adds no fresh HTML tail or duplicate coordinate table. These are consistency facts, not source or acceptance authority.
|
|
119
119
|
|
|
@@ -131,7 +131,7 @@ Removing fresh collection of untracked bodies would leave affected old/intermedi
|
|
|
131
131
|
|
|
132
132
|
### Retained capture compatibility
|
|
133
133
|
|
|
134
|
-
The numbered wire strings ax_records_v1/v2/v3, old indexed/single_result captures and result_layout records/run_headers/headings/headings_flat_omissions/headings_outcomes/headings_shared_results/headings_shared_kinds/frontmatter_v1/frontmatter_v2 are retained storage encodings, not public alternatives. Fresh ordinary source admission chooses own-data
|
|
134
|
+
The numbered wire strings ax_records_v1/v2/v3, old indexed/single_result captures and result_layout records/run_headers/headings/headings_flat_omissions/headings_outcomes/headings_shared_results/headings_shared_kinds/frontmatter_v1/frontmatter_v2 are retained storage encodings, not public alternatives. Fresh ordinary source admission chooses own-data completion_yaml_files_v1 with canonical ax_records and text_budget (over the unchanged completion_yaml_core_v1 carrier); earlier completion_yaml_core_v1 snapshots retain their old layout, never single_result. Absence preserves its original indexed/singleton codec; inherited aspects never restyle data and accessors are refused without invocation. Earlier record/header/count/kind grammar and each captured16KiB/48KiB share remain exact. The first frontmatter draft retains its original bytes and hints, including unquoted unknown index ?. Retained v2 fixed that one YAML case by writing index: "?"; numeric indices and other fields/body bytes are unchanged, and the presentation-hint schema remains completion_frontmatter_v1 because its block structure is unchanged. Preserve the frozen v1 unknown-content fingerprint90e29fb36087e2620fe0cf150b4238bce8a6d1dcc68ee922a1cf71ee8604610e and prior shared-result fingerprint4428de318b8ebaa029d3588ab7e8e705b9a80fa5cf08b6a2cc7151916207e266. Journals store snapshots rather than complete text: neither an unreleased product, a YAML correction nor member count permits relabeling/migration. Pending/queued/presented replay must reproduce original model bytes and hints without operator journal scans, source refresh or message rewrites. Removing compatibility requires a separate history-lifetime decision.
|
|
135
135
|
|
|
136
136
|
Steer envelopes already store complete content. Both attention and steer now have one canonical new-message formatter with no presentation selector; the redundant legacy formatting branches/path/body helpers are removed where no retained-data consumer needs them. Existing steer identity is reused before formatting and saved content bypasses the formatter in every pending/queued/presented recovery path. Removing stored Batch codecs later requires a separate retention/data-lifetime decision; an unreleased product is not permission to change previously captured delivery bytes.
|
|
137
137
|
|
package/docs/inspection.md
CHANGED
|
@@ -95,12 +95,14 @@ inspect target=recipes view=imports verbose=true
|
|
|
95
95
|
inspect target=recipes view=reviews verbose=true
|
|
96
96
|
```
|
|
97
97
|
|
|
98
|
-
- `status` reports registry/watch state, active/shadowed/invalid/disabled user Recipes, active Skill components, drafts, usage, and diagnostics.
|
|
98
|
+
- `status` reports registry/watch state, active/shadowed/invalid/disabled user Recipes, active Skill components, drafts, retained-library definitions, usage, and diagnostics.
|
|
99
99
|
- `summary` returns the compact discovery-oriented projection of the same current context. `registry` is the same inspection path under a second accepted view name: with identical `verbose` and other inputs it returns identical facts, details and rendering, so only `verbose` selects compact text versus structured output. The compact label stays `recipes: registry` for both names.
|
|
100
100
|
- `doctor` diagnoses one canonical `<skill>/<recipe>` identity when `identity` is provided; without an identity it reports registry remediation evidence.
|
|
101
101
|
- `imports` exposes active Skill namespaces and component import inventory for resolution diagnosis.
|
|
102
102
|
- `reviews` exposes bounded automatic draft/tool review state and recovery guidance.
|
|
103
103
|
|
|
104
|
+
The compact Recipe catalog adds `retained` as a separate count for direct `.json`/`.md` definitions under `recipes/library`. Details/verbose includes `retained` rows (exact path, source_kind=user_retained_recipe, tool=false, resolved async mode, invalid/disabled state and diagnostics) and `retained_diagnostics`. Both root and library use the active session Skill context. Storage placement does not certify review, current callability or Run state; invalid/disabled definitions remain diagnostic. The missing library is empty, without initialization. Inspection performs no retention write, activation or launch. Valid enabled exact paths remain ordinary explicit-file references for `spawn` or deliberate `register_tool from` specialization. Inspection adds no mutation or logical-reference syntax; use explicit `register_tool expose=false` for supported create-only retention, and see [capability memory](./capability-memory.md#read-only-inventory-checkpoint).
|
|
105
|
+
|
|
104
106
|
`identity` is accepted only with `view=doctor`. Skill inventory is fail-soft diagnostic evidence; exact launch and registration still resolve against the immutable active-session context.
|
|
105
107
|
|
|
106
108
|
## Tool Targets
|
|
@@ -91,10 +91,10 @@ F1.6 source-audit correction: Pi 1.0 native custom-message storage, session reco
|
|
|
91
91
|
### 10. Completion Projection, Compiler Frame and Native Renderer
|
|
92
92
|
|
|
93
93
|
- **Owners:** `run-delivery.ts:1510` `completionRecordRow`, `:1683` `assembleFrontmatter`, `:1886` `projectCompletionRecords`, `:2110` `projectRunCompletionBatchPresentation`, `:2233` `readRunCompletionPresentation`, `:2283` retained projection, `:2328` message formatting. `run-ui-runtime.ts:68` `registerRunCompletionRenderer` is wired at `extension-runtime.ts:122`.
|
|
94
|
-
- **Current:**
|
|
95
|
-
- **Prepared wire core:** `run-delivery.ts` `encodeCompletionYamlCarrier` / `readCompletionYamlCarrier` provide the accepted pure/nondefault mechanics; the prototype encodes and typed native view decodes them.
|
|
96
|
-
- **Prepared source projection (nondefault):** `projectRunCompletionYamlPrototype` projects current AX/frontmatter selected snapshots through one shared private fitter; it returns content, a format/digest-only hint and fitting facts, not a newly admitted or relabeled Batch. Frozen budgets/prefixes remain immutable. Canonical header/raw-part costs are measured additively, with unsupported data distinct from byte overflow; final content must agree with the unchanged core encoder/decoder. This prototype API remains data-only and source-class restricted. The
|
|
97
|
-
- **
|
|
94
|
+
- **Current:** Fresh ordinary `completion_yaml_files_v1` snapshots use the unchanged strict YAML-core carrier. Header files.groups stores literal roots with relative entries, source bindings and English purposes; read_first is a group/entry pointer. Unknown-base paths stay explicitly as recorded. Native compact presentation exposes those file groups and places omission/inventory facts in the first header, never after the Markdown. Only known character-cut markers remain beside a truncated body. Earlier core-v1 and historical layouts keep their exact projection and hiding/footer behavior.
|
|
95
|
+
- **Prepared wire core:** `run-delivery.ts` `encodeCompletionYamlCarrier` / `readCompletionYamlCarrier` provide the accepted pure/nondefault mechanics; the prototype encodes and typed native view decodes them. Ordinary source/default capture uses the rooted snapshot layout; the earlier core-v1 snapshot and inactive numbered-stream frontmatter_v2 remain separate replay/gated paths. Strict-core Pi correlation and source/packed SDK evidence bind the selected content without granting native or read authority. They encode one `_completion` packet ID/codec and relative body/marker lengths, then derive dense ranges while decoding. Canonical bare headers are independent YAML fragments separated by codec-owned blank lines, not one mapping or a standard YAML multi-document stream; body-present headers are delimited and raw parts remain opaque. All-omitted is bare service metadata, not a fake Run. Empty selected bodies are distinct from absent bodies; empty-marker-only wrappers are refused. Input uses named top-level result keys with explicit depth/work bounds; loaded-host acceptance remains F1.7/U1-gated, and meaningful service warnings must not be hidden wholesale.
|
|
96
|
+
- **Prepared source projection (nondefault):** `projectRunCompletionYamlPrototype` projects current AX/frontmatter selected snapshots through one shared private fitter; it returns content, a format/digest-only hint and fitting facts, not a newly admitted or relabeled Batch. Frozen budgets/prefixes remain immutable. Canonical header/raw-part costs are measured additively, with unsupported data distinct from byte overflow; final content must agree with the unchanged core encoder/decoder. This prototype API remains data-only and source-class restricted. The ordinary rooted capture reuses internal YAML assembly and the same independently measured quotas/inventory rather than relabeling an old frozen snapshot. Native renderer support is prepared below; actual carrier acceptance remains a separate gate.
|
|
97
|
+
- **Native view:** `readRunCompletionPresentation` derives frozen typed segments from the strict core and exact minimal format/digest hint; the registered renderer keeps new rooted file groups and all meaningful service facts in headers, then renders each body as independent native Markdown with literal Text metadata/markers. Retained core-v1 captures keep their earlier reference hiding and service placement. Valid-core missing/invalid hints and declared-new malformed content expose original full Text; expansion keeps that same source. Malformed content without a safely readable own new-format declaration retains historical dispatch, not a body-scanning heuristic. Headless widths/padding and old-history preservation are proved. Repo SDK 1.0.0's public `CustomMessageComponent` falls back to one Markdown component when a renderer is absent, returns undefined or throws, joining text-block arrays; a fresh component probe confirms this boundary, not session/interactive dispatch. Preserve that fallback unchanged: full content supplied to Markdown is neither independent-body rendering nor literal-visibility proof. Independent Markdown belongs only to the validated registered compact branch; registered degraded/expanded views keep original Text. Actual source-admitted native storage/context/provider/reopen and loaded terminal/phone behavior remain separate acceptance.
|
|
98
98
|
- **Selected-source bridge preservation:** A collector-supported legacy artifact declaration containing a lone UTF-16 surrogate is accepted and escaped by the old codec but refused by the pure YAML core; validator support does not assert that its target exists. The private bridge must preserve such known string-valued metadata slots as an explicit `utf16_code_units` mapping of four-hex-digit units, without U+FFFD repair, reference deletion, body encoding or machine/history mutation. Ordinary strings stay strings. Source-family probes reached depth eight and an exact 49,152-byte equal-share packet; these are representative witnesses, not universal compatibility proof. Pure projection/fitting precedes native renderer dispatch. One private optimizer distinguishes unsupported data from byte overflow; no public selector or newly budgeted Batch mislabeled as an old layout. Meaningful service warnings remain visible, and final output must satisfy the unchanged core's whole-packet contract.
|
|
99
99
|
- **Concrete coupling:** `readFrontmatterFrame` (`run-delivery.ts:1628`) validates single-line fields/values and complete ordered coverage. The retained compact branch in `run-ui-runtime.ts` hides only single `references: ` / `run_files: ` lines; the new typed-segment branch handles whole decoded nodes. A serializer-only switch to nested YAML breaks validation or exposes orphaned reference children. Header parsing, whole-node hiding, fitting, hint/frame generation and source/body rendering must move together for new captures.
|
|
100
100
|
- **Target / guard:** Block-YAML metadata with body-dependent delimiters; opaque independent body blocks and exact omission markers. Keep old codecs/bytes/hints, 48 KiB/64 shown/256 captured limits and whole references. The F1.6 necessity audit supports deleting fresh prefix hashes and duplicate coordinate/range tables, not moving the whole payload. The target carries one packet identity and minimal relative opaque-body/marker segments in YAML; absolute ranges are derived. Full admitted-source compatibility and actual carrier acceptance remain unproved, so the current HTML reader/producer are not removed in place. `completionInventoryText` (`:434`) remains machine JSON.
|
package/docs/releasing.md
CHANGED
|
@@ -19,6 +19,10 @@ Simple tools/registration fixtures bind original runner births and actual `close
|
|
|
19
19
|
|
|
20
20
|
Implementation and native CI acceptance are tracked in BACKLOG until the complete sharded candidate passes.
|
|
21
21
|
|
|
22
|
+
## Local Validation
|
|
23
|
+
|
|
24
|
+
Run `npm run validate` and `npm run audit:dependencies` with Node24 selected, including after any login-shell setup. When launching validation from an Actor, clear `PI_ACTORS_DELIVERY_OWNER_ID`, `PI_ACTORS_DELIVERY_PARENT_RUN`, `PI_ACTORS_DELIVERY_PARENT_INSTANCE` and `PI_ACTORS_DELIVERY_PARENT_STATE_DIR` only in the test workload's environment. Otherwise independent SDK fixture coordinators may inherit the validation Actor's completion lineage and wait for the wrong coordinator. Keep the launcher's own lineage intact; this isolation changes neither product defaults nor test cases, the standard agent-directory preload or watchdogs.
|
|
25
|
+
|
|
22
26
|
## One-time npm Trusted Publisher setup
|
|
23
27
|
|
|
24
28
|
Configure the existing public package `@llblab/pi-actors` on npmjs.com with a GitHub Actions Trusted Publisher using these exact values:
|
package/docs/tool-registry.md
CHANGED
|
@@ -38,7 +38,20 @@ register_tool name=repo_check template=null
|
|
|
38
38
|
|
|
39
39
|
An empty `template` string is the equivalent deletion form. Do not delete registry files manually. Host-visible dynamic definitions may remain visible until reload because Pi cannot unregister them, but extension-local lookup rejects the deleted definition immediately. Name collisions for creation or replacement require `update=true`. Invalid content fails before active mutation.
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
Ordinary tool registration resolves the effective delegated contract before persistence, then reports logical `source`, effective `required_args` / `optional_args`, and distinct `persisted`, `registry_active`, `host_registered`, `active_tool`, and `callable_now` states. A callable result points to the actual generated tool as the next action. An uncallable result names its `activation_boundary` and status/doctor action without suggesting spawn as a substitute. Diagnose one maintained source with `inspect target=recipes view=doctor identity=<skill>/<recipe>`; diagnose final activation, source, schema summary, and separate spawn/tool usage with `inspect target=tool:<name> view=status`. Treat the tool as callable in the current session only when `callable_now` is true; persistence alone is not activation proof. Ordinary tool-registration results omit raw persisted paths and executable template/config payloads; retained receipts deliberately return the reusable file path, never an executable body.
|
|
42
|
+
|
|
43
|
+
## Retain a New Capability Without Tool Exposure
|
|
44
|
+
|
|
45
|
+
```text
|
|
46
|
+
register_tool name=repo_check expose=false template="make check" description="Run repository checks"
|
|
47
|
+
register_tool name=review_changes expose=false from=swarm/lens-review
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
This create-only mode writes `recipes/library/<name>.json`, not a tool-root entry. It accepts a new name and one template/from source; retained update, null/empty deletion, draft promotion and direct references to managed disposable drafts (including Recipe-valued template leaves) are rejected. JSON/Markdown stem conflicts, invalid source/defaults, oversized candidates and unsafe storage fail without activating a tool. Existing tools with the same name are untouched. Publication is a complete atomic no-overwrite file link; unsupported filesystems refuse rather than falling back to replacement.
|
|
51
|
+
|
|
52
|
+
A `kind: retained_recipe`, `activation: not_requested` receipt confirms admission/persistence and returns the exact Recipe path with required/optional argument names. It omits global callability flags and defaults: a separately exposed same-named tool might already be callable. The next hint is read-only Recipe inspection, not activation repair. Reuse the exact retained file with `spawn recipe=<path>`, or expose a compact wrapper deliberately with ordinary `register_tool name=<tool> from=<path>`. Deleting an admitted wrapper through the existing deletion path does not delete its retained source or stop active Runs. No automatic retention, review decision, source relocation or live migration is added.
|
|
53
|
+
|
|
54
|
+
A post-persistence admission, storage, publication-cleanup or lock-release failure can leave committed bytes; original causes are retained and uncertain files are not blindly deleted. Inspect the inventory before considering any retry. Ordinary tool registration keeps its existing rollback and activation contract.
|
|
42
55
|
|
|
43
56
|
## Resolution
|
|
44
57
|
|
|
@@ -66,6 +79,12 @@ Async registered tools add runtime-owned optional invocation parameters after re
|
|
|
66
79
|
|
|
67
80
|
These parameters are not Recipe-authored args. Singleton tools do not expose `run_id` because their canonical Run identity is owned by the Skill Recipe.
|
|
68
81
|
|
|
82
|
+
## Retention Versus Exposure — Adopted Direction
|
|
83
|
+
|
|
84
|
+
The [capability-memory design](./capability-memory.md) separates preserving a reviewed reusable Recipe from offering it as a named tool. Either sync or async execution can qualify; a subagent-backed tool launches a capability, not a persisted live subagent. Async calls return a launch receipt, not the eventual work result.
|
|
85
|
+
|
|
86
|
+
The explicit create-only slice is implemented; the automatic lifecycle remains a target. The user root still exposes valid enabled Recipes by location, automatic draft review still chooses promote/discard, and ordinary `register_tool` remains available for both modes. `expose=false` explicitly retains new template/from definitions without activation. Read-only `inspect target=recipes view=summary` lists library definitions separately from tool candidates; verbose detail provides exact paths, mode and invalid/disabled diagnostics. No Recipe exposure field or retain/defer review action is added. A library row is storage intent, not proof of review, callability or a running Actor. Structural review and launch counts alone cannot prove semantic usefulness or accepted successful reuse. Remaining integration belongs to [BACKLOG.md](../BACKLOG.md).
|
|
87
|
+
|
|
69
88
|
## Automatic Review
|
|
70
89
|
|
|
71
90
|
Automatic draft/tool review remains silent and mechanically fenced:
|
package/lib/file-state.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
8
8
|
import { createHash, randomUUID } from "node:crypto";
|
|
9
|
-
import { existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { existsSync, linkSync, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { tmpdir } from "node:os";
|
|
11
11
|
import { basename, dirname, join, parse, resolve } from "node:path";
|
|
12
12
|
|
|
@@ -297,14 +297,18 @@ function replaceFileWithRetry(source: string, target: string): void {
|
|
|
297
297
|
export function writeTextAtomic(
|
|
298
298
|
path: string,
|
|
299
299
|
content: string,
|
|
300
|
-
options: { onBeforeReplace?(): void } = {},
|
|
300
|
+
options: { exclusive?: boolean; onBeforeReplace?(): void } = {},
|
|
301
301
|
): void {
|
|
302
302
|
mkdirSync(dirname(path), { recursive: true });
|
|
303
303
|
const tempPath = `${path}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;
|
|
304
304
|
try {
|
|
305
305
|
writeFileSync(tempPath, content, "utf8");
|
|
306
306
|
options.onBeforeReplace?.();
|
|
307
|
-
|
|
307
|
+
if (options.exclusive === true) {
|
|
308
|
+
// Publish a complete new file atomically without clobbering an uncooperative creator.
|
|
309
|
+
linkSync(tempPath, path);
|
|
310
|
+
unlinkSync(tempPath);
|
|
311
|
+
} else replaceFileWithRetry(tempPath, path);
|
|
308
312
|
} catch (error) {
|
|
309
313
|
try {
|
|
310
314
|
unlinkSync(tempPath);
|