pi-session-mcp 0.14.0 → 0.14.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.
Files changed (41) hide show
  1. package/.pi-session-mcp-cli/writer-boundary.js +2 -1
  2. package/.pi-session-mcp-cli/writer-lifecycle.js +99 -18
  3. package/.pi-session-mcp-cli/writer-models-store.js +64 -0
  4. package/.pi-session-mcp-cli/writer-tree-removal.js +75 -0
  5. package/CHANGELOG.md +95 -0
  6. package/README.md +1 -1
  7. package/dist/capabilities.d.ts +13 -0
  8. package/dist/capabilities.js +17 -13
  9. package/dist/model-catalog.d.ts +18 -5
  10. package/dist/model-catalog.js +8 -5
  11. package/dist/sdk-pi-adapter.d.ts +6 -1
  12. package/dist/sdk-pi-adapter.js +35 -7
  13. package/dist/writer-boundary.d.ts +1 -1
  14. package/dist/writer-boundary.js +2 -1
  15. package/dist/writer-guard.js +1 -1
  16. package/dist/writer-launch.d.ts +2 -0
  17. package/dist/writer-launch.js +3 -0
  18. package/dist/writer-lifecycle.d.ts +8 -0
  19. package/dist/writer-lifecycle.js +99 -18
  20. package/dist/writer-models-store.d.ts +16 -0
  21. package/dist/writer-models-store.js +64 -0
  22. package/dist/writer-relay.js +12 -1
  23. package/dist/writer-tree-removal.d.ts +16 -0
  24. package/dist/writer-tree-removal.js +75 -0
  25. package/docs/acceptance.md +314 -0
  26. package/docs/adr/0012-leased-writer-pool.md +10 -6
  27. package/docs/adr/0013-hardened-read-only-profiles.md +30 -0
  28. package/docs/adr/0014-profile-probe-and-model-catalog.md +1 -1
  29. package/docs/doctor-design.md +1 -1
  30. package/docs/execution-modes.md +66 -10
  31. package/docs/installation.md +9 -0
  32. package/docs/isolation-setup.md +12 -2
  33. package/docs/roadmap.md +21 -0
  34. package/docs/sprints/2026-10-24-s8-writer-migration.md +5 -3
  35. package/docs/sprints/2026-10-29-s12-audit-fixes.md +174 -0
  36. package/docs/sprints/2026-10-30-s13-index-only-work.md +199 -0
  37. package/docs/threat-model.md +10 -1
  38. package/docs/tool-contracts.md +1 -1
  39. package/package.json +1 -1
  40. package/skills/pi-session-orchestration/SKILL.md +1 -1
  41. package/skills/pi-session-profiles/SKILL.md +5 -1
@@ -2598,6 +2598,320 @@ image) reported `writer_accepted` from 09:48 to 09:51 UTC. Client-target
2598
2598
  all 14 writers. Doctor reported no error, only `workspace_mode_unset` for one
2599
2599
  workspace without a mode.
2600
2600
 
