@aeondave/pi-persona 1.17.3 → 1.17.5
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 +57 -36
- package/CHANGELOG.md +106 -1
- package/README.md +32 -16
- package/docs/ARCHITECTURE.md +199 -48
- package/docs/COLLABORATION.md +110 -0
- package/docs/EXPERIENCE.md +33 -8
- package/docs/README.md +12 -0
- package/docs/REFERENCE.md +65 -14
- package/docs/STRATEGIES.md +6 -0
- package/package.json +12 -6
- package/src/agents/agent.ts +6 -0
- package/src/bridge.ts +33 -23
- package/src/bus/broker/client.ts +151 -30
- package/src/bus/broker/host.ts +130 -11
- package/src/bus/broker/messages.ts +35 -5
- package/src/bus/contact-transport.ts +8 -0
- package/src/bus/contact.ts +118 -40
- package/src/bus/peers.ts +23 -6
- package/src/bus/result-reader.ts +88 -0
- package/src/core/brief.ts +14 -4
- package/src/core/capabilities.ts +1 -1
- package/src/core/companion.ts +64 -0
- package/src/core/leg-thinking.ts +57 -0
- package/src/core/ownership.ts +1 -2
- package/src/core/pi-compat.ts +5 -12
- package/src/core/session-runs.ts +1 -1
- package/src/core/types.ts +1 -1
- package/src/engine/adapter.ts +100 -20
- package/src/engine/async.ts +53 -11
- package/src/engine/child.ts +11 -2
- package/src/engine/inproc.ts +98 -47
- package/src/engine/result-grant.ts +62 -0
- package/src/engine/session-tools.ts +6 -0
- package/src/engine/stream.ts +6 -0
- package/src/engine/worktree.ts +17 -7
- package/src/exocom/collaboration-delivery.ts +29 -0
- package/src/exocom/collaboration.ts +148 -0
- package/src/exocom/envelope.ts +43 -4
- package/src/exocom/gate.ts +7 -2
- package/src/exocom/install.ts +45 -17
- package/src/exocom/ledger.ts +58 -8
- package/src/exocom/plane.ts +32 -1
- package/src/exocom/profile.ts +40 -0
- package/src/exocom/registry.ts +5 -0
- package/src/exocom/status.ts +11 -1
- package/src/exocom/write-guard.ts +7 -0
- package/src/extension/broker-host.ts +61 -9
- package/src/extension/companion.ts +38 -0
- package/src/extension/engine.ts +40 -29
- package/src/extension/hooks.ts +954 -931
- package/src/extension/identity.ts +41 -75
- package/src/extension/memory-owner.ts +54 -0
- package/src/extension/mind-worker.ts +129 -0
- package/src/extension/results.ts +138 -0
- package/src/extension/shared.ts +17 -0
- package/src/extension.ts +2922 -2849
- package/src/loader.ts +11 -3
- package/src/orchestration/agent-attempt.ts +15 -0
- package/src/orchestration/flow-journal.ts +7 -1
- package/src/orchestration/flow-run.ts +125 -19
- package/src/orchestration/flow.ts +11 -0
- package/src/orchestration/roster.ts +71 -23
- package/src/orchestration/sdk.ts +61 -19
- package/src/orchestration/types.ts +10 -0
- package/src/persona/model-participants.ts +20 -7
- package/src/persona/orchestrate.ts +6 -7
- package/src/persona/persona.ts +1 -1
- package/src/results/archive.ts +281 -0
- package/src/results/record.ts +166 -0
- package/src/results/retrieval.ts +142 -0
- package/src/results/types.ts +128 -0
- package/src/telemetry/contract.ts +1 -1
- package/src/tools/council.ts +40 -23
- package/src/tools/delegate-tool.ts +50 -14
- package/src/tools/delegate.ts +13 -6
- package/src/tools/exocom-collaboration-input.ts +168 -0
- package/src/tools/exocom-collaboration.ts +315 -0
- package/src/tools/exocom-profile.ts +102 -0
- package/src/tools/exocom.ts +20 -3
- package/src/tools/flow.ts +24 -8
- package/src/tools/intercom-tool.ts +98 -14
- package/src/tools/models.ts +15 -0
- package/src/tools/monitor.ts +20 -1
- package/src/tools/read-result.ts +52 -0
- package/src/tools/timer.ts +23 -2
- package/src/ui/activity-rows.ts +34 -0
- package/src/ui/agent-overlay.ts +23 -13
- package/src/ui/agent-tree.ts +67 -6
- package/src/ui/literal-tool-result.ts +1 -1
- package/src/ui/model-picker.ts +1 -1
- package/src/ui/orchestration-launch.ts +49 -0
- package/src/ui/session-cards.ts +191 -14
- package/src/ui/strategy-leg.ts +40 -0
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -81,9 +81,10 @@ These are the guardrails a contributor must not violate. They are enforced in co
|
|
|
81
81
|
by the SDK on every `agent()` call and by the engine per child. Concurrency is shared across all
|
|
82
82
|
calls in one SDK instance; token admission uses completed usage and is rechecked after queueing, so
|
|
83
83
|
active legs can overshoot it. Nesting depth is not one of these numeric knobs — it is structural,
|
|
84
|
-
capped at 1:
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
capped at 1: full pi-persona activation short-circuits under `PI_PERSONA_DISABLE=1` (the
|
|
85
|
+
**fork-bomb guard**), so no supervisor orchestration tools (`delegate`/`council`/`flow`) are
|
|
86
|
+
registered. A broker-enabled child instead loads the minimal bridge, which registers only
|
|
87
|
+
granted communication and scoped result-reader tools. On the child engine this is a one-shot env var
|
|
87
88
|
set on each spawned process; the in-process engine instead shares one Node process across
|
|
88
89
|
concurrently-building sub-sessions, so it ref-counts the guard (`inproc.ts`) to keep it set until the
|
|
89
90
|
LAST one finishes. The in-process engine also excludes `ORCHESTRATION_TOOLS` from the child session
|
|
@@ -151,6 +152,12 @@ These are the guardrails a contributor must not violate. They are enforced in co
|
|
|
151
152
|
|
|
152
153
|
## Module layout
|
|
153
154
|
|
|
155
|
+
The supported host is the latest stable Pi (currently 1.4.0). Runtime code uses its public APIs
|
|
156
|
+
directly; obsolete host facades, ignored SDK options and compatibility-only aliases are removed
|
|
157
|
+
with their callers. Genuine host failures remain explicit rather than fabricating session identity
|
|
158
|
+
or model availability. One-way user-data migrations and retained-history readers are separate data
|
|
159
|
+
contracts: they remain only while serving stored user data, not to support an older Pi runtime.
|
|
160
|
+
|
|
154
161
|
Domain dependencies remain downward-only and acyclic: `core ← all`; `engine`/`bus → core`;
|
|
155
162
|
`orchestration → engine + bus + core`; `persona → orchestration + core`; the Exocom transport and
|
|
156
163
|
ledger → `core + bus`; `tools`/`ui → lower layers`. `src/exocom/install.ts` is deliberately the
|
|
@@ -281,10 +288,18 @@ up to 16 names. Same idea, two layers, two caps — see docs/REFERENCE.md's exam
|
|
|
281
288
|
|
|
282
289
|
`isolation: worktree` is fail-closed. It requires a clean Git checkout so the detached `HEAD` view
|
|
283
290
|
cannot silently omit staged/unstaged/untracked supervisor work; a non-repository, dirty checkout,
|
|
284
|
-
worktree creation failure,
|
|
285
|
-
the base engine is never invoked against the real tree as a fallback. The
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
worktree creation failure, or a successful leg with neither a captured diff nor a returned unified
|
|
292
|
+
patch fails the leg — the base engine is never invoked against the real tree as a fallback. The
|
|
293
|
+
created worktree starts from the committed `HEAD` only: local ignored files are not copied into it,
|
|
294
|
+
and untracked ignored outputs are not exported. The original checkout's ignored files remain
|
|
295
|
+
untouched; tasks that need local-only inputs must recreate or provide them explicitly. The
|
|
296
|
+
worktree's initial commit is pinned before the worker starts. Export compares the final file state
|
|
297
|
+
against that immutable commit, so worker commits, later unstaged changes and new files are included
|
|
298
|
+
together. The diff is captured before the temporary tree is removed, then returned to the supervisor;
|
|
299
|
+
commit history is not applied to the main checkout. If Git cannot capture the diff but the worker
|
|
300
|
+
returned a unified patch, that patch is retained with a warning that it was worker-provided and not
|
|
301
|
+
verified against the worktree state. Every git call is asynchronous so the extension host keeps
|
|
302
|
+
rendering, ticking timers, and serving broker sockets while a large checkout runs.
|
|
288
303
|
|
|
289
304
|
Transient retries inside one agent session belong to the host **Pi runtime**'s `retry.*` settings, not
|
|
290
305
|
a persona contract — pi-persona neither parses retry notation nor schedules its own backoff. Both
|
|
@@ -307,10 +322,19 @@ models, repeated same-model attempts, stop/timeout/contract/agent/infrastructure
|
|
|
307
322
|
child/token budgets do not recover. Both attempts are accounted, and a warning plus result metadata
|
|
308
323
|
identify the switch. A per-role model assignment can supplement the legacy per-agent assignment;
|
|
309
324
|
preflight model selection includes external arbiters and other declared agent-valued parameters.
|
|
310
|
-
Live tree and telemetry labels
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
325
|
+
Live tree and telemetry labels resolve auxiliary actors and every repeated roster occurrence from
|
|
326
|
+
the SDK's exact, immutable attempt identity (`agent`, full `role`, explicit `model` and `thinking`),
|
|
327
|
+
alongside its unique node key. Short role hints and `#N` suffixes are presentation only: colliding
|
|
328
|
+
hints never discard a known model or borrow another occurrence's inline pins. Model assignments
|
|
329
|
+
and agent defaults are copied after setup when the engine is constructed, so a queued leg's display
|
|
330
|
+
cannot change with later persona/settings edits. Seeds use each actual roster member; lifecycle
|
|
331
|
+
updates use the actual attempt, including custom strategy calls and auxiliary actors in any order.
|
|
332
|
+
A recovery keeps the node key, changes its requested model, and clears the preceding attempt's Pi
|
|
333
|
+
effort observation. Terminal `modelUsed` and validated effort observations override intent only when
|
|
334
|
+
reported; an absent observation retains the current attempt's intent. Initial effort remains the
|
|
335
|
+
explicit spec request, never inferred provider cognition. Clearing an unknown model emits an empty
|
|
336
|
+
`model` string in a telemetry update, preserving the existing string patch contract without leaving
|
|
337
|
+
a stale previous model in consumers.
|
|
314
338
|
|
|
315
339
|
**Model assignment repair is configuration, not execution recovery.** `/persona models [profile]`
|
|
316
340
|
is an explicit user edit in the Pi adapter: it reuses `resolveCouncilInvocation`,
|
|
@@ -487,8 +511,9 @@ never simultaneously a telemetry event, a routed message, and a UI source of tru
|
|
|
487
511
|
usage`). Feeds derived UI; never routed as an agent message.
|
|
488
512
|
- **Bus Msg** — semantic agent↔agent communication (`progress · decision · interview`, and sibling
|
|
489
513
|
peer sends). `src/bus`. `progress_update` is a Bus message only, not an EngineEvent.
|
|
490
|
-
- **ProgressView** — a derived, read-only UI view computed
|
|
491
|
-
|
|
514
|
+
- **ProgressView** — a derived, read-only UI view computed from observed runtime lifecycles:
|
|
515
|
+
EngineEvents, wake-source lifecycles, or the bounded local companion lifecycle below. Never a
|
|
516
|
+
source of truth. The `f9` agent tree and `peek` digest are ProgressViews.
|
|
492
517
|
|
|
493
518
|
Steering is a supervisor-to-child control action, distinct from semantic Bus messages; requesting
|
|
494
519
|
steering does not establish that a blocking headless delegate call has been interrupted. The peek digest is
|
|
@@ -498,15 +523,83 @@ External telemetry is not a fourth communication plane: it is a generic observer
|
|
|
498
523
|
future plugins may consume or produce, not an exclusive pi-persona protocol. It observes projected
|
|
499
524
|
events and cannot route, reply, steer, or otherwise control agents.
|
|
500
525
|
|
|
526
|
+
### Loaded companions and local activity
|
|
527
|
+
|
|
528
|
+
The local EventBus request `pi-persona:companion:v1` accepts `{ version: 1, respond }` and calls
|
|
529
|
+
`respond` synchronously with `{ version: 1, provider: "pi-persona", role, capabilities }`.
|
|
530
|
+
`role` is `supervisor`, `leg`, or `disabled`; `capabilities.memory` is `1` for a loaded supervisor or
|
|
531
|
+
delegated leg and `false` for a disabled supervisor. `capabilities.activity` is `1` only for an enabled
|
|
532
|
+
supervisor. Persona registers this responder before its bridge and disable guards. Pi owns the
|
|
533
|
+
subscription lifetime and revokes it on disposal/reload. Companions query the loaded extension;
|
|
534
|
+
settings files, package installation, and environment flags cannot establish its presence. This is
|
|
535
|
+
a cooperative local extension contract, not authentication or an OS permission boundary. The
|
|
536
|
+
contract is available from Persona 1.17.5, paired with Mind 0.8.4 or later; qualify both together.
|
|
537
|
+
|
|
538
|
+
Memory ownership is queried through `pi-persona:memory-owner:v1` with
|
|
539
|
+
`{ version: 1, sessionId, respond }`. Its synchronous response is
|
|
540
|
+
`{ version: 1, provider: "pi-persona", sessionId, ready, persona }`, where `persona` is the actual
|
|
541
|
+
live selection or `null` for Persona off. The native manager/id/file must still match, and `ready`
|
|
542
|
+
remains false until Persona's session startup finishes. `pi-persona:memory-owner-changed:v1`
|
|
543
|
+
broadcasts that same descriptor on readiness, selection and shutdown. A companion loaded first
|
|
544
|
+
must not wait inside its startup hook for a later extension's hook; it can activate on the readiness
|
|
545
|
+
broadcast or its next fresh query. Canonical settings retain startup preferences, not live ownership:
|
|
546
|
+
`persist: false` selections still change this descriptor. Unavailable ownership never authorizes a
|
|
547
|
+
fallback to another persona's saved selection.
|
|
548
|
+
|
|
549
|
+
Each engine freezes the parent's memory persona when it is built. In-process children receive a
|
|
550
|
+
private ResourceLoader factory and their own native manager/EventBus responder; child processes
|
|
551
|
+
receive the versioned `PI_PERSONA_MEMORY_PERSONA` record only in that spawn's environment. Inherited
|
|
552
|
+
values are scrubbed before spawning. Neither route mutates a shared process-wide persona value.
|
|
553
|
+
Delegated legs expose this frozen owner while retaining their lean memory-only role, including an
|
|
554
|
+
explicit `null` owner when the parent had Persona off.
|
|
555
|
+
|
|
556
|
+
`pi-persona:activity:v1` contributes operator-only local-work rows through a closed record:
|
|
557
|
+
`{ version: 1, producerId: "pi-persona-mind", activityId, sessionId, phase, state, target }`.
|
|
558
|
+
`phase` is `inspect|stage|apply`, `state` is `running|staged|done|failed`, and `target` is
|
|
559
|
+
`project|persona|shared`; `staged` is valid only for `stage`. No memory text, paths, models, token
|
|
560
|
+
counts, or free-form output are accepted. Ingress matches the active native session and checks its
|
|
561
|
+
manager's current id/file. The producer also captures its native owner and scope across awaits.
|
|
562
|
+
The resulting `Mind` row uses `kind: "activity"`: it has no agent count, steer/stop handle, wake,
|
|
563
|
+
stall badge, or `agent.*` telemetry. The F9 drill-in explains the local phase. Up to eight rows are
|
|
564
|
+
retained, with completed/staged rows evicted before live work; teardown and explicit persona
|
|
565
|
+
selection revoke old rows without cancelling workers. A staged row states that a proposal was
|
|
566
|
+
prepared and requires explicit apply; it does not claim that the plan remains valid indefinitely or
|
|
567
|
+
that model review is running.
|
|
568
|
+
Receipts remain in Mind's session-native cards; local activity events are not replayed on restart.
|
|
569
|
+
|
|
570
|
+
Mind may request one real async maintenance worker through `pi-persona:mind-worker:v1` with
|
|
571
|
+
the closed record `{ version: 1, sessionId, persona, task, respond }` (task at most 48,000 characters).
|
|
572
|
+
Persona checks the current native session and ready memory owner, actual persona, delegation and
|
|
573
|
+
memory permissions, the persona's raw delegate target rules for `mind`, and idle/no-pending-message
|
|
574
|
+
state. A strict target allowlist must include `mind`; explicit memory/delegate/target denies win.
|
|
575
|
+
No installed agent is required. Accepted
|
|
576
|
+
responses are synchronous `{ ok: true, runId, model, thinking, completion, cancel }`; rejection is
|
|
577
|
+
`{ ok: false, reason }`. `completion` resolves with `{ ok, output, error?, resultRef?, model?, thinking? }`.
|
|
578
|
+
Output remains untrusted data for Mind's bounded proposal parser, never executable code.
|
|
579
|
+
|
|
580
|
+
This is an ordinary tracked `Mind` sub-agent in the tree and F9, sharing the launch pool, usage,
|
|
581
|
+
progress, stop/steer controls and terminal-result archive. Its private definition has an explicit empty
|
|
582
|
+
tool allowlist and no worker spine. Provider/model and thinking are pinned to the accepting session;
|
|
583
|
+
provider fallback is disabled. The worker has a 180-second cancellation ceiling; owner readiness or
|
|
584
|
+
selection changes cancel it. Mind can also cancel through its accepted handle. Settlement returns
|
|
585
|
+
to the requesting companion and retains operator failure reporting, but never wakes a supervisor
|
|
586
|
+
model turn. Native session teardown cancels it through the ordinary stop registry. Local inspect,
|
|
587
|
+
stage and apply operations continue to use activity rows; model work uses the real agent view.
|
|
588
|
+
|
|
501
589
|
## The comm plane in practice
|
|
502
590
|
|
|
503
591
|
- **In-process bus** (`bus/inproc.ts`) — a handle-based mailbox: `send` (one-way), `ask` (blocks for a
|
|
504
|
-
reply), `reply`, `onMessage`. `contact_supervisor` (child→supervisor, gated by `
|
|
592
|
+
reply), `reply`, `onMessage`. `contact_supervisor` (child→supervisor, gated by `canUseBus` and,
|
|
593
|
+
for in-process children, `coaching: on`) and
|
|
505
594
|
`contact_peer` (sibling→sibling) are the child-side tools bound onto it. Each unread inbox holds at
|
|
506
595
|
most 200 messages; overflow evicts an old one-way note first, and an inbox full of live asks rejects
|
|
507
596
|
new delivery rather than stranding a sender. Ask settlement (reply, timeout, abort, departure, or
|
|
508
597
|
failed delivery) releases its timer/listener and unread entry; a previously retained question stays
|
|
509
598
|
readable with `expectsReply: false`, so old questions never advertise an active reply id.
|
|
599
|
+
Tool outcomes distinguish `answered` from unavailable, timed-out, cancelled and failed requests;
|
|
600
|
+
delivery evidence is never approval. Headless questions remain nonblocking and unresolved, allowing
|
|
601
|
+
only independent already-authorized work or a blocked/partial handoff. Pre-aborted calls perform no
|
|
602
|
+
bus action, and replies racing cancellation are not reported as answers.
|
|
510
603
|
- **Sibling peer comm** — a strategy opts a run in via `AgentRunSpec.peers` (gated by `canUseBus`). The
|
|
511
604
|
child gets `contact_peer` (`list`/`send`, ONE-WAY so peers can never deadlock; per-engine-instance
|
|
512
605
|
scoping; a 20-send budget and an 8,000-character body limit, enforced before consuming a send). The
|
|
@@ -524,11 +617,19 @@ events and cannot route, reply, steer, or otherwise control agents.
|
|
|
524
617
|
notifier, f9, peek) is unchanged by construction. Off ⇒ the host never starts and the child spawns
|
|
525
618
|
byte-identical to pre-broker pi-persona. Ask failures echo the originating `msgId` so the client
|
|
526
619
|
rejects the right request immediately, and a client cancellation propagates only to that connection's
|
|
527
|
-
own host ask.
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
620
|
+
own host ask. A one-way send gets a UUID-correlated host receipt; `delivered: true` means admitted
|
|
621
|
+
to the bus, not read or acted on by the peer. Each child is limited to 64 pending receipts with a
|
|
622
|
+
maximum 10-second wait. Abort, disconnect, a full or missing inbox, or a lost receipt reports
|
|
623
|
+
`delivered: false`. It never waits for a recipient answer or approval and does not replay.
|
|
624
|
+
`extension/broker-host.ts`'s `SupervisorBroker` owns the lifecycle: it starts
|
|
625
|
+
the host lazily on the first child-engine build and warns once on a failed bind. Children built while
|
|
626
|
+
it's down spawn without a bus endpoint, so they never burn connect backoff against a dead socket;
|
|
627
|
+
the next build retries; `/doctor` surfaces its state on one line. A dropped client connection flips the
|
|
531
628
|
child-side bridge to `⇄ offline` and fails a fresh ask fast rather than hanging.
|
|
629
|
+
A connected child exposes `contact_supervisor` when `canUseBus` is allowed; `contact_peer` also
|
|
630
|
+
requires the run's peer opt-in. Granted custom tool names join a nonempty plain allowlist so Pi's
|
|
631
|
+
host filter does not hide them. Explicit `tools: []` and exclusions remain restrictive. A denied
|
|
632
|
+
semantic bus does not remove supervisor runtime steering or an independently scoped result reader.
|
|
532
633
|
|
|
533
634
|
### Presentation is a projection, not another comm plane
|
|
534
635
|
|
|
@@ -627,6 +728,30 @@ for the supervisor and for explicit retrieval; the default TUI projection is del
|
|
|
627
728
|
unreadable versions. Session-file deletion owns log deletion; ordinary shutdown saves diagnostics,
|
|
628
729
|
not deletes them. Operator-only expandable log entries do not create model context or extra wakes.
|
|
629
730
|
|
|
731
|
+
**Terminal result archive** (`src/results/`, `src/extension/results.ts`) applies the same
|
|
732
|
+
ownership model to settled agent work: every terminal delegate/council/flow outcome (and each final
|
|
733
|
+
SDK leg result, after any model recovery) is projected — once, idempotently by immutable origin —
|
|
734
|
+
into a versioned session entry with an opaque `result:<UUID>` reference, independent from runtime
|
|
735
|
+
run ids. Budgets: 128 entries, 8 MiB cumulative serialized payload per owning session file, 512 KiB
|
|
736
|
+
per record; bodies cap at 262,144 UTF-16 units with honest truncation metadata. Unknown, malformed
|
|
737
|
+
and duplicate history still consumes quota; unaccountable history disables new persistence instead
|
|
738
|
+
of resetting it. A fork/copy is a new owning file charged only for the entries it actually carries.
|
|
739
|
+
`storage: "session-entry"` means the host accepted the entry — never an fsync guarantee; memory-only
|
|
740
|
+
retention is labelled as such. Retrieval is read-only: `intercom results` lists, `intercom result
|
|
741
|
+
{ ref, offset, limit }` pages (8,000 default / 16,000 max units, surrogate-safe), with no respawn,
|
|
742
|
+
rebilling, collection or extra wake; reading a page never returns the whole run hidden in metadata.
|
|
743
|
+
A saved result is not a live worker: the restore notice says runtime handles are gone while retained
|
|
744
|
+
reports remain readable. Archive listeners belong to the extension factory/tracker lifetime and are
|
|
745
|
+
installed exactly once. `session_shutdown` revokes that session's archive owner and clears its
|
|
746
|
+
captures. A later activation creates a fresh owner and capture context before the ordinary completion
|
|
747
|
+
listener observes terminal results for that activation; a late callback bound to the old owner cannot
|
|
748
|
+
append into the new session.
|
|
749
|
+
|
|
750
|
+
Flow reference handoff needs durable session-entry storage when a phase's result feeds a dependent
|
|
751
|
+
phase. In a no-session run, a source phase can finish with a memory-only receipt, but the dependent's
|
|
752
|
+
reader preflight rejects it before that dependent is spawned. Use a persisted session for reference
|
|
753
|
+
handoff across phases, or use inline handoff when no durable reader is available.
|
|
754
|
+
|
|
630
755
|
This is a UI invariant only: truncating a collapsed card must never be confused with truncating the
|
|
631
756
|
underlying result or changing a strategy's contract.
|
|
632
757
|
|
|
@@ -640,17 +765,22 @@ and message peer-to-peer, with no parent/child relationship. (intercom = interna
|
|
|
640
765
|
external comm.)
|
|
641
766
|
|
|
642
767
|
Exocom supplies fenced one-way postcards and presence; chat `message` never mutates shared work
|
|
643
|
-
state.
|
|
644
|
-
|
|
645
|
-
`
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
768
|
+
state. `src/exocom/ledger.ts` stores claims, questions, answers and progress; `src/exocom/collaboration.ts`
|
|
769
|
+
adds voluntary tasks, optional offers and explicit evidence excerpts. Tool adapters live in
|
|
770
|
+
`src/tools/exocom.ts`, `src/tools/exocom-collaboration.ts`, and `src/tools/exocom-profile.ts`.
|
|
771
|
+
`exocom_ask` resolves its target through the live registry to a raw session id, never a display name.
|
|
772
|
+
Attribution is resolved by `from_session`, never the envelope's self-reported `from_name`.
|
|
773
|
+
|
|
774
|
+
A pending ask gates the participant to `exocom_answer`/`exocom_decline`, `exocom_name` identity
|
|
775
|
+
metadata, `exocom_status`, explicit collaboration reads (`exocom_task` list/get, `exocom_publish`
|
|
776
|
+
read, `exocom_profile` get), and read-only filesystem tools. Profile `set`/`clear` and all other
|
|
777
|
+
mutations remain unavailable until the ask is settled. Naming does not settle an ask. The bounded
|
|
778
|
+
`pendingAskPrompt` explains the outstanding obligation. `exocom_wait` is non-blocking. Clean shutdown
|
|
779
|
+
releases this session's claims/asks; if that best-effort write fails, the vanished owner is pruned on
|
|
780
|
+
the next ledger transaction (live registry sessions are the lease), and `expireWaiters` wakes any
|
|
781
|
+
waiter left behind instead of leaving `exocom_wait` armed forever. This gate is cooperative
|
|
782
|
+
coordination for participating local Pi processes — not filesystem authorization, not isolation from
|
|
783
|
+
another same-user process, and not a delegate/council or task/run replacement.
|
|
654
784
|
|
|
655
785
|
- **Opt-in, OFF by default.** `PI_PERSONA_EXOCOM=1` or bare `--exocom` joins the current workspace;
|
|
656
786
|
`--exocom=Ab0T` joins an existing scope by its exact, case-sensitive four-character Base62 alias
|
|
@@ -677,18 +807,31 @@ replacement.
|
|
|
677
807
|
standing brief, and `/exocom` label a peer as same-workspace or external without exposing its
|
|
678
808
|
absolute `cwd`. External peers are full Pi instances that can inspect files in their own workspace;
|
|
679
809
|
paths are never implicitly shared.
|
|
680
|
-
- **Postcards
|
|
681
|
-
reply is another `exocom_send` with `in_reply_to` set. `target: "*"` broadcasts
|
|
682
|
-
best-effort
|
|
683
|
-
`
|
|
684
|
-
|
|
685
|
-
|
|
810
|
+
- **Postcards, asks and offers have different obligations.** `exocom_send` returns a `msg_id`
|
|
811
|
+
immediately; a reply is another `exocom_send` with `in_reply_to` set. `target: "*"` broadcasts
|
|
812
|
+
postcards best-effort. `exocom_ask` commits a targeted question that must be answered or declined;
|
|
813
|
+
`exocom_wait` arms a bounded idle wake rather than blocking. `exocom_offer` is an optional,
|
|
814
|
+
expiring invitation to consider an open task, targeted to one peer or the pool; it creates no ask,
|
|
815
|
+
no answer obligation and no work authorization. Its default lifetime is one hour, capped at 24 hours.
|
|
816
|
+
Signed immediate-wake frames are signals; the ledger remains authoritative when delivery is deferred.
|
|
817
|
+
- **Task generations and atomic pickup.** `exocom_task create` records a voluntary objective and
|
|
818
|
+
declared write set; creation neither launches work nor claims paths. The task's immutable
|
|
819
|
+
`task_id` is its creation frame's `msg_id`; later take/progress/finish, offer and publish actions
|
|
820
|
+
carry both `work_key` and `task_id`, so reusing a key cannot attach a delayed action to a newer
|
|
821
|
+
task. A write-set task's pickup and overlapping write claim commit together under the ledger lock;
|
|
822
|
+
competing overlapping pickups are rejected. Read-only tasks can be taken by a peer in another
|
|
823
|
+
workspace scope. A task whose owning registry lease disappears becomes stale and cannot be taken
|
|
824
|
+
over automatically. `exocom_task list` is paginated; `get` returns the complete bounded task detail,
|
|
825
|
+
including the exact write set. `exocom_publish` attaches an explicitly authored bounded excerpt to
|
|
826
|
+
one task generation; discovery pages titles and message ids, and the full excerpt is read by id.
|
|
827
|
+
These excerpts do not grant access to a peer's terminal-result archive.
|
|
686
828
|
- **Cross-workspace writes are advisory-only in v1.** A repository-relative claim path has meaning
|
|
687
829
|
only in the workspace that owns the selected scope, so a member whose home workspace differs from
|
|
688
|
-
the scope cannot `exocom_claim` (the tool and
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
unrelated repositories are never compared as
|
|
830
|
+
the scope cannot `exocom_claim` or create/pick up a write-set task (the tool and signed-frame
|
|
831
|
+
transaction both enforce the scope). It can still read shared tasks/evidence, take read-only tasks,
|
|
832
|
+
send postcards, ask, answer/decline, and release its own asks. Multi-repository write coordination
|
|
833
|
+
would need a versioned resource namespace; paths from unrelated repositories are never compared as
|
|
834
|
+
if they named the same files.
|
|
692
835
|
- **Reply routing is session-stable.** `exocom_list` shows human display names (`name`/`name#2`);
|
|
693
836
|
sends and inbound reply hints use `name@<96-bit session hash>`, so a retained qualified target
|
|
694
837
|
stays valid after a rename. Ambiguous suffix matches fail closed rather than guess. The
|
|
@@ -727,15 +870,23 @@ replacement.
|
|
|
727
870
|
double-trigger a turn. An exchange's LENGTH is instead bounded prompt-side: the per-turn peer brief
|
|
728
871
|
(`core/brief.ts`) carries a relevance bound — send only what changes what someone does — deliberately
|
|
729
872
|
not a round count, since a counter can't see whether a round still serves the work.
|
|
730
|
-
- **
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
`
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
and
|
|
737
|
-
|
|
738
|
-
|
|
873
|
+
- **Fifteen tools are lazy and fail closed.** `EXOCOM_TOOL_NAMES` lists `exocom_list`, `exocom_send`,
|
|
874
|
+
`exocom_name`, `exocom_claim`, `exocom_ask`, `exocom_answer`, `exocom_decline`, `exocom_wait`,
|
|
875
|
+
`exocom_release`, `exocom_progress`, `exocom_status`, `exocom_task`, `exocom_offer`,
|
|
876
|
+
`exocom_publish`, and `exocom_profile`. Each tool rechecks effective persona capabilities at call
|
|
877
|
+
time; the live accessor and active-tool set also deny calls once the plane stops, since Pi has no
|
|
878
|
+
dynamic tool-unregister API. A joined foreign-scope peer has no `exocom_claim`. Admission requires
|
|
879
|
+
`canUseBus` and at least one callable ask closer, because every published peer may receive a durable
|
|
880
|
+
ask and must retain a way to settle it.
|
|
881
|
+
- **Collaboration and profile retention are bounded.** The ledger retains at most 64 tasks, 64 offers,
|
|
882
|
+
and 64 evidence excerpts with a 128 KiB aggregate collaboration-state budget. Task descriptions cap
|
|
883
|
+
at 2,048 characters; write sets at 64 paths of 256 characters each; progress/finish notes at 4,096.
|
|
884
|
+
Evidence titles cap at 160 characters and excerpts at 4,096 characters / 8,192 UTF-8 bytes. Finished
|
|
885
|
+
or stale tasks remain for up to one hour; expired offers remain for up to one hour after expiry, and
|
|
886
|
+
evidence for up to one hour after publication. Active tasks remain while their owner lease is live.
|
|
887
|
+
`exocom_profile` contains up to eight self-described capability labels (48 characters each) and
|
|
888
|
+
optional `available`, `busy` or `away` status. Missing status is unknown. Profiles exist only for
|
|
889
|
+
their live Exocom session and never prove skills, permission or availability.
|
|
739
890
|
- **Inbound delivery is bounded without loss.** Each message is injected under the same byte cap
|
|
740
891
|
whether plain text or an artifact descriptor. Bursts stay FIFO-queued; each rate-limited wake drains
|
|
741
892
|
as many whole messages as fit, leaving the rest queued. A presentation cap on the collapsed card
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Supervised workers and independent colleagues
|
|
2
|
+
|
|
3
|
+
Use **Intercom** for workers launched by this session. Use **Exocom** for independent Pi sessions
|
|
4
|
+
that choose to collaborate. An Exocom peer is responsible to its own user; messages and shared tasks
|
|
5
|
+
do not make it a subordinate or grant it permission to act.
|
|
6
|
+
|
|
7
|
+
## Supervising a leg
|
|
8
|
+
|
|
9
|
+
Choose `thinking` independently of `model` in a delegate or roster entry. The runtime preserves an
|
|
10
|
+
explicit provider choice and reports requested and observed thinking separately. A compact tree
|
|
11
|
+
label is only a projection; it does not prove the provider's internal reasoning effort.
|
|
12
|
+
|
|
13
|
+
Use `intercom peek` to inspect a particular uncertainty, `steer` to queue an instruction, and `stop`
|
|
14
|
+
to stop work. A steering receipt proves acceptance, not that the worker acted on it. Interactive
|
|
15
|
+
completions wake the supervisor automatically; end the turn instead of polling or sleeping.
|
|
16
|
+
|
|
17
|
+
`intercom results` lists terminal reports. `intercom result { ref, offset, limit }` reads a bounded
|
|
18
|
+
page after reload. A `result:<UUID>` identifies stored evidence, not a live worker. The receipt says
|
|
19
|
+
whether storage is durable or memory-only and whether retention truncated the original report.
|
|
20
|
+
Paging to the end cannot restore text that exceeded the retention ceiling.
|
|
21
|
+
|
|
22
|
+
An unanswered decision remains unresolved. Timeout, cancellation, disconnection and headless delivery
|
|
23
|
+
never imply approval. Continue only independent work already authorized, or return a partial result.
|
|
24
|
+
|
|
25
|
+
## Sharing an objective without a leader
|
|
26
|
+
|
|
27
|
+
Start participating sessions with `--exocom` in the same workspace, or join an existing scope with
|
|
28
|
+
its code as described in [the user guide](EXPERIENCE.md#work-with-another-pi-through-exocom).
|
|
29
|
+
Use `exocom_list` to find stable qualified targets and self-reported profiles. A profile's capabilities
|
|
30
|
+
and availability are suggestions for choosing a colleague, not verified skills, reservations or tool
|
|
31
|
+
permissions. Missing availability is **unknown**. Profiles disappear when the Exocom plane stops.
|
|
32
|
+
|
|
33
|
+
1. Describe an independent slice with `exocom_task { action: "create", work_key, description,
|
|
34
|
+
write_set }`. An empty write set means read-only work. Creation is an invitation to collaborate,
|
|
35
|
+
not an assignment. Keep tasks within the users' authorized objective.
|
|
36
|
+
2. Inspect tasks using `exocom_task { action: "list", offset, limit, detail }`. Every task carries a
|
|
37
|
+
`task_id`, the immutable creation message ID, alongside its human-selected `work_key`. Before
|
|
38
|
+
taking a write task, use `exocom_task { action: "get", work_key, task_id }` to inspect the complete
|
|
39
|
+
bounded write set; list previews may shorten paths.
|
|
40
|
+
3. Optionally send `exocom_offer { work_key, task_id, target, expiresInMinutes }`. Omitting target
|
|
41
|
+
records a pool invitation for discovery; it does not broadcast a model wake. A targeted offer uses
|
|
42
|
+
the existing signed wake path. Default expiry is one hour, maximum 24 hours. It never creates a
|
|
43
|
+
response obligation; expiration is not acceptance or rejection.
|
|
44
|
+
4. A colleague voluntarily calls `exocom_task { action: "take", work_key, task_id }`. Pickup and the
|
|
45
|
+
declared file claim commit atomically; a second pickup or overlapping claim fails. A stale task ID
|
|
46
|
+
cannot act on a newly created task that reused the same key. Foreign-workspace members may take
|
|
47
|
+
read-only tasks, but cannot create or take write tasks in the joined scope.
|
|
48
|
+
5. The taker posts `progress` with a bounded `note`, then `finish` with `outcome: "done"` or
|
|
49
|
+
`"abandoned"` and a note, always supplying the same `work_key` and `task_id`. The latest progress
|
|
50
|
+
note is retained. Finishing releases the task's claim; it never merges or approves code.
|
|
51
|
+
|
|
52
|
+
Task division does not need leader election. Each task has one taker and its own claim. Peers choose
|
|
53
|
+
separate open tasks; availability and execution remain up to each session. A peer may propose a different split, decline by doing
|
|
54
|
+
nothing with an offer, or discuss a conflict in a postcard. If a decision belongs to a user, bring it
|
|
55
|
+
to that user; there is no automatic arbitration or authority transfer between peers.
|
|
56
|
+
|
|
57
|
+
Use `exocom_ask` only when a peer's answer must gate the next action. Unlike an offer, an ask constrains
|
|
58
|
+
its addressee until `exocom_answer` or `exocom_decline`. Identity metadata and explicit read actions
|
|
59
|
+
remain available. `exocom_wait` arms a nonblocking wake; it does not block the current tool call.
|
|
60
|
+
|
|
61
|
+
## Publishing evidence
|
|
62
|
+
|
|
63
|
+
`exocom_publish { action: "publish", work_key, task_id, title, excerpt }` copies an explicit excerpt
|
|
64
|
+
into the shared ledger. A title is at most 160 characters; an excerpt is at most 4,096 UTF-16 units
|
|
65
|
+
and 8,192 UTF-8 bytes. No tool follows paths or archive references in that text. To share a retained
|
|
66
|
+
leg report, read the needed page through Intercom, select the relevant excerpt, then publish that text.
|
|
67
|
+
|
|
68
|
+
Discover evidence with `exocom_publish { action: "read", work_key, offset, limit }`, then retrieve one
|
|
69
|
+
excerpt with `msg_id`. Publisher identity is resolved from the registry or retained session ID, never
|
|
70
|
+
the envelope's claimed display name. The excerpt remains fenced peer data. Verify its claims before
|
|
71
|
+
using it; a peer's `done` means that peer finished its task, not that checks passed or a reviewer approved.
|
|
72
|
+
|
|
73
|
+
## Failure, retention and controls
|
|
74
|
+
|
|
75
|
+
`exocom_status` and `/exocom` show claims, questions, shared task states and invitations. Task lists
|
|
76
|
+
default to 10 entries (maximum 20); detailed pages default to 5 and allow at most 5. Evidence-title pages are bounded.
|
|
77
|
+
Tool cards collapse long content and expand using Pi's normal keybinding; this never changes the
|
|
78
|
+
stored evidence or the text supplied to the model.
|
|
79
|
+
|
|
80
|
+
Loss of a task owner's registry lease or explicit release marks unfinished work **stale**. It never
|
|
81
|
+
silently reopens the task. Inspect the work and create a newly scoped task if reassignment is needed.
|
|
82
|
+
Clean shutdown releases both write and read-only task ownership. `exocom_release` intentionally
|
|
83
|
+
releases this session's current ownership and outbound asks on a work key.
|
|
84
|
+
|
|
85
|
+
The ledger retains at most 64 tasks, 64 offers and 64 evidence excerpts in a 128 KiB collaboration
|
|
86
|
+
budget, including lifecycle headroom. Terminal/stale tasks remain visible for the existing one-hour
|
|
87
|
+
result retention window; offers remain until one hour after expiry, evidence for one hour after
|
|
88
|
+
publication. Removing a task also removes its related offers/evidence. Live open/taken tasks are not
|
|
89
|
+
evicted to admit new work. Limits fail explicitly; there is no silent ownership loss or global archive
|
|
90
|
+
access. Transactions perform durable cleanup; reads project current expiry without starting workers.
|
|
91
|
+
|
|
92
|
+
## Contract boundaries
|
|
93
|
+
|
|
94
|
+
| Boundary | Required meaning | Enforcement |
|
|
95
|
+
|---|---|---|
|
|
96
|
+
| Intercom vs Exocom | Worker supervision vs independent peers | Existing capability gates, attribution and distinct transports |
|
|
97
|
+
| Delivery vs decision | Accepted/delivered is not answered, acted or approved | Explicit outcomes and unresolved guidance |
|
|
98
|
+
| Task identity | A reused key cannot retarget old intent | Creation `task_id` checked inside the ledger transaction and signed on frames |
|
|
99
|
+
| Pickup and ownership | One taker, claim admitted together, no overlap | Atomic ledger transaction; scope/capability admission inside its lock |
|
|
100
|
+
| Optional offer vs ask | Invitations never gate; targeted asks do | Separate records, tool actions and pending-ask gate |
|
|
101
|
+
| Evidence | Explicit bounded copied text, no cross-session archive access | Schema validation, ledger budgets, registry attribution and fences |
|
|
102
|
+
| Persistence | Versioned authoritative state must not become an empty ledger on error | Snapshot validation, retained-message idempotency and lifecycle budget reservation |
|
|
103
|
+
| Retention | Stale is visible, expiry never implies takeover | First-stale timestamp, bounded retention, fresh generation on recreation |
|
|
104
|
+
| Profiles and views | Self-report/projection cannot grant authority | Separate presence metadata and runtime-owned status |
|
|
105
|
+
| Flow references | Only compatible, successful archived phases can resume | Journal-version/status checks, scoped reads, explicit unavailable/truncated outcomes |
|
|
106
|
+
|
|
107
|
+
These are runtime contracts. The existing `outputContract` facility validates a worker's final JSON
|
|
108
|
+
report with scalar fields; a model-written `proof` is not a command receipt. It is not the right place
|
|
109
|
+
to encode task ownership or ledger transitions. Nested report schemas and host-executed acceptance
|
|
110
|
+
receipts remain separate product work; this collaboration protocol does not claim to implement them.
|
package/docs/EXPERIENCE.md
CHANGED
|
@@ -80,6 +80,10 @@ receipt previews the message that was actually queued; expand it (`Ctrl+O` by de
|
|
|
80
80
|
configured key) to read the full message and diagnostic run ID. Aliases are display names, not
|
|
81
81
|
addresses: tools still route by run ID, and duplicate aliases show a secondary ID to distinguish them.
|
|
82
82
|
Saved launch/control receipts keep their display names even after the runtime tracker is gone.
|
|
83
|
+
Council and flow launch receipts also retain each participant's model and requested effort, with
|
|
84
|
+
phase and auxiliary roles identified. Expand the card for the full panel. A pending model choice
|
|
85
|
+
is marked explicitly; launch selections are not proof of the effective model or effort after
|
|
86
|
+
setup, provider recovery or Pi's effort adjustment. Later settings changes do not rewrite history.
|
|
83
87
|
|
|
84
88
|
A truncated message includes a message id. The supervisor can retrieve its retained body with
|
|
85
89
|
`intercom { action: "message", messageId: "m1" }`. A worker's final report has a run id instead:
|
|
@@ -92,14 +96,35 @@ outcomes that make sense without remembering those names.
|
|
|
92
96
|
|
|
93
97
|
## Work with another Pi through Exocom
|
|
94
98
|
|
|
95
|
-
Use Intercom for this supervisor's workers and Exocom for independent Pi sessions.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
Use Intercom for this supervisor's workers and Exocom for independent Pi sessions. Use
|
|
100
|
+
`exocom_list` or `/exocom` to find the intended session-qualified target before sending a postcard
|
|
101
|
+
or asking a question.
|
|
102
|
+
|
|
103
|
+
`exocom_task create` records a voluntary objective and declared write paths; it does not start
|
|
104
|
+
another Pi or require a peer to accept. The receipt gives a `task_id`; keep it with the
|
|
105
|
+
`work_key` when inviting, taking, reporting progress, finishing or publishing evidence. A work key
|
|
106
|
+
can be reused after retention, so the task id identifies the exact generation. The peer chooses
|
|
107
|
+
whether to take the task. Write-set pickup and its advisory claim happen atomically, but a claim is
|
|
108
|
+
not an OS file lock. A peer that joined from another workspace can take read-only tasks, not tasks
|
|
109
|
+
that claim paths in the selected workspace. Use `exocom_task get` to inspect the complete write set;
|
|
110
|
+
the paginated list is a summary.
|
|
111
|
+
|
|
112
|
+
Use `exocom_offer` for a non-blocking invitation that expires by itself; it creates no obligation
|
|
113
|
+
to answer. Use `exocom_ask` when a specific decision is needed and the recipient must answer or
|
|
114
|
+
decline. A pending ask temporarily permits settlement, `exocom_name`, `exocom_status`, explicit
|
|
115
|
+
read actions (task list/get, evidence read and profile get), and read-only filesystem tools.
|
|
116
|
+
Changing `exocom_profile` is not allowed while an ask is pending. Changing the name does not answer
|
|
117
|
+
the ask.
|
|
118
|
+
|
|
119
|
+
Share only a bounded excerpt through `exocom_publish`; it does not grant access to another session's
|
|
120
|
+
terminal-result archive. Peer text is untrusted and appears fenced with registry-resolved attribution.
|
|
121
|
+
Capability labels and availability in `exocom_profile` are self-reported metadata, not verified
|
|
122
|
+
skills or permissions. Exchange decisions and evidence when they affect the work; acknowledgement-only
|
|
123
|
+
conversations add noise. If a peer joined from another workspace, agree which workspace owns each
|
|
124
|
+
artifact.
|
|
125
|
+
|
|
126
|
+
See the [cross-session collaboration guide](./COLLABORATION.md) for command shapes, retention limits,
|
|
127
|
+
and detailed examples.
|
|
103
128
|
|
|
104
129
|
`exocom_status` (or `/exocom`) shows who owns what right now: your open claims, a peer's open
|
|
105
130
|
claims, and any asks waiting on either side. Claims are advisory, not a lock — if you write into a
|
package/docs/README.md
CHANGED
|
@@ -16,6 +16,8 @@ introduced a behavior. An `Unreleased` entry is not yet part of a published rele
|
|
|
16
16
|
- Vendor-neutral observer contract, privacy boundary, persistence, and consumer conformance.
|
|
17
17
|
- [`EXPERIENCE.md`](./EXPERIENCE.md)
|
|
18
18
|
- Choosing a persona, supervising background work, and collaborating through Exocom.
|
|
19
|
+
- [`COLLABORATION.md`](./COLLABORATION.md)
|
|
20
|
+
- Intercom supervision, voluntary peer tasks, invitations, evidence, retention and runtime contracts.
|
|
19
21
|
- [`MONITORS.md`](./MONITORS.md)
|
|
20
22
|
- Clock snapshots, timers, and background event sources that wake the supervisor.
|
|
21
23
|
- [`REFERENCE.md`](./REFERENCE.md)
|
|
@@ -28,6 +30,16 @@ introduced a behavior. An `Unreleased` entry is not yet part of a published rele
|
|
|
28
30
|
- **For behavior-layer changes:** consult `SPINE.md` and `src/core` / `src/persona/spine.ts` together.
|
|
29
31
|
- **For telemetry producers or consumers:** start with `TELEMETRY.md` and its conformance corpus.
|
|
30
32
|
|
|
33
|
+
## Research and verification records
|
|
34
|
+
|
|
35
|
+
- [`The pi-subagents comparison`](https://github.com/AeonDave/pi-persona/blob/main/docs/reviews/2026-10-24-pi-subagents.md) records the bounded source
|
|
36
|
+
study and links its evidence and source notes.
|
|
37
|
+
- [`Current-host cleanup and final requalification`](https://github.com/AeonDave/pi-persona/blob/main/docs/reviews/2026-10-25-current-host-cleanup.md)
|
|
38
|
+
is the latest qualification report and links the preceding implementation review for its distinct
|
|
39
|
+
behavioral and live-run evidence.
|
|
40
|
+
- [`ROADMAP.md`](https://github.com/AeonDave/pi-persona/blob/main/ROADMAP.md) tracks the remaining work, including the unqualified
|
|
41
|
+
[compaction proposal](https://github.com/AeonDave/pi-persona/blob/main/docs/plans/2026-10-20-compaction-evaluation-proposal.md).
|
|
42
|
+
|
|
31
43
|
## Documentation maintenance notes
|
|
32
44
|
|
|
33
45
|
When touching this folder:
|