pi-session-mcp 0.8.0 → 0.8.2
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/.pi-session-mcp-cli/doctor.js +14 -7
- package/.pi-session-mcp-cli/override-lock.js +125 -0
- package/.pi-session-mcp-cli/setup-platform.js +195 -0
- package/.pi-session-mcp-cli/skill-customize.js +131 -46
- package/.pi-session-mcp-cli/skill-library.js +72 -4
- package/CHANGELOG.md +159 -0
- package/CONTRIBUTING.md +5 -3
- package/README.md +5 -3
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +14 -7
- package/dist/override-lock.d.ts +34 -0
- package/dist/override-lock.js +125 -0
- package/dist/pi-adapter.d.ts +6 -4
- package/dist/sdk-pi-adapter.js +28 -5
- package/dist/session-registry.d.ts +8 -6
- package/dist/session-registry.js +3 -2
- package/dist/setup-ownership.d.ts +4 -1
- package/dist/setup-ownership.js +6 -3
- package/dist/setup-skills.d.ts +46 -1
- package/dist/setup-skills.js +349 -78
- package/dist/setup.js +21 -4
- package/dist/skill-customize.d.ts +19 -1
- package/dist/skill-customize.js +131 -46
- package/dist/skill-library.d.ts +46 -1
- package/dist/skill-library.js +72 -4
- package/docs/acceptance.md +747 -26
- package/docs/adr/0007-profile-skill-selection.md +11 -0
- package/docs/architecture.md +1 -1
- package/docs/client-setup-design.md +157 -24
- package/docs/client-setup.md +38 -7
- package/docs/doctor-design.md +1 -1
- package/docs/installation.md +8 -2
- package/docs/pi-extensions-design.md +11 -0
- package/docs/releasing.md +25 -10
- package/docs/reviews/2026-10-13-post-0.8.1-audit.md +393 -0
- package/docs/roadmap.md +127 -0
- package/docs/skills.md +173 -16
- package/docs/sprints/2026-10-13-s1-correctness.md +355 -0
- package/docs/sprints/2026-10-13-s2-evidence.md +431 -0
- package/docs/sprints/2026-10-13-s3-live-usage.md +267 -0
- package/docs/sprints/2026-10-13-s4-decision-inputs.md +241 -0
- package/docs/threat-model.md +3 -3
- package/docs/tool-contracts.md +20 -2
- package/package.json +1 -1
- package/skills/pi-session-orchestration/SKILL.md +8 -4
|
@@ -215,3 +215,14 @@ the controller skill that `setup --apply` installs, so the next release after
|
|
|
215
215
|
0.7.1 is 0.8.0. The change is recorded under
|
|
216
216
|
`## Unreleased` in the changelog. Implementing it does not itself release or
|
|
217
217
|
bump the version.
|
|
218
|
+
|
|
219
|
+
## Note of 2026-10-12: fingerprints, locking and recovery after 0.8.0
|
|
220
|
+
|
|
221
|
+
The decisions above stand. Three details changed after 0.8.0 (issues #50, #51
|
|
222
|
+
and #52): skill tree fingerprints are version 2 (`v2:` values; a 0.8.0 value
|
|
223
|
+
counts only when a trusted reference proves it), `--apply --customize-skill`
|
|
224
|
+
runs under a kernel lock on the override directory and writes the bases file as
|
|
225
|
+
schema version 2, and an interrupted update of an installed copy is settled by
|
|
226
|
+
the next `--apply`, `--remove` or `--rollback`. See [Pi
|
|
227
|
+
skills](../skills.md#upgrade-from-080) and the [setup lifecycle
|
|
228
|
+
design](../client-setup-design.md#skill-installation).
|
package/docs/architecture.md
CHANGED
|
@@ -71,7 +71,7 @@ The registry owns public UUIDs, lifecycle state, the turn read model, background
|
|
|
71
71
|
|
|
72
72
|
## Lifecycle and public data
|
|
73
73
|
|
|
74
|
-
`pi_session_prompt` creates a provisional turn identity, invokes Pi preflight, and only after accepted preflight publishes `{session, turn}`; rejected preflight leaves no orphan turn. Completion returns to `idle`; failure is generic. Each active session retains at most 20 turns. A turn is `running`, `completed`, `failed`, or `aborted`; its public view contains IDs, timestamps, and terminal `completedAt`. Completed text is normalized and capped at 64 KiB UTF-8 with Unicode-safe truncation. It can contain model-visible workspace data and remains potentially sensitive.
|
|
74
|
+
`pi_session_prompt` creates a provisional turn identity, invokes Pi preflight, and only after accepted preflight publishes `{session, turn}`; rejected preflight leaves no orphan turn. Completion returns to `idle`; failure is generic. Each active session retains at most 20 turns. A turn is `running`, `completed`, `failed`, or `aborted`; its public view contains IDs, timestamps, and terminal `completedAt`. While a turn runs, its `updatedAt` is the time of the last observed public SDK activity (assistant message and tool execution events, content-free) and is a liveness lower bound only; see [Tool contracts](tool-contracts.md). Completed text is normalized and capped at 64 KiB UTF-8 with Unicode-safe truncation. It can contain model-visible workspace data and remains potentially sensitive.
|
|
75
75
|
|
|
76
76
|
Session metadata contains only whitelisted values: public session ID, workspace alias, execution-profile alias, derived permission `profile`, post-verified provider/model/effective thinking level, lifecycle state, and timestamps. Capability metadata uses a separate whitelist: server name/version, loaded configuration fingerprint/reload policy, workspace aliases, and configured profile fields, including the selected skill aliases. Neither view contains workspace paths, full model objects, SDK session IDs, auth sources, credential paths, headers, base URLs, prompts, transcripts, or raw provider errors.
|
|
77
77
|
|
|
@@ -79,7 +79,12 @@ findings make the overall result `failed`, like any other finding.
|
|
|
79
79
|
bounded reader: regular files only, at most 32 files, 1 MiB and 8 levels, no
|
|
80
80
|
links or special files inside the tree, entries whose name starts with `.`
|
|
81
81
|
skipped. A missing or unusable source is `skill_source_invalid`. The fingerprint
|
|
82
|
-
is a SHA-256 over
|
|
82
|
+
is version 2 (`src/skill-library.ts`): `v2:` and a SHA-256 over a domain and
|
|
83
|
+
version tag, the file count, and per file in path byte order the length-prefixed
|
|
84
|
+
UTF-8 path and the length-prefixed content. Version 1 (0.8.0) hashed
|
|
85
|
+
`path NUL content NUL` per file and was ambiguous, because content may contain
|
|
86
|
+
NUL bytes. A version 1 value never proves identity alone (see *Legacy records*
|
|
87
|
+
below).
|
|
83
88
|
|
|
84
89
|
**Slots.** A slot is the skill directory a client reads:
|
|
85
90
|
|
|
@@ -97,20 +102,86 @@ alias `skill:pi-session-orchestration`. A project slot uses
|
|
|
97
102
|
of a SHA-256 over the project's real path: an opaque identity, not a path, so a
|
|
98
103
|
`--remove` in another project never touches this project's copy. The `pending`
|
|
99
104
|
phase is written before the first filesystem change and promoted to `owned`
|
|
100
|
-
after a post-install inspection.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
after a post-install inspection. Skill records hold `v2:` values in
|
|
106
|
+
`fingerprint` and `replaces`; registration records keep their 64-hex snapshot
|
|
107
|
+
hash. During an update, the pending record also keeps the fingerprint of the
|
|
108
|
+
copy it replaces (`replaces`), so an interrupted update leaves a copy that
|
|
109
|
+
setup still recognizes as its own: the next `--apply` finishes the update and
|
|
110
|
+
`--remove` deletes it. A later run that finds a `pending` record whose slot
|
|
111
|
+
already equals the source promotes it.
|
|
105
112
|
|
|
106
113
|
**Atomic copy.** Setup creates missing parent directories (mode `0755`), stages
|
|
107
|
-
the tree in a
|
|
114
|
+
the tree in a staging directory in the slot's parent (files `0644`, each file
|
|
108
115
|
and directory fsynced), and re-checks that the slot is unchanged
|
|
109
116
|
(fingerprint, device and inode). It then renames the exact old tree to a backup
|
|
110
|
-
name, renames the staged directory into place, inspects the result,
|
|
111
|
-
the backup
|
|
112
|
-
|
|
113
|
-
|
|
117
|
+
name, renames the staged directory into place, inspects the result, verifies
|
|
118
|
+
that the backup is still the exact old tree it moved, renames it to a trash
|
|
119
|
+
name and deletes the trash entry by entry. The slot is never edited in place.
|
|
120
|
+
|
|
121
|
+
**Intermediates and recovery.** The three intermediate directories are named
|
|
122
|
+
after the pending record's transaction id, in the slot's parent:
|
|
123
|
+
`.pi-session-mcp-<skill>-<transactionId>.staging`, `.backup` and `.trash`.
|
|
124
|
+
Recovery derives them from the persisted record and never searches. The order
|
|
125
|
+
is: pending record, staging, backup rename, install, verify backup, rename to
|
|
126
|
+
trash, delete the trash entry by entry, owned record. `--apply`, `--remove` and
|
|
127
|
+
`--rollback` settle a pending transaction first, inside the ownership
|
|
128
|
+
transaction, and only when the slot is empty, a link, or one of setup's two
|
|
129
|
+
copies:
|
|
130
|
+
|
|
131
|
+
- A trash directory is deleted only when its current contents are the
|
|
132
|
+
complete, unchanged replaced copy: its version 2 tree fingerprint equals the
|
|
133
|
+
replaced fingerprint in the pending record. A partial trash tree (for
|
|
134
|
+
example after an interruption during the entry-by-entry deletion, including
|
|
135
|
+
an empty one), a modified, added-to, foreign or otherwise unprovable tree,
|
|
136
|
+
and any trash when the record names no usable replaced fingerprint are
|
|
137
|
+
retained. A directory name, the transaction id or a structural check alone
|
|
138
|
+
never proves ownership of the contents. This deliberately replaces the
|
|
139
|
+
earlier automatic deletion of partial trash trees; continuing a partial
|
|
140
|
+
deletion would need additional durable evidence, and no journal is added
|
|
141
|
+
for it. The fingerprint covers the paths and contents of regular files, not
|
|
142
|
+
empty directories or file modes, so an empty directory or a mode change
|
|
143
|
+
inside an otherwise complete trash does not prevent its deletion; no file
|
|
144
|
+
content is lost that way.
|
|
145
|
+
- The backup must equal the replaced copy; the staging directory must equal
|
|
146
|
+
the target or be an interrupted write of it. A partially written staging
|
|
147
|
+
directory counts only when every file is a prefix-consistent part of the
|
|
148
|
+
current library tree and that tree is the record's target.
|
|
149
|
+
- `--apply` with an empty slot finishes forward with a complete staging
|
|
150
|
+
directory, else moves the verified backup back. `--remove` and `--rollback`
|
|
151
|
+
delete the verified intermediates and leave the slot to the removal.
|
|
152
|
+
- Anything modified or foreign, the trash directory included, stays and is
|
|
153
|
+
reported as `skill_recovery_retained` (divergent). The pending record and
|
|
154
|
+
the slot then stay unchanged, and nothing is reported as settled. A
|
|
155
|
+
retained trash stops recovery before any other intermediate is touched; a
|
|
156
|
+
retained backup or staging directory can follow the deletion of a proven
|
|
157
|
+
own trash. A failed update leaves its staging directory until the
|
|
158
|
+
next run settles it, and an unhealthy ownership fence retains intermediates
|
|
159
|
+
the same way.
|
|
160
|
+
- Operator settlement of a retained trash directory: inspect the
|
|
161
|
+
`.pi-session-mcp-<skill>-<transactionId>.trash` directory next to the slot,
|
|
162
|
+
move any own content elsewhere, delete that directory, and rerun the same
|
|
163
|
+
setup command.
|
|
164
|
+
- For a pending version 2 transaction, `--dry-run` reports
|
|
165
|
+
`skill_recovery_retained` (divergent, exit 1) whenever `--apply` would
|
|
166
|
+
retain an intermediate, using the same read-only classification as the
|
|
167
|
+
recovery, and otherwise `skill_recovery_planned`. A dry-run never writes.
|
|
168
|
+
- Random-name directories of 0.8.0 updates are never searched for or deleted;
|
|
169
|
+
an operator may remove them by hand.
|
|
170
|
+
- `--dry-run` and `--verify` do not report leftover intermediates.
|
|
171
|
+
|
|
172
|
+
**Legacy records.** A skill record whose `fingerprint` or `replaces` is a bare
|
|
173
|
+
64-hex value was written by 0.8.0. It names the slot only if a trusted
|
|
174
|
+
reference has that version 1 value and is byte-identical, by its version 2
|
|
175
|
+
fingerprint, to the installed copy. The references are the shipped skills of
|
|
176
|
+
0.8.0 (a fixed table in `src/skill-library.ts`) and the current library tree.
|
|
177
|
+
When proven, `--apply` migrates the record to version 2 and then updates the
|
|
178
|
+
copy as usual; `--remove` removes the proven copy. Otherwise `--dry-run`,
|
|
179
|
+
`--apply`, `--remove` and `--rollback` report `skill_legacy_unverified`
|
|
180
|
+
(divergent) and change neither the copy nor the record. The operator inspects
|
|
181
|
+
the copy and, if it is unchanged setup content that is no longer needed,
|
|
182
|
+
deletes it; an absent slot clears the record on the next operation. The
|
|
183
|
+
intermediates of an interrupted 0.8.0 update cannot be derived and are not
|
|
184
|
+
recovered.
|
|
114
185
|
|
|
115
186
|
**Divergence rules.** Setup never replaces or deletes, and reports
|
|
116
187
|
`skill_divergent` for:
|
|
@@ -118,6 +189,8 @@ The slot is never edited in place.
|
|
|
118
189
|
- an installed copy whose fingerprint differs from setup's own copy (the
|
|
119
190
|
owned fingerprint, or the one a pending update replaces), even if it happens
|
|
120
191
|
to equal the new source;
|
|
192
|
+
- an installed copy that a 0.8.0 record names but no trusted reference proves
|
|
193
|
+
(`skill_legacy_unverified`);
|
|
121
194
|
- a copy that differs from the source without a record (a foreign skill of the
|
|
122
195
|
same name);
|
|
123
196
|
- any entry whose name starts with `.` inside the installed copy: setup never
|
|
@@ -171,11 +244,41 @@ status and a stable code. No skill content, fingerprint or path leaves setup.
|
|
|
171
244
|
`src/skill-customize.ts`) does not touch clients and takes no ownership lock. It
|
|
172
245
|
copies a shipped skill into the override directory `skills/` next to the
|
|
173
246
|
configuration file and records the shipped fingerprint under the skill name in
|
|
174
|
-
`<override dir>/.pi-session-mcp-bases.json`
|
|
175
|
-
strictly validated,
|
|
247
|
+
`<override dir>/.pi-session-mcp-bases.json`
|
|
248
|
+
(`{"schemaVersion":2,"bases":{"<name>":"<fingerprint>"}}`, strictly validated,
|
|
249
|
+
at most 64 KiB). A value is a `v2:` fingerprint or a legacy 64-hex value that
|
|
250
|
+
could not be migrated. A version 1 file is still read. The copy is staged in
|
|
176
251
|
a temporary directory and renamed into place; an existing override is never
|
|
177
252
|
overwritten. A changed shipped skill is only reported (`skill_override_outdated`);
|
|
178
|
-
`--apply` then moves the recorded base and nothing else.
|
|
253
|
+
`--apply` then moves the recorded base and nothing else.
|
|
254
|
+
|
|
255
|
+
`--apply` is a cross-process transaction per override directory
|
|
256
|
+
(`src/override-lock.ts`). It takes an exclusive kernel lock on the stable lock
|
|
257
|
+
file `<override dir>/.pi-session-mcp-bases.lock` (mode `0600`, never deleted;
|
|
258
|
+
on Linux the trusted util-linux `flock` at `/usr/bin/flock` or `/bin/flock`, on
|
|
259
|
+
macOS `O_EXLOCK`; the same supported local filesystems as the ownership
|
|
260
|
+
state). Under the lock it re-reads and validates the bases file, copies the
|
|
261
|
+
skill if no override exists, merges the base and writes the file atomically: an
|
|
262
|
+
fsynced temporary file, a rename and a directory fsync. The lock is released on
|
|
263
|
+
every path. The wait is bounded at 30 seconds (`skill_customize_lock_busy`);
|
|
264
|
+
a lock that cannot be taken is `skill_customize_lock_unavailable`. A killed
|
|
265
|
+
holder releases the lock through the kernel and may leave a stale
|
|
266
|
+
`.pi-session-mcp-bases.json.<hex>.tmp` or a temporary copy directory
|
|
267
|
+
`.<skill>.<hex>.tmp`, which are harmless. The dry-run takes no
|
|
268
|
+
lock and writes nothing: no directory, lock file or temporary file. Every write
|
|
269
|
+
produces version 2 and migrates each legacy value that names a released 0.8.0
|
|
270
|
+
shipped skill; other legacy values are kept as they are.
|
|
271
|
+
|
|
272
|
+
Decisions for an existing legacy base: if it names the current shipped skill,
|
|
273
|
+
the dry-run reports `skill_base_migration_planned` and `--apply` reports
|
|
274
|
+
`skill_base_migrated`; if it names no released shipped skill, the dry-run
|
|
275
|
+
reports `skill_base_unverified` and `--apply` acknowledges it as
|
|
276
|
+
`skill_base_updated`; if it names another released skill, the result is
|
|
277
|
+
`skill_override_outdated`. An existing override that equals the shipped skill
|
|
278
|
+
but has no base completes a customization whose bases commit failed
|
|
279
|
+
(`skill_customize_planned`, `skill_customized`). Of two concurrent applies for
|
|
280
|
+
the same skill, one reports `skill_customized` and the other
|
|
281
|
+
`skill_override_current`. Doctor stays offline, read-only and lock-free. It cannot be combined
|
|
179
282
|
with `--target`, `--verify`, `--rollback` or `--remove`. The finding target is
|
|
180
283
|
`{ client: "skills", scope: "override", alias: <name> }`; the alias is the
|
|
181
284
|
skill name only if it is valid, else `invalid`.
|
|
@@ -184,38 +287,68 @@ skill name only if it is valid, else `invalid`.
|
|
|
184
287
|
|
|
185
288
|
Production verification requires the offline Doctor, exact public client-CLI registration inspection, and a directly launched bounded MCP handshake. The verifier calls `pi_capabilities_get` with `{}` only; session start/prompt/turn and provider/model operations are forbidden. Its child process is terminated and awaited before success is reported.
|
|
186
289
|
|
|
290
|
+
Verify evaluates one target in this order, and the first failure ends that
|
|
291
|
+
target's verification:
|
|
292
|
+
|
|
293
|
+
1. An unknown client: `client_unsupported`.
|
|
294
|
+
2. Ownership: `ownership_unavailable`.
|
|
295
|
+
3. Release binding: `release_binding_mismatch`.
|
|
296
|
+
4. Doctor, under its own code. A failed Doctor ends the target's verify before
|
|
297
|
+
the registration is inspected and before any handshake.
|
|
298
|
+
5. Inspection. A client the inspection reports as unsupported or not
|
|
299
|
+
discoverable, for example because its CLI is not on `PATH`, gives status
|
|
300
|
+
`unsupported` and code `unsupported`. An inspection that throws gives
|
|
301
|
+
`inspect_failed`. An absent registration, a divergent one, and an
|
|
302
|
+
equivalent one with another fingerprint give `registration_not_equivalent`.
|
|
303
|
+
6. Only a truly equivalent registration runs the bounded handshake, which
|
|
304
|
+
reports `mcp_verified` or `mcp_<status>`.
|
|
305
|
+
|
|
306
|
+
None of the failure cases mutates a registration or starts the handshake, and
|
|
307
|
+
all exit with `1`. There is no separate client-unavailable code; the
|
|
308
|
+
dry-run already separates these states the same way.
|
|
309
|
+
|
|
187
310
|
## Compatibility and reporting
|
|
188
311
|
|
|
189
312
|
Codex and Claude Code are separate adapters with explicit supported scopes and documented capability fixtures. Unknown output fails closed with stable codes. Absolute paths are passed only to client commands where required and are never included in results, logs, or ownership metadata. Client command diagnostics must be reduced to stable codes before crossing this boundary.
|
|
190
313
|
|
|
191
|
-
Human and JSON output derive from one `schemaVersion: 1` result. Exit code `0` is success, `1` is an operational or reconciliation failure, `64` is command-line misuse, and interrupted setup returns `130` for `SIGINT` or `143` for `SIGTERM` only after the guardian cleanup path has completed or retained the fence. Automatic replacement of a divergent registration is deliberately unsupported.
|
|
314
|
+
Human and JSON output derive from one `schemaVersion: 1` result; this version is the setup report's own, not the package version or the MCP tool contract (see [Versions](tool-contracts.md#versions)). Exit code `0` is success, `1` is an operational or reconciliation failure, `64` is command-line misuse, and interrupted setup returns `130` for `SIGINT` or `143` for `SIGTERM` only after the guardian cleanup path has completed or retained the fence. Automatic replacement of a divergent registration is deliberately unsupported.
|
|
192
315
|
|
|
193
316
|
Stable finding codes are grouped by responsibility:
|
|
194
317
|
|
|
195
318
|
- input/prerequisites: `target_invalid`, `config_required`, `usage_invalid`,
|
|
196
319
|
`platform_unsupported`, `operation_interrupted`,
|
|
197
320
|
`doctor_failed`, `release_unavailable`, `client_unsupported`, `inspect_failed`;
|
|
198
|
-
- planning/reconciliation: `absent`, `equivalent`, `divergent`,
|
|
321
|
+
- planning/reconciliation: `absent`, `equivalent`, `divergent`, `unsupported`,
|
|
199
322
|
`already_equivalent`, `divergence_requires_replacement`,
|
|
200
323
|
`registration_fingerprint_mismatch`, `release_binding_mismatch`;
|
|
201
324
|
- ownership/apply: `applied`, `pending_recovered`, `ownership_unavailable`,
|
|
202
325
|
`apply_failed_recovery_pending`;
|
|
203
|
-
- verification: `registration_not_equivalent
|
|
204
|
-
`
|
|
326
|
+
- verification: `registration_not_equivalent` (absent, divergent or
|
|
327
|
+
differently fingerprinted registration), `mcp_verified`, `mcp_unsupported`,
|
|
328
|
+
`mcp_timeout`, `mcp_failed`; verify also reports `unsupported` (client not
|
|
329
|
+
discoverable) and `inspect_failed` (inspection threw), which belong to the
|
|
330
|
+
other groups;
|
|
205
331
|
- rollback/removal: `rolled_back`, `removed`, `ownership_diverged`,
|
|
206
332
|
`cleanup_failed`;
|
|
207
333
|
- controller skill, dry-run: `skill_absent`, `skill_equivalent`, `skill_linked`,
|
|
208
|
-
`skill_update_planned`, `
|
|
209
|
-
|
|
210
|
-
`
|
|
334
|
+
`skill_update_planned`, `skill_recovery_planned`,
|
|
335
|
+
`skill_recovery_retained`, `skill_divergent`, `skill_legacy_unverified`,
|
|
336
|
+
`skill_source_invalid`;
|
|
337
|
+
- controller skill, apply: `skill_installed`, `skill_already_equivalent`, `skill_recovered`, `skill_linked`,
|
|
338
|
+
`skill_updated`, `skill_divergent`, `skill_legacy_unverified`,
|
|
339
|
+
`skill_recovery_retained`, `skill_source_invalid`,
|
|
211
340
|
`skill_install_failed`;
|
|
212
341
|
- controller skill, verify: `skill_verified`, `skill_missing`,
|
|
213
342
|
`skill_not_equivalent`;
|
|
214
343
|
- controller skill, rollback/removal: `skill_removed`, `skill_kept_shared`,
|
|
215
|
-
`skill_not_owned`, `skill_absent`, `skill_divergent
|
|
344
|
+
`skill_not_owned`, `skill_absent`, `skill_divergent`,
|
|
345
|
+
`skill_legacy_unverified`, `skill_recovery_retained`;
|
|
216
346
|
- skill customization: `skill_customize_planned`, `skill_customized`,
|
|
217
|
-
`skill_override_current`, `skill_override_outdated`,
|
|
218
|
-
`
|
|
347
|
+
`skill_override_current`, `skill_override_outdated`,
|
|
348
|
+
`skill_base_migration_planned`, `skill_base_migrated`,
|
|
349
|
+
`skill_base_unverified`, `skill_base_updated`, `skill_not_shipped`,
|
|
350
|
+
`skill_bases_corrupt`, `skill_override_invalid`,
|
|
351
|
+
`skill_customize_lock_busy`, `skill_customize_lock_unavailable`,
|
|
219
352
|
`skill_customize_failed`.
|
|
220
353
|
|
|
221
354
|
The public statuses are `planned`, `ok`, `unchanged`, `absent`, `divergent`,
|
package/docs/client-setup.md
CHANGED
|
@@ -90,6 +90,25 @@ bounded direct MCP `initialize`, `tools/list`, and
|
|
|
90
90
|
`pi_capabilities_get({})` check. It never starts a session or invokes
|
|
91
91
|
a provider.
|
|
92
92
|
|
|
93
|
+
### Verify diagnostics
|
|
94
|
+
|
|
95
|
+
Verify checks one target in a fixed order and stops that target at the first
|
|
96
|
+
failure. After an unknown client (`client_unsupported`), ownership
|
|
97
|
+
(`ownership_unavailable`) and release binding (`release_binding_mismatch`),
|
|
98
|
+
Doctor runs; if it fails, verify reports its code and neither inspects the
|
|
99
|
+
registration nor runs the handshake. Then the client's registration is
|
|
100
|
+
inspected:
|
|
101
|
+
|
|
102
|
+
| Result | Status | Code | What it means |
|
|
103
|
+
| --- | --- | --- | --- |
|
|
104
|
+
| Client unsupported or not discoverable | `unsupported` | `unsupported` | Setup could not run or recognize the client CLI. Usually the CLI is not on `PATH`, for example in a non-interactive SSH shell. Put the client's directory on `PATH` and rerun; the registration itself may be fine. `--dry-run` reports the same state |
|
|
105
|
+
| Inspection failed | `failed` | `inspect_failed` | Inspecting the registration threw an error. Rerun; if it persists, run the client's own `mcp` listing |
|
|
106
|
+
| Registration absent, divergent, or equivalent with another fingerprint | `failed` | `registration_not_equivalent` | The registration is missing or differs from the one setup would create. Run `--dry-run` for the exact state |
|
|
107
|
+
| Registration equivalent | `ok` or `failed` | `mcp_verified` or `mcp_<status>` | Only now does the bounded MCP handshake run |
|
|
108
|
+
|
|
109
|
+
None of the failure rows changes a registration or starts the handshake, and
|
|
110
|
+
each exits with `1`.
|
|
111
|
+
|
|
93
112
|
### Controller skill
|
|
94
113
|
|
|
95
114
|
Besides the registration, `--apply` installs the controller skill
|
|
@@ -117,19 +136,26 @@ Skill findings per operation:
|
|
|
117
136
|
|
|
118
137
|
- `--dry-run`: `skill_absent` (absent), `skill_equivalent` (unchanged),
|
|
119
138
|
`skill_linked` (unchanged; a link to exactly the effective skill),
|
|
120
|
-
`skill_update_planned` (planned), `
|
|
139
|
+
`skill_update_planned` (planned), `skill_recovery_planned` (planned; the
|
|
140
|
+
ownership record is pending for an update that `--apply` would settle),
|
|
141
|
+
`skill_divergent` (divergent), `skill_legacy_unverified` (divergent),
|
|
121
142
|
`skill_source_invalid` (failed).
|
|
122
143
|
- `--apply`: `skill_installed` (ok), `skill_already_equivalent` (unchanged),
|
|
123
|
-
`
|
|
144
|
+
`skill_recovered` (ok; `--apply` settled an interrupted update and needed no
|
|
145
|
+
further update), `skill_linked` (unchanged),
|
|
124
146
|
`skill_updated` (ok; setup replaced its own unmodified copy after a package
|
|
125
|
-
update or a changed override), `skill_divergent`, `
|
|
147
|
+
update or a changed override), `skill_divergent`, `skill_legacy_unverified`,
|
|
148
|
+
`skill_recovery_retained` (divergent), `skill_source_invalid`,
|
|
126
149
|
`skill_install_failed` (failed).
|
|
127
150
|
- `--verify`: `skill_verified` (ok), `skill_missing` and
|
|
128
151
|
`skill_not_equivalent` (failed).
|
|
129
152
|
- `--rollback` and `--remove`: `skill_removed` (ok), `skill_kept_shared`
|
|
130
153
|
(unchanged; the other Claude Code `user` or `local` registration still uses
|
|
131
154
|
the shared directory), `skill_not_owned` (unchanged), `skill_absent` (ok) and
|
|
132
|
-
`skill_divergent` (a modified copy stays in place)
|
|
155
|
+
`skill_divergent` (a modified copy stays in place), `skill_legacy_unverified`
|
|
156
|
+
(a record of 0.8.0 that no trusted reference proves; copy and record stay) and
|
|
157
|
+
`skill_recovery_retained` (a modified or foreign intermediate directory of an
|
|
158
|
+
interrupted update stays).
|
|
133
159
|
|
|
134
160
|
Setup never replaces or deletes a modified installed copy, a skill of the same
|
|
135
161
|
name that it did not install, or a link or non-directory at the skill entry; it
|
|
@@ -155,9 +181,14 @@ records the fingerprint of the shipped skill in
|
|
|
155
181
|
`.pi-session-mcp-bases.json` there. The finding target is
|
|
156
182
|
`skills/override/<name>`; the codes are `skill_customize_planned` (planned),
|
|
157
183
|
`skill_customized` (ok), `skill_override_current` (unchanged),
|
|
158
|
-
`skill_override_outdated` (planned), `
|
|
159
|
-
`
|
|
160
|
-
`
|
|
184
|
+
`skill_override_outdated` (planned), `skill_base_migration_planned` (planned),
|
|
185
|
+
`skill_base_unverified` (planned), `skill_base_migrated` (ok),
|
|
186
|
+
`skill_base_updated` (ok), `skill_not_shipped`, `skill_bases_corrupt`,
|
|
187
|
+
`skill_override_invalid`, `skill_customize_lock_busy`,
|
|
188
|
+
`skill_customize_lock_unavailable` and `skill_customize_failed` (failed). An
|
|
189
|
+
existing override is never overwritten. `--apply` runs under an exclusive lock
|
|
190
|
+
on the override directory (`.pi-session-mcp-bases.lock`, 30 s wait); the dry-run
|
|
191
|
+
takes no lock and writes nothing.
|
|
161
192
|
After `skill_override_outdated`, `--apply` moves only the recorded base, which
|
|
162
193
|
acknowledges a change you reviewed. Details: [Pi skills](skills.md#customize-and-add-skills).
|
|
163
194
|
|
package/docs/doctor-design.md
CHANGED
|
@@ -43,7 +43,7 @@ Doctor reads extension manifests from Pi's user install location only; it never
|
|
|
43
43
|
|
|
44
44
|
The skill checks run after the extension checks. They look only at file availability: Doctor never reads a skill's frontmatter, never parses or executes it, and the subject carries the catalog alias, never a path. Frontmatter, skill-name rules, name collisions and explicit-only flags (`disable-model-invocation`) are checked only at session start by the Pi SDK contract, so a clean Doctor run does not promise that a skill loads. Unknown or duplicate aliases and two selected aliases with one path are configuration errors and surface as `config_invalid`. See [Pi skills](skills.md).
|
|
45
45
|
|
|
46
|
-
`skill_override_outdated` concerns the operator's skill overrides, not a profile. It is `ok` for a recorded base that still matches the shipped skill, and a warning (exit status 1, never blocking `setup`) when the shipped skill changed after the operator ran `pi-session-mcp-setup --customize-skill <name>`; its remediation names `pi-session-mcp-setup --apply --customize-skill <name>` to acknowledge a reviewed change. Only skills with a recorded base are compared, and a recorded name that is no longer shipped is skipped. A corrupt bases file yields a warning with the subject `skill:bases`. Doctor reads the bases file and the shipped skill files only; nothing is executed and the subject is a validated skill name, never a path. The check sorts after `skill_duplicate`.
|
|
46
|
+
`skill_override_outdated` concerns the operator's skill overrides, not a profile. It is `ok` for a recorded base that still matches the shipped skill, and a warning (exit status 1, never blocking `setup`) when the shipped skill changed after the operator ran `pi-session-mcp-setup --customize-skill <name>`; its remediation names `pi-session-mcp-setup --apply --customize-skill <name>` to acknowledge a reviewed change. Only skills with a recorded base are compared, and a recorded name that is no longer shipped is skipped. A recorded base of 0.8.0 (a version 1 fingerprint) is compared through the version 2 value of the released 0.8.0 skill it names; a version 1 base that names no released skill cannot be confirmed and yields a warning that says so, with the same remediation. A corrupt bases file yields a warning with the subject `skill:bases`. Doctor reads the bases file and the shipped skill files only; nothing is executed and the subject is a validated skill name, never a path. The check sorts after `skill_duplicate`.
|
|
47
47
|
|
|
48
48
|
The Doctor reuses `loadConfig`; it does not duplicate alias, profile, thinking
|
|
49
49
|
level, or read-only-default rules. Workspace values are used only for local
|
package/docs/installation.md
CHANGED
|
@@ -20,7 +20,7 @@ An installation consists of these locations:
|
|
|
20
20
|
| --- | --- | --- |
|
|
21
21
|
| Package | the npm global prefix, or a dedicated directory per release such as `~/.local/share/pi-session-mcp/releases/<tag>` | The built server, `setup` and Doctor. |
|
|
22
22
|
| Configuration | a file outside the package, for example `~/.config/pi-session-mcp/pi-session-mcp.json` | Operator-owned, mode `0600`, never committed. |
|
|
23
|
-
| Skill overrides (optional) | the `skills/` directory next to the configuration file, for example `~/.config/pi-session-mcp/skills/` | Your own or customized skills, plus `.pi-session-mcp-bases.json`. Created by you or by `setup --customize-skill`. See [Pi skills](#pi-skills). |
|
|
23
|
+
| Skill overrides (optional) | the `skills/` directory next to the configuration file, for example `~/.config/pi-session-mcp/skills/` | Your own or customized skills, plus `.pi-session-mcp-bases.json` and its lock file `.pi-session-mcp-bases.lock`. Created by you or by `setup --customize-skill`. See [Pi skills](#pi-skills). |
|
|
24
24
|
| Controller skill | `~/.agents/skills/pi-session-orchestration` (Codex), `~/.claude/skills/pi-session-orchestration` or `<project>/.claude/skills/pi-session-orchestration` (Claude Code) | Written by `setup --apply`; see [Register a client](#4-register-a-client). |
|
|
25
25
|
| Setup ownership state | `~/.local/state/pi-session-mcp/ownership.json` | Written by `setup` only. Path-free and secret-free. |
|
|
26
26
|
|
|
@@ -487,7 +487,8 @@ choose skills.
|
|
|
487
487
|
create `<override dir>/<new-name>/SKILL.md` and reference it as
|
|
488
488
|
`builtin:<new-name>`; no command is needed. After a package update, Doctor
|
|
489
489
|
warns with `skill_override_outdated` when a shipped skill changed since you
|
|
490
|
-
copied it.
|
|
490
|
+
copied it. `--apply --customize-skill` takes a kernel lock on the override
|
|
491
|
+
directory, so concurrent runs cannot lose a recorded base.
|
|
491
492
|
- A `builtin:` name with no override and no shipped skill makes Doctor report
|
|
492
493
|
`skill_missing` and the session start fail with `skill_load_failed`.
|
|
493
494
|
- An extension allowlist alone makes none of a package's skills available. If a
|
|
@@ -513,6 +514,11 @@ the new text. A package update that changes a shipped skill does not touch an
|
|
|
513
514
|
override of yours; Doctor warns with `skill_override_outdated` so that you can
|
|
514
515
|
review the change.
|
|
515
516
|
|
|
517
|
+
After an upgrade from 0.8.0, run `setup` as a dry-run and then with `--apply`
|
|
518
|
+
for your targets. Copies and records of 0.8.0 are proven before setup acts on
|
|
519
|
+
them; one that cannot be proven is reported as `skill_legacy_unverified` and
|
|
520
|
+
stays untouched. See [Upgrade from 0.8.0](skills.md#upgrade-from-080).
|
|
521
|
+
|
|
516
522
|
### npm installation
|
|
517
523
|
|
|
518
524
|
An npm upgrade replaces the package in place. Close the client sessions that
|
|
@@ -263,6 +263,17 @@ and step 6 leaves no session to dispose.
|
|
|
263
263
|
- **UI and flags.** No UI context is bound; extension UI calls return the
|
|
264
264
|
SDK's no-op results. `extensionFlagValues` is empty, so extension flags keep
|
|
265
265
|
their defaults.
|
|
266
|
+
- **Turn liveness.** The `updatedAt` of a running turn follows the public SDK
|
|
267
|
+
events of the prompt: assistant message start, update and end, and tool
|
|
268
|
+
execution start, update and end. An extension provider with its own agent
|
|
269
|
+
loop, such as `pi-cursor-sdk`, therefore advances `updatedAt` whenever it
|
|
270
|
+
publishes streaming updates. Its internal steps that publish no public
|
|
271
|
+
event stay invisible, so an unchanged `updatedAt` is no proof of a hang, and
|
|
272
|
+
streaming is no evidence of finished or useful work. Only the event type and
|
|
273
|
+
the message role are inspected; no delta, thinking block, tool argument or
|
|
274
|
+
partial result is stored or projected, and the answer still comes only from
|
|
275
|
+
the final assistant message. See [Tool
|
|
276
|
+
contracts](tool-contracts.md).
|
|
266
277
|
|
|
267
278
|
### Lifecycle and close
|
|
268
279
|
|
package/docs/releasing.md
CHANGED
|
@@ -16,7 +16,13 @@ does not authorize any of them.
|
|
|
16
16
|
- A change to the public contract needs a minor release and its own decision.
|
|
17
17
|
|
|
18
18
|
The public contract is the set of eight tools with their fields, states and
|
|
19
|
-
error codes, as defined in the [tool contracts](tool-contracts.md).
|
|
19
|
+
error codes, as defined in the [tool contracts](tool-contracts.md). How the
|
|
20
|
+
package version, the MCP tool contract and the setup report's `schemaVersion`
|
|
21
|
+
differ is defined under [Versions](tool-contracts.md#versions).
|
|
22
|
+
|
|
23
|
+
Wherever this document asks for the Level 1 checks, the test step is
|
|
24
|
+
`NODE_OPTIONS=--unhandled-rejections=strict npm test`. CI uses the same
|
|
25
|
+
setting, so an unhandled promise rejection fails the run locally and in CI.
|
|
20
26
|
|
|
21
27
|
A release is distributed twice: as the GitHub release with its source archive,
|
|
22
28
|
and as the npm package `pi-session-mcp`. The npm package contains the built
|
|
@@ -131,9 +137,15 @@ Patch transitive advisories in the lockfile only:
|
|
|
131
137
|
|
|
132
138
|
## Prepare a release
|
|
133
139
|
|
|
140
|
+
A release travels in the pull request of the change it ships. That pull
|
|
141
|
+
request also carries the version, the changelog heading and the pre-release
|
|
142
|
+
acceptance record; there is no separate release pull request. Records that can
|
|
143
|
+
only be made after publishing go into the pull request of the next change.
|
|
144
|
+
|
|
134
145
|
Preconditions:
|
|
135
146
|
|
|
136
|
-
-
|
|
147
|
+
- The change pull request contains every change of the release, and Level 1
|
|
148
|
+
is green on its head.
|
|
137
149
|
- `npm audit` reports 0 vulnerabilities.
|
|
138
150
|
- The public contract is unchanged, or a minor release has been decided.
|
|
139
151
|
- The acceptance that this release requires is recorded, or its absence is
|
|
@@ -141,8 +153,9 @@ Preconditions:
|
|
|
141
153
|
|
|
142
154
|
Steps:
|
|
143
155
|
|
|
144
|
-
1.
|
|
145
|
-
that predates later merges: its lockfile would undo
|
|
156
|
+
1. Bring the change branch up to date with `main` if `main` moved. Never reuse
|
|
157
|
+
a branch that predates later merges without that: its lockfile would undo
|
|
158
|
+
later dependency patches.
|
|
146
159
|
2. Set the version:
|
|
147
160
|
|
|
148
161
|
```bash
|
|
@@ -159,11 +172,13 @@ Steps:
|
|
|
159
172
|
5. Run the working-tree
|
|
160
173
|
[Level 2 acceptance](acceptance.md#tooling) on the release candidate with
|
|
161
174
|
`scripts/setup-acceptance.mjs`, unless the release notes state its absence.
|
|
162
|
-
6.
|
|
175
|
+
6. Record the pre-release acceptance in the status section of
|
|
176
|
+
[Acceptance](acceptance.md) and push it to the change pull request. Name the
|
|
177
|
+
release in the pull request's title and description.
|
|
163
178
|
|
|
164
179
|
## Publish a release
|
|
165
180
|
|
|
166
|
-
After the
|
|
181
|
+
After the change pull request is merged and CI is green on the merge commit:
|
|
167
182
|
|
|
168
183
|
```bash
|
|
169
184
|
git switch main
|
|
@@ -217,9 +232,9 @@ After publishing:
|
|
|
217
232
|
|
|
218
233
|
1. Run the published-release
|
|
219
234
|
[Level 2 acceptance](acceptance.md#working-tree-and-published-release-variants)
|
|
220
|
-
from a fresh clone of the tag and from the published npm package
|
|
221
|
-
|
|
222
|
-
([Tooling](acceptance.md#tooling)).
|
|
235
|
+
from a fresh clone of the tag and from the published npm package.
|
|
236
|
+
`scripts/setup-acceptance.mjs` runs both ([Tooling](acceptance.md#tooling)).
|
|
223
237
|
2. Upgrade the installation as described in
|
|
224
238
|
[Installation](installation.md#upgrade).
|
|
225
|
-
3.
|
|
239
|
+
3. Record both in the status section of [Acceptance](acceptance.md) with the
|
|
240
|
+
pull request of the next change, not in a pull request of their own.
|