2601
+ ### v0.14.2 pre-release acceptance
2602
+
2603
+ `v0.14.2` was accepted on 2026-10-30 from 15:41 to 15:47 UTC on its release
2604
+ candidate `e911a6d`, on Linux (`x64`) with Node.js `26.12.1`, npm `12.2.1`,
2605
+ Codex CLI `0.166.0` and Claude Code `2.1.314`. Level 2 passed in the working
2606
+ tree and for the packed tarball, each with delivery off and with delivery on.
2607
+ Level 1 passed except `npm audit`. Later commits on the release pull request
2608
+ change only this record.
2609
+
2610
+ The patch release ships #198 and #199 (PR #201), #200 (PR #203) and #202
2611
+ (PR #204) from `main` at `5acbebb`; the candidate adds only the version, the
2612
+ changelog heading and the roadmap lines. The MCP tool contract, result codes
2613
+ and configuration schema are unchanged. The #200 source changes were accepted
2614
+ on their branch before the merge, see
2615
+ [Issue #200 candidate acceptance](#issue-200-candidate-acceptance-2026-10-30);
2616
+ #198, #199 and #202 were checked in their pull requests by the full test suite
2617
+ and, for #202, the native Podman tests.
2618
+
2619
+ - Level 1 in the checkout of the candidate after `npm ci`: every command exited
2620
+ with `0`, except `npm audit`; `git status --short` printed nothing.
2621
+ - The test run, with `NODE_OPTIONS=--unhandled-rejections=strict`, reported
2622
+ 144 files, 4097 passed and 14 skipped, and no unhandled error.
2623
+ - `npm audit` and `npm audit --omit=dev` exited with `1` and name only the
2624
+ known `undici` 8.10.2 finding through `@earendil-works/pi-coding-agent`
2625
+ 1.4.0, the latest SDK release, with eight advisories
2626
+ ([#120](https://github.com/plagor44-ux/pi-session-mcp/issues/120)).
2627
+ - `npm pack` listed 940 files (tarball shasum
2628
+ `775e6de2a6e810839ceb4d8518b8e7c93eae38f3`, identical in three packs). A
2629
+ scan of the unpacked files for private names, hosts and the maintainer's
2630
+ home path found none; the only matches were the public repository owner in
2631
+ links and the already redacted paths of disposable evaluation homes.
2632
+ - Doctor reported `packageVersion` `0.14.2`. It exited with `2` because no
2633
+ configuration was set (`config_missing`).
2634
+ - Level 2 in the working tree: 160 of 160 checks passed with delivery off and
2635
+ 228 of 228 with `--events spool`.
2636
+ - Packed-tarball Level 2 from a fresh prefix: 160 of 160 with delivery off and
2637
+ 228 of 228 with `--events spool`. The install smoke from another fresh prefix
2638
+ reported `smoke_passed` with version `0.14.2`.
2639
+ - In every Level 2 run the real client registrations were unchanged and the
2640
+ disposable prefixes were removed.
2641
+ - Podman-gated writer tests on the candidate (Podman `6.1.4`): 10 of 10 passed
2642
+ (9 lifecycle tests, including the detached-`HEAD` export of #193, the
2643
+ index-only export of #200 and the stash export of #202, and 1 boundary test).
2644
+ - Bubblewrap-gated sandbox tests (`test/sandbox-shell.bwrap.test.ts`, bubblewrap
2645
+ `0.13.1`): 12 of 12 passed, none skipped.
2646
+
2647
+ Not established for `0.14.2`: a Level 3 cycle on the release candidate, and the
2648
+ published-release Level 2 and upgrades, which follow after publishing.
2649
+
2650
+ ### Issue #200 candidate acceptance (2026-10-30)
2651
+
2652
+ Recorded on 2026-10-30 for the unreleased branch `fix/writer-index-only-work`
2653
+ ([Sprint 13](sprints/2026-10-30-s13-index-only-work.md)), on its source commit
2654
+ `1e1e694`, in a fresh clone on Linux (`x64`) with Node.js `26.12.1`, npm
2655
+ `12.2.1`, Podman `6.1.4`, Codex CLI `0.166.0` and Claude Code `2.1.314`. Later
2656
+ commits on the branch change only documentation.
2657
+
2658
+ - Level 1 after `npm ci`: typecheck, `build:cli`, the test run with
2659
+ `NODE_OPTIONS=--unhandled-rejections=strict` (141 files, 4062 passed, 14
2660
+ skipped, no unhandled error), `build` and `git diff --check` exited with `0`,
2661
+ and the status stayed clean. `npm audit --omit=dev` still exits with `1` for
2662
+ the known `undici` finding (#120).
2663
+ - Podman-gated writer tests, none skipped: 8 of 8 lifecycle tests, including
2664
+ the new index-only case, and 1 of 1 boundary test. The index-only case fails
2665
+ against the source of `7752d3f` (`baseline: true`).
2666
+ - Level 2 in the working tree: 160 of 160 with delivery off and 228 of 228 with
2667
+ `--events spool`.
2668
+ - Packed-tarball Level 2 from a fresh prefix: 160 of 160 with delivery off and
2669
+ 228 of 228 with `--events spool`. The install smoke reported `smoke_passed`
2670
+ with version `0.14.1`.
2671
+ - In every Level 2 run the built files and the real client registrations were
2672
+ unchanged, no process remained, the leak check passed and the disposable
2673
+ directories were removed.
2674
+
2675
+ Not established: a Level 3 cycle, macOS, and a productive writer upgrade with
2676
+ this change.
2677
+
2678
+ ### v0.14.1 post-release evidence
2679
+
2680
+ Recorded on 2026-10-30 for the published release `0.14.1`.
2681
+
2682
+ Tag binding: a fresh clone of the annotated tag `v0.14.1` (tag object
2683
+ `bd1ba81739742e1692d65425e94d5a091731a402`) peels to the merge commit
2684
+ `7752d3f1555d19fed06f43c0e046da07017a3320` of #197, and
2685
+ `git describe --tags --exact-match` reports `v0.14.1`. CI on the merge commit
2686
+ (run 41013930671) and on the tag (run 41017350738) passed on the first attempt.
2687
+ The npm package records `gitHead` `0753fad`, the head of #197, whose tree
2688
+ (`36654c3`) is identical to the merge commit's.
2689
+
2690
+ - Level 1 on the tag clone after `npm ci`, from 08:27 to 08:29 UTC with
2691
+ Node.js `26.12.1` and npm `12.2.1`: typecheck, `build:cli`, the test run
2692
+ (141 files, 4044 passed, 14 skipped, with
2693
+ `NODE_OPTIONS=--unhandled-rejections=strict`), `build` and
2694
+ `git diff --check` exited with `0`, and the status stayed clean. Doctor
2695
+ reported `packageVersion` `0.14.1` and exited with `2` because no
2696
+ configuration was set (`config_missing`).
2697
+ - `pi-session-mcp@0.14.1` on npm: 933 files, shasum
2698
+ `886a20688a44a967d1f917e9dad5461e83fbb5e5`; `npm pack` of the tag clone gives
2699
+ the same shasum and file count. It differs from the candidate's
2700
+ `47cf66a3856530112c11095d07cb05b7ab9892f4` below because `0753fad` adds the
2701
+ pre-release record to the packaged `docs/acceptance.md`; no other file
2702
+ changed.
2703
+ - Published release, Level 2 on the tag clone, 08:29 to 08:31 UTC: 160 of 160
2704
+ with delivery off and 228 of 228 with `--events spool`. The manifests and
2705
+ the 152 built runtime files were byte-identical before and after the client
2706
+ cycles.
2707
+ - Published npm package, Level 2 from a fresh prefix, 08:31 to 08:33 UTC:
2708
+ 160 of 160 with delivery off and 228 of 228 with `--events spool`. The
2709
+ install smoke on the published package reported `smoke_passed` with version
2710
+ `0.14.1`.
2711
+ - In every Level 2 run the real client registrations were unchanged, no process
2712
+ remained and the leak check passed. Clients: Codex CLI `0.166.0` and Claude
2713
+ Code `2.1.314`.
2714
+ - The GitHub release `v0.14.1` was published the same day.
2715
+
2716
+ Installations: the workstation and the second machine (Linux) run `0.14.1`,
2717
+ upgraded by the maintainer with the steps of
2718
+ [Installation](installation.md#container-writers); both client targets passed
2719
+ `--verify`, and Doctor reported no error. On the workstation all 18 writers
2720
+ were provisioned again with `0.14.1` on 2026-10-30 for a profile change and
2721
+ reported `writer_accepted` and `--writer <name> --verify` ok. Those writers
2722
+ were removed before re-provisioning on the strength of the `0.14.1`
2723
+ inspection, which #200 shows to ignore work that exists only in the index;
2724
+ whether any of them held such work cannot be established afterwards.
2725
+
2726
+ ### v0.14.1 pre-release acceptance
2727
+
2728
+ `v0.14.1` was accepted on 2026-10-30 from 06:04 to 06:15 UTC on its release
2729
+ candidate `a239610`, on Linux (`x64`) with Node.js `26.12.1`, npm `12.2.1`,
2730
+ Codex CLI `0.162.1` and Claude Code `2.1.314`. Level 2 passed in the working
2731
+ tree and for the packed tarball, each with delivery off and with delivery on.
2732
+ Level 1 passed except `npm audit`. Later commits on the release pull request
2733
+ change only this record.
2734
+
2735
+ The patch release ships Sprint 12 (#192, #193, #194) from `main` at `d9a47f5`;
2736
+ the candidate adds only the version, the changelog heading and the roadmap
2737
+ line. The MCP tool contract, result codes and configuration schema are
2738
+ unchanged. The source changes were accepted on the branch before the merge, see
2739
+ [Sprint 12 candidate acceptance](#sprint-12-candidate-acceptance-2026-10-30),
2740
+ including Level 3.
2741
+
2742
+ - Level 1 in the checkout of the candidate after `npm ci`: every command exited
2743
+ with `0`, except `npm audit`; `git status --short` printed nothing.
2744
+ - The test run, with `NODE_OPTIONS=--unhandled-rejections=strict`, reported
2745
+ 141 files, 4044 passed and 14 skipped, and no unhandled error.
2746
+ - `npm audit` and `npm audit --omit=dev` exited with `1` and name only the
2747
+ known `undici` 8.10.2 finding through `@earendil-works/pi-coding-agent`
2748
+ 1.4.0, the latest SDK release, with eight advisories
2749
+ ([#120](https://github.com/plagor44-ux/pi-session-mcp/issues/120)).
2750
+ - `npm pack` listed 933 files (tarball shasum
2751
+ `47cf66a3856530112c11095d07cb05b7ab9892f4`, identical in three packs). A
2752
+ scan of the unpacked files for private names, hosts and the maintainer's
2753
+ home path found none.
2754
+ - Doctor reported `packageVersion` `0.14.1`. It exited with `2` because no
2755
+ configuration was set (`config_missing`).
2756
+ - Level 2 in the working tree: 160 of 160 checks passed with delivery off and
2757
+ 228 of 228 with `--events spool`.
2758
+ - Packed-tarball Level 2 from a fresh prefix: 160 of 160 with delivery off and
2759
+ 228 of 228 with `--events spool`. The install smoke from another fresh prefix
2760
+ reported `smoke_passed` with version `0.14.1`.
2761
+ - In every Level 2 run the real client registrations were unchanged, no process
2762
+ remained, the leak check passed and the disposable directories were removed.
2763
+ - Podman-gated writer tests on the candidate (Podman `6.1.4`): 8 of 8 passed
2764
+ (7 lifecycle tests, including the detached-`HEAD` export of #193, and 1
2765
+ boundary test).
2766
+ - Bubblewrap-gated sandbox tests (`test/sandbox-shell.bwrap.test.ts`, bubblewrap
2767
+ `0.13.1`): 12 of 12 passed, none skipped.
2768
+
2769
+ Not established for `0.14.1` beyond the Sprint 12 record: a Level 3 cycle on
2770
+ the release candidate itself, and the published-release Level 2 and upgrades,
2771
+ which follow after publishing.
2772
+
2773
+ ### Sprint 12 candidate acceptance (2026-10-30)
2774
+
2775
+ Recorded on 2026-10-29 and 2026-10-30 for the unreleased branch
2776
+ `fix/s12-audit-findings` ([Sprint 12](sprints/2026-10-29-s12-audit-fixes.md),
2777
+ #192, #193, #194). No release, tag or npm publish.
2778
+
2779
+ - Level 1 on the final source `9426682` in a separate detached worktree with
2780
+ Node.js `26.12.1`: `npm ci`, typecheck, `build:cli`, the test run (530 files,
2781
+ 4044 passed, 14 skipped, 0 failed, no unhandled error, with
2782
+ `NODE_OPTIONS=--unhandled-rejections=strict`), `build` and
2783
+ `git diff --check 9d9cf8c..HEAD` exited with `0`. The same on the earlier
2784
+ candidate `896e29d`: 4043 passed, 0 failed.
2785
+ - Real rootless Podman with the pinned Node image (git `2.39.5`):
2786
+ `test/writer-lifecycle.podman.test.ts` 7 of 7, including the new clean
2787
+ detached-HEAD case, which fails against the source of `9d9cf8c`. In the image,
2788
+ `core.useReplaceRefs=false` makes a graft-hidden detached commit visible
2789
+ again.
2790
+ - Level 2 on `npm pack` of `896e29d`, installed into a fresh prefix below the
2791
+ home directory: 160 of 160 with delivery off and 228 of 228 with
2792
+ `--events spool`; real registrations and built files unchanged, no process
2793
+ left, path-leak scan 0 hits. Clients: Codex CLI `0.162.1` and Claude Code
2794
+ `2.1.312`. The two later commits change the guard's Git arguments and a
2795
+ regular-expression spelling; Level 2 was not repeated for them.
2796
+ - Live profile change (item D), on an expendable test configuration: a
2797
+ candidate with the new read-only profile `review-sonnet`
2798
+ (`anthropic/claude-sonnet-5-5`), Doctor without error, probe dry run, one
2799
+ probe turn (`probe_passed`), timestamped backup and atomic replacement,
2800
+ Doctor, then real use by a fresh server for the R1 and final reviews; rolled
2801
+ back to the backup afterwards, Doctor without error. No production
2802
+ configuration or writer was touched.
2803
+ - Live container writer (items B and C), with the packed candidate, an
2804
+ expendable configuration and a synthetic repository: profile `s12-code`
2805
+ (`deepseek/deepseek-flash`, coding, skill `pi-session-implementation`),
2806
+ one probe turn (`probe_passed`); `--repo s12-probe --mode container
2807
+ --profiles s12-code --writers 1 --apply` reported `writer_provisioned` and
2808
+ `writer_accepted`, `--writer s12-probe-w1 --verify` `mcp_verified`. A real
2809
+ relayed coding session completed its task; `--collect --apply` imported
2810
+ `refs/remotes/writer-s12-probe-w1/s12-live-c`, whose commit passed the
2811
+ oracle (4 of 4 tests), and `--finish --apply` reported `writer_reset`. With
2812
+ the host profile's skills then changed, a start failed with
2813
+ `writer_unavailable`, reason `none_admitted`, counts
2814
+ `settingsChanged: 1`, and collection afterwards found `nothing_to_collect`.
2815
+ After `--writer s12-probe-w1 --remove` and the provisioning request again,
2816
+ the writer was accepted and verified and a start succeeded. Rolled back:
2817
+ writer removed, configuration, repository and prefix deleted.
2818
+ - Level 3, ten-task cycle, on the final source `9426682` (`dist/main.js`
2819
+ SHA-256 `ab93e3a932b1d5147dcbb48bbb85d9442c2214e486101f8d427360b99672cfdb`)
2820
+ from a separate detached checkout, with a temporary credential-free
2821
+ configuration (`l3-read` read-only and `l3-code` coding, both
2822
+ `deepseek/deepseek-flash`), synthetic fixtures, events off, one probe turn
2823
+ per profile as preflight. Claude Code `2.1.312` ran as `claude -p` with a
2824
+ per-invocation `--mcp-config`, `--strict-mcp-config` and
2825
+ `--model claude-sonnet-5-5`; Codex CLI `0.162.1` as `codex exec
2826
+ --ignore-user-config` with `-c mcp_servers.*` overrides. Both clients could
2827
+ wait with `sleep` between polls.
2828
+ - Cycle 1 was not accepted: 5 of 10. The worker ignored a requested
2829
+ ordering in both follow-ups (T2), refused the abort task's essay prompt
2830
+ within about 1.5 seconds so that nothing was left to abort (T5), and the
2831
+ Codex coding snapshot could not be written from the Codex sandbox (T3). A
2832
+ path hit came from the worker's `assistantText`, not from server
2833
+ metadata. No Pi Session MCP defect was found; the cycle is kept unchanged
2834
+ with its own pre-registration.
2835
+ - Cycle 2, with a new pre-registration before its first task (order-free
2836
+ LOW oracle, a long deterministic listing as the abort prompt, snapshots
2837
+ inside the controller directory, the leak scan separating server metadata
2838
+ from `assistantText`): 10 of 10. Read-only fixture digests unchanged; both
2839
+ coding snapshots pass their oracle and the fixture tests and show only the
2840
+ allowed files; both abort tasks show running, abort, `aborted`, close, an
2841
+ empty `pi_session_list` and a fresh `idle` session. Zero leak hits, also in
2842
+ `assistantText`; no server or client process before or 10 seconds after any
2843
+ invocation. Polls per turn: Claude Code 1 to 2, Codex 2 to 4. Deviation:
2844
+ the Codex sandbox refused to spawn `git`, so the Codex snapshots' status
2845
+ lists were derived from the barrier snapshot trees against `HEAD`.
2846
+ The cycle-2 gate holds.
2847
+
2848
+ Not established: a release of these fixes, the operator rollout on either
2849
+ machine, macOS, and #120.
2850
+
2851
+ ### v0.14.0 post-release evidence
2852
+
2853
+ Recorded on 2026-10-29 for the published release `0.14.0`.
2854
+
2855
+ Tag binding: a fresh clone of the annotated tag `v0.14.0` (tag object
2856
+ `f3a8b088a85fd5825c3256f4a7950ba051cbf388`) peels to the merge commit
2857
+ `fd4fdeacf9d02c04b6fd89314f7ec819b5506ed9` of #190, and
2858
+ `git describe --tags --exact-match` reports `v0.14.0`. CI on the merge commit
2859
+ (run 40928709705) and on the tag (run 40929602738) passed on the first attempt.
2860
+ The tag was created after the npm package had been published; the package
2861
+ records `gitHead` `d904fed`, the head of #190, whose tree (`3ae966b`) is
2862
+ identical to the merge commit's.
2863
+
2864
+ - Level 1 on the tag clone after `npm ci`, from 18:18 to 18:20 UTC with
2865
+ Node.js `26.12.1` and npm `12.2.1`: typecheck, `build:cli`, the test run
2866
+ (139 files, 4005 passed, 14 skipped, with
2867
+ `NODE_OPTIONS=--unhandled-rejections=strict`), `build` and
2868
+ `git diff --check` exited with `0`, and the status stayed clean. Doctor
2869
+ reported `packageVersion` `0.14.0` and exited with `2` because no
2870
+ configuration was set (`config_missing`).
2871
+ - `pi-session-mcp@0.14.0` on npm: 932 files, shasum
2872
+ `579cc111a7aa62987b5f35e79b34b69853c8a0e3`. It was published by the
2873
+ maintainer; `npm pack` of the tag clone gives the same shasum and file count.
2874
+ - Published npm package, Level 2 from a fresh prefix, 18:19 to 18:21 UTC:
2875
+ 160 of 160 with delivery off and 228 of 228 with `--events spool`; real
2876
+ registrations and built files unchanged, no process left, leak check passed.
2877
+ Clients: Codex CLI `0.162.1` and Claude Code `2.1.312`. The install smoke on
2878
+ the published package reported `smoke_passed` with version `0.14.0`.
2879
+ - The GitHub release `v0.14.0` was published the same day.
2880
+
2881
+ Operator upgrade of the workstation from `0.13.1` to `0.14.0` on 2026-10-29
2882
+ from 18:33 UTC, following
2883
+ [Installation](installation.md#container-writers): no Pi session of this
2884
+ controller was open; other controller sessions without a coding session stayed
2885
+ open and need a reconnect. Both client targets were removed with `0.13.1`, then
2886
+ `npm install -g pi-session-mcp@0.14.0`. Doctor reported only
2887
+ `writer_not_ready` for the 18 writers. A dry run of the first provisioning
2888
+ command with unchanged settings reported `mode_unchanged` and
2889
+ `writer_planned`; with `--apply`, five container repositories with 18 writers
2890
+ (2 with `--tags reachable`, 4, 4 with `--cache go`, 4 with base `dev`, 4 with
2891
+ `--cache uv`, three of them with operator images) reported `writer_accepted`
2892
+ from 18:34 to 18:37 UTC. Client-target `--apply` and `--verify` passed for
2893
+ `claude-code:user` and `codex:user` (`mcp_verified`, `skill_verified` for both
2894
+ controller skills, and `hook_verified` for the wake hook); `--writer <name>
2895
+ --verify` passed for all 18 writers. Doctor reported no error, only the known
2896
+ `workspace_gitfile` and `workspace_mode_unset` warnings of the worktree
2897
+ aliases.
2898
+
2899
+ Operator upgrade of the second machine (Linux) from `0.13.1` to `0.14.0` on
2900
+ 2026-10-29 from 18:38 UTC, with Node.js `24.22.0` and the same steps; one
2901
+ controller session's server process stayed open and needs a reconnect. Both
2902
+ client targets were removed with `0.13.1`, then
2903
+ `npm install -g pi-session-mcp@0.14.0`; Doctor reported only
2904
+ `writer_not_ready` for the 14 writers. With `--apply`, four container
2905
+ repositories with 14 writers (4, 2, 4 with an operator image and `--cache go`,
2906
+ 4 with an operator image) reported `writer_accepted` from 18:39 to 18:42 UTC.
2907
+ Client-target `--apply` and `--verify` passed for `claude-code:user` and
2908
+ `codex:user` (`mcp_verified`, `skill_verified` for both controller skills), and
2909
+ `--writer <name> --verify` passed for all 14 writers. Doctor reported no
2910
+ error, only `workspace_mode_unset` for one workspace without a mode.
2911
+
2912
+ Not established for `0.14.0` after the upgrade: a coding session through a
2913
+ re-accepted writer, an agent-driven profile change end to end, and Level 3.
2914
+
2601
2915
  ### v0.14.0 pre-release acceptance
2602
2916
 
2603
2917
  `v0.14.0` was accepted on 2026-10-29 from 17:56 to 18:01 UTC on its release
@@ -161,12 +161,16 @@ base.
161
161
  In both cases the host server closes that launcher and tries the next
162
162
  candidate. The inner writer guard stays the authority for baseline admission
163
163
  inside the boundary.
164
- - Before the inner start the relay compares the requested profile's entry in
165
- the writer's validated `pi_capabilities_get` (permission profile, provider,
166
- model, thinking level) with the host's configured profile and moves on with
167
- `writer_settings_changed` on any difference, so a writer never runs an
168
- outdated version of a host profile. The launcher cannot do this check: it
169
- starts before the profile is known.
164
+ - Before the inner start the relay compares every public capability field of
165
+ the requested profile's entry in the writer's validated `pi_capabilities_get`
166
+ with the host's configured profile: `permissionProfile`, `provider`, `model`,
167
+ `thinkingLevel` and `shell` exactly, and `skills`, `extensions` and
168
+ `grantedExtensionTools` as sets. A mismatch or a missing entry counts as
169
+ `settingsChanged`, starts no inner session and moves on to the next
170
+ candidate, so a writer never runs an outdated version of a host profile. The
171
+ launcher cannot do this check: it starts before the profile is known.
172
+ (Amended 2026-10-29, #194: the comparison covers every public capability
173
+ field, not only permission profile, provider, model and thinking level.)
170
174
  - If no candidate is admitted, the start fails with `writer_unavailable`, a
171
175
  content-free reason and content-free counts. `none_ready`: no writer of the
172
176
  workspace is ready, migrated and has a recorded acceptance (whether that
@@ -145,6 +145,36 @@ directly after `grantedExtensionTools`. This is configured intent only.
145
145
  MCP tools and the granted extension tools. A profile with granted extension
146
146
  tools cannot have a sandboxed shell.
147
147
 
148
+ ### Amendment 2026-10-29: context files
149
+
150
+ Issue #192. Previously every resource loader kept the SDK behaviour: Pi
151
+ context files (`AGENTS.override.md`, `AGENTS.md`, `AGENTS.MD`, `CLAUDE.md`,
152
+ `CLAUDE.MD`) were loaded from the session directory and every ancestor
153
+ directory, and a context file reached through a symlink was followed to its
154
+ target, so a read-only session also loaded context from outside the workspace.
155
+
156
+ New rule: for **read-only profiles** every resource loader keeps only context
157
+ files whose real path lies inside the workspace's real path, on every reload
158
+ and before the first provider request. A symlinked context file that resolves
159
+ outside the workspace, a dangling one, and the context files of ancestor
160
+ directories above the workspace (including a main repository above a linked
161
+ worktree) are not loaded for read-only profiles; the linked worktree's own file
162
+ inside the workspace is. Coding profiles keep the SDK behaviour (the working
163
+ directory and its ancestors).
164
+
165
+ The filter runs on the SDK's result, after the SDK has picked the first
166
+ existing name per directory. If that file is dropped, the directory contributes
167
+ no context file: an `AGENTS.override.md` that resolves outside the workspace
168
+ also hides an `AGENTS.md` beside it. This fails closed. A kept file's content
169
+ is read again from its verified real path, so a link swapped after the SDK
170
+ read cannot bring in outside content.
171
+
172
+ Why: the read roots of this ADR. Section 1 confines the read tools of
173
+ read-only profiles to the workspace and the skill directories; context files
174
+ are model input rather than tool reads, and before this amendment they were the
175
+ one channel through which content from outside those roots still reached a
176
+ read-only session.
177
+
148
178
  ## Contract change and versioning
149
179
 
150
180
  The capabilities field `shell`, the start error `sandbox_unavailable` and the
@@ -1,6 +1,6 @@
1
1
  # ADR 0014: Profile probe and offline model catalog
2
2
 
3
- **Status:** Accepted (2026-10-29); unreleased. Issue #139.
3
+ **Status:** Accepted (2026-10-29); released in 0.14.0. Issue #139.
4
4
 
5
5
  ## Context
6
6
 
@@ -44,7 +44,7 @@ and owners are:
44
44
  | `writer_not_migrated` | Warning. The writer state still records the legacy launch, so the writer is not in the relay pool | validated writer name |
45
45
  | `writer_off_default_base` | Warning. One or more ready writers of a container workspace have a base other than the workspace default base; the remediation states only their count | validated workspace alias |
46
46
  | `writer_git_identity_missing` | Clone Git config lacks a readable user name and email (warning); set both in the operator checkout, then reset the writer | validated writer name |
47
- | `writer_dirty` | HEAD is not the base branch, its commit differs, another branch exists, or Git metadata cannot safely be inspected (warning) | validated writer name |
47
+ | `writer_dirty` | HEAD is not the base branch, its commit differs, another branch exists, or Git metadata cannot safely be inspected (warning). It reads only `HEAD` and refs, never the index or worktree, so an `ok` result is no proof of a clean clone and never authorizes cleanup; the boundary inspection does (#200) | validated writer name |
48
48
  | `writer_uncollected_bundle` | Bundles directory is unsafe/unreadable or contains entries other than base.bundle (warning; an absent directory alone does not warn) | validated writer name |
49
49
  | `extension_missing` | Pi has no user install of a package that a profile's `extensions` names | `<profile>:<package>` |
50
50
  | `extension_version_mismatch` | The installed manifest's version differs from the pinned version, or the manifest or its `pi.extensions` entries are invalid | `<profile>:<package>` |
@@ -119,6 +119,11 @@ not selected by tool prefix. Their private state lives under
119
119
  `$HOME/.local/state/pi-session-mcp/writers/<name>/`: `clone`, `agent`, `config`,
120
120
  `bundles`, optional `go`, `wrapper.sh` and `state.json`. This lifecycle metadata
121
121
  is distinct from in-memory MCP sessions; it does not implement session resume.
122
+ The writer's `agent` directory also receives a byte copy of the host's persisted
123
+ Pi model catalog (`models-store.json`, catalog data and no credentials) at
124
+ provisioning, reset, recreate and each launch, so that a writer resolves the same
125
+ models as the host. A missing, linked or oversized host file is skipped and the
126
+ writer keeps the SDK's shipped model list.
122
127
  Each clone is made via a bundle, with no hardlinks to the operator's repository.
123
128
  The workspace records a short default `base` in its container entry, selected
124
129
  by `--base` or origin's symbolic HEAD, falling back to local main. Repeating a
@@ -182,11 +187,54 @@ pi-session-mcp-setup --writer my-project-w1 --finish --apply
182
187
 
183
188
  Collection exports changed branches via a bundle into the operator's checkout as
184
189
  `refs/remotes/writer-<name>/<branch>`. Uncommitted/untracked work is captured as a
185
- synthetic `<branch>-wip` commit. These refs are **untrusted until reviewed**;
190
+ synthetic `<branch>-wip` commit. A staged index whose tree differs from both
191
+ HEAD's tree and the worktree (for example a staged change that the worktree
192
+ reverted) is exported as an extra synthetic `<branch>-index` commit, so work
193
+ that exists only in the index is recovered too; the same name rules apply. An
194
+ index that cannot be evaluated, such as an unmerged conflict, makes inspection
195
+ and collection fail closed and every non-`--discard` cleanup refuse. Such a
196
+ writer is not at its baseline, so it is not admitted to a new session either;
197
+ its work stays in the clone until the operator decides on `--discard`. Have the
198
+ worker resolve or abort a merge before its session ends. A clean detached HEAD whose commit no branch in
199
+ the clone reaches is exported as `<base>-detached` (for example `main-detached`,
200
+ imported as `refs/remotes/writer-<name>/main-detached`); a dirty detached HEAD
201
+ keeps the `<base>-wip` export. A `<base>-detached`, `<branch>-wip` or `<branch>-index` name that
202
+ an exported branch already takes, or that conflicts with an exported branch's
203
+ path (such as a branch `main-detached/topic`), is refused and makes collection
204
+ incomplete (`writer_collect_incomplete`); the other branches are still
205
+ exported. Clone-side Git ignores replace refs. These refs are **untrusted until reviewed**;
186
206
  setup does not merge or cherry-pick them. Collection does not change the clone.
187
- Only a complete export records a digest authorizing later destructive cleanup;
188
- new work invalidates that authorization. Stashes are not exported as branches:
189
- resolve any needed stash inside the boundary before collection.
207
+ Only a complete export records a digest authorizing later destructive cleanup,
208
+ and the digest is recorded only when the clone's HEAD commit and every branch
209
+ commit exist in the operator checkout; otherwise collection is incomplete
210
+ (`writer_collect_incomplete`) and no digest is recorded. This includes the base
211
+ commit: if the operator checkout no longer has it (for example after a deleted
212
+ base branch and garbage collection), collect and finish fail this way even on a
213
+ pristine writer, without refused names; fetch the base again or resolve with
214
+ `--reset --discard --apply`. The digest also covers the index tree, and the
215
+ index and worktree tree objects must exist in the operator checkout. New work,
216
+ including a change to the index alone, invalidates that authorization. A digest
217
+ recorded by 0.14.1 or earlier never matches the versioned digest, so collect
218
+ such a writer again before a destructive operation.
219
+
220
+ Every stash entry is exported as `<base>-stash-<n>`, numbered like
221
+ `stash@{n}` (newest first; for example `main-stash-0`), with its index and
222
+ untracked parts as the commit's parents; `git stash apply` accepts the imported
223
+ ref. The same name rules apply, and every stash commit must exist in the
224
+ operator checkout before a digest is recorded. Tags, notes, replace refs,
225
+ remote-tracking refs and refs in any other namespace are not exported, whatever
226
+ their names. Each must name an object the operator checkout already has,
227
+ otherwise the writer is not at its baseline and collection is incomplete
228
+ (`writer_collect_incomplete`, without a refused name) and no digest is
229
+ recorded. Provisioned tags and remote-tracking refs, and a lightweight tag on
230
+ an exported commit, pass while the operator checkout still has their objects:
231
+ only such a ref's name is lost at cleanup. An annotated tag, a note, a replace
232
+ ref or a moved remote-tracking ref that the worker created blocks cleanup until
233
+ a later session on that writer removes it or the operator decides on
234
+ `--discard`; the server guard admits such a writer as long as its branches,
235
+ stash and worktree are at the baseline. If the operator deletes a provisioned
236
+ annotated tag and its object is garbage-collected, the writer's copy blocks
237
+ cleanup the same way.
190
238
 
191
239
  `--finish` collects, then resets a persistent writer or recreates an ephemeral
192
240
  writer, on the workspace default base. A collect refusing any branch name is incomplete (`writer_collect_incomplete`,
@@ -201,7 +249,7 @@ unexported work. Do not blindly retry or bypass a refusal by changing profiles.
201
249
  | `--reset --apply [--base <ref>]` | Reset to current source base branch, delete extra branches/stash and clean untracked files, and ignored files with `--clean all`; falls back to recreation if post-reset verification fails |
202
250
  | `--recreate --apply` | Rebuild clone and agent copy from the workspace default base, retaining image/settings |
203
251
  | `--finish --apply` | Collect, then reset persistent / recreate ephemeral on the workspace default base |
204
- | `--remove` | Explicit mutation: unregister owned registrations before removing writer state |
252
+ | `--remove` | Explicit mutation: unregister owned registrations before removing writer state; read-only directories inside the writer root (such as Go's module cache) are made removable first, and `state.json` is removed last |
205
253
  | `--verify` | Doctor and bounded relay-path MCP handshake/tools/list/capabilities; verify the host registration with client-target verify too; no session or provider request |
206
254
 
207
255
  Other than explicit `--remove` and `--verify`, omitting apply only plans.
@@ -234,6 +282,14 @@ reset updates admission without a server restart. A missing/unusable runtime or
234
282
  stale/unready writer launches a refusal MCP server: all eight tools return the
235
283
  sanitized refusal, with no host fallback. See [Tool contracts](tool-contracts.md).
236
284
 
285
+ Before the relay starts an inner session it compares every public capability
286
+ field of the writer's profile with the host profile: `permissionProfile`,
287
+ `provider`, `model`, `thinkingLevel` and `shell` exactly, and `skills`,
288
+ `extensions` and `grantedExtensionTools` as sets. A mismatch or a missing
289
+ entry counts as `settingsChanged`, starts no inner session and moves on to the
290
+ next writer; if no writer is admitted, the start fails with
291
+ `writer_unavailable` and reason `none_admitted`.
292
+
237
293
  ## Reports and finding codes
238
294
 
239
295
  Reports use sanitized targets `modes/workspace/<alias>` or `writer/<alias>/<name>`
@@ -283,12 +339,12 @@ listed below; dynamic planned and MCP verification outcomes are listed explicitl
283
339
  | `clone_verification_failed` | Clone layout/hook/config redirects unsafe |
284
340
  | `writer_state_invalid` | Missing/malformed/mismatched writer state |
285
341
  | `writer_state_unreadable` | Writer state cannot safely be read |
286
- | `writer_inspect_failed` | Contained work inspection failed |
342
+ | `writer_inspect_failed` | Contained work inspection failed, including an index that cannot be evaluated such as an unmerged conflict |
287
343
  | `writer_uncollected` | Changed work lacks a matching complete collection digest; destructive operation refused |
288
- | `writer_collect_failed` | Bundle generation/verification/import failed |
289
- | `writer_collect_incomplete` | One or more branch names refused; no cleanup authorization |
290
- | `ref_name_refused` | Unsafe export name or WIP name collision |
291
- | `nothing_to_collect` | No changed branch or WIP to export |
344
+ | `writer_collect_failed` | Bundle generation/verification/import failed, including an unmerged index |
345
+ | `writer_collect_incomplete` | One or more branch names refused, a collected commit or tree is missing from the operator checkout, or a tag, note or other unexported ref names an object only the clone has; no cleanup authorization |
346
+ | `ref_name_refused` | Unsafe export name, or a `-wip`, `-index`, `-detached` or `-stash-<n>` name collision |
347
+ | `nothing_to_collect` | No changed branch, WIP, index, detached commit or stash entry to export |
292
348
  | `writer_collected` | Complete export recorded |
293
349
  | `writer_reset_failed` | Reset command or validation failed |
294
350
  | `writer_reset` | Persistent clone reset and verified |
@@ -105,6 +105,15 @@ Providers registered only by Pi extensions are not listed; the report includes
105
105
  that note. Invalid catalog ids are omitted and counted in `omittedIds`.
106
106
  Catalog creation failures report `catalog_unavailable`.
107
107
 
108
+ The list includes the models in the catalog overlay that Pi persisted earlier
109
+ (`models-store.json` in Pi's agent directory, written when Pi refreshes its
110
+ model catalogs), restored offline. Pi Session MCP makes no request to refresh
111
+ it: run Pi once so that it refreshes its catalogs, and a model that only the
112
+ refreshed catalog offers becomes available to `--list-models`,
113
+ `--probe-profile` and sessions alike. Without that file only the models
114
+ shipped with the Pi SDK are listed. (ADR 0014 describes the original design
115
+ with a catalog that is created without this restore.)
116
+
108
117
  With `PI_SESSION_MCP_CONFIG` already set to the configuration to check:
109
118
 
110
119
  ```bash
@@ -225,12 +225,22 @@ Git metadata can redirect reads to worker-controlled configuration. Setup checks
225
225
  clone layout and runs clone-side Git only in throwaway no-network containers.
226
226
  Collect with `--writer <name> --collect --apply`, then review
227
227
  `refs/remotes/writer-<name>/<branch>` in your own checkout, with dirty work at
228
- `<branch>-wip`. Imported refs are untrusted until reviewed.
228
+ `<branch>-wip`, staged work that differs from both HEAD and the worktree at
229
+ `<branch>-index`, a clean detached HEAD at `<base>-detached` (for example
230
+ `main-detached`), and every stash entry at `<base>-stash-<n>`. Tags, notes and
231
+ other refs are not exported; one that names an object only the clone has makes
232
+ collection incomplete. Imported refs are untrusted until reviewed.
229
233
 
230
234
  After taking over the result and closing its coding session, run
231
235
  `pi-session-mcp-setup --writer <name> --finish --apply` before another task.
232
236
  It collects then resets (persistent) or recreates (ephemeral). If any branch name
233
- was refused, collection is incomplete and finish stops. Rename inside the writer
237
+ was refused, collection is incomplete and finish stops; a `<base>-detached`,
238
+ `<branch>-wip`, `<branch>-index` or `<base>-stash-<n>` name that an exported branch takes or whose path it conflicts
239
+ with is refused the same way. The release digest is
240
+ recorded only when the clone's HEAD commit, every branch commit and every stash
241
+ commit exist in the operator checkout and every other ref names an object the
242
+ operator checkout has, otherwise collection is `writer_collect_incomplete` and
243
+ no digest is recorded. Rename inside the writer
234
244
  or have the operator explicitly resolve loss with `--reset --discard --apply`.
235
245
  Finish returns persistent and ephemeral writers to the workspace default base.
236
246
  No reconnect is needed after finish, reset or recreate: each session starts a
package/docs/roadmap.md CHANGED
@@ -41,6 +41,27 @@ mean they are implemented or that their gates passed.
41
41
  profile), and the shipped controller skill `pi-session-profiles`, with which a
42
42
  controlling agent creates, changes or retires profiles without a guided
43
43
  dialog. Recorded in [ADR 0014](adr/0014-profile-probe-and-model-catalog.md).
44
+ - 2026-10-30: [Sprint 12](sprints/2026-10-29-s12-audit-fixes.md) fixes three
45
+ audit findings on `9d9cf8c` in one pull request, released in patch release
46
+ 0.14.1: read-only context files confined to the workspace
47
+ ([#192](https://github.com/plagor44-ux/pi-session-mcp/issues/192)), detached
48
+ `HEAD` collection and the release-digest gate
49
+ ([#193](https://github.com/plagor44-ux/pi-session-mcp/issues/193)) and the
50
+ full writer profile comparison in the relay
51
+ ([#194](https://github.com/plagor44-ux/pi-session-mcp/issues/194)). Levels 1
52
+ to 3 passed for the candidate. #120 still waits for a Pi SDK release.
53
+ - 2026-10-30: [Sprint 13](sprints/2026-10-30-s13-index-only-work.md) fixes the
54
+ loss of work that exists only in a writer's Git index
55
+ ([#200](https://github.com/plagor44-ux/pi-session-mcp/issues/200)) in one
56
+ pull request, released in patch release 0.14.2: inspection evaluates the
57
+ original index tree, the versioned collection digest covers it, and
58
+ collection exports staged-only work as `<branch>-index`. The published-release Level 2 of 0.14.1 passed. The
59
+ same loss for work that exists only in a stash entry is the follow-up
60
+ [#202](https://github.com/plagor44-ux/pi-session-mcp/issues/202).
61
+ - 2026-10-30: #202 is fixed in one pull request, released in patch release
62
+ 0.14.2: collection exports every stash entry as `<base>-stash-<n>`, and tags,
63
+ notes and other unexported refs must name objects the operator checkout
64
+ already has before a writer counts as baseline or a digest is recorded.
44
65
 
45
66
  The explicit 2026-10-17 user request adds a bounded development-only
46
67
  [token-footprint measurement](sprints/2026-10-17-token-footprint.md), following
@@ -4,9 +4,11 @@ Planning basis: main `47b8d26` (Sprint 7 merged), installed release 0.11.2.
4
4
  Tracking: [#79](https://github.com/plagor44-ux/pi-session-mcp/issues/79),
5
5
  prerequisite [#121](https://github.com/plagor44-ux/pi-session-mcp/issues/121).
6
6
 
7
- Status: **planned**. The maintainer made the choices on 2026-10-25; see
8
- [Decisions](#decisions-2026-10-25). Nothing below has been changed on any
9
- machine yet.
7
+ Status: **in progress** — steps 1–4 are done (pilot `pi-session-mcp`,
8
+ `node-app`, the operator images, go-app) and steps 5–7 are open; see
9
+ [Progress (2026-10-27)](#progress-2026-10-27) and the v0.11.6 and v0.11.8
10
+ post-release evidence in [Acceptance](../acceptance.md). The maintainer made
11
+ the choices on 2026-10-25; see [Decisions](#decisions-2026-10-25).
10
12
 
11
13
  ## Mandate
12
14