@timurproko/a1 0.2.6-dev.717 → 0.2.6-dev.727

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 (143) hide show
  1. package/bin/cli.js +13 -2
  2. package/bin/ui.js +13 -0
  3. package/dist/app/session-shell/prompt-suggestion-controller.d.ts +5 -4
  4. package/dist/app/session-shell/prompt-suggestion-controller.js +148 -58
  5. package/dist/app/session-shell/session-shell-root.d.ts +2 -1
  6. package/dist/app/session-shell/session-shell-root.js +13 -6
  7. package/dist/app/session-shell/session-shell.js +45 -3
  8. package/dist/app/session-shell/session-viewport-controller.js +33 -21
  9. package/dist/cli/dispatch.js +5 -3
  10. package/dist/cli/session-context.d.ts +5 -1
  11. package/dist/cli/session-context.js +25 -8
  12. package/dist/composition/owned-ui.d.ts +3 -0
  13. package/dist/composition/owned-ui.js +66 -6
  14. package/dist/composition/settings-route-host.js +2 -1
  15. package/dist/contracts/owned-ui/index.d.ts +4 -2
  16. package/dist/contracts/owned-ui/index.js +1 -1
  17. package/dist/contracts/owned-ui/model.d.ts +4 -0
  18. package/dist/contracts/owned-ui/suggestion-diagnostics.d.ts +5 -1
  19. package/dist/contracts/owned-ui/suggestion-diagnostics.js +3 -1
  20. package/dist/contracts/owned-ui/validation.js +7 -0
  21. package/dist/features/launch/index.d.ts +1 -1
  22. package/dist/features/launch/session-context.d.ts +17 -2
  23. package/dist/features/launch/session-context.js +9 -5
  24. package/dist/features/owned-ui/settings-app.js +21 -39
  25. package/dist/features/prompt-suggestions/diagnostics.js +4 -1
  26. package/dist/foundation/lifecycle/index.d.ts +2 -2
  27. package/dist/foundation/lifecycle/index.js +1 -1
  28. package/dist/foundation/lifecycle/session-repository-context.d.ts +19 -2
  29. package/dist/foundation/lifecycle/session-repository-context.js +434 -66
  30. package/dist/foundation/process-containment/index.d.ts +1 -0
  31. package/dist/foundation/process-containment/index.js +1 -0
  32. package/dist/foundation/process-containment/native-process-inspector.d.ts +3 -0
  33. package/dist/foundation/process-containment/native-process-inspector.js +21 -0
  34. package/dist/foundation/release/progress-palette.generated.d.ts +2 -2
  35. package/dist/foundation/release/progress-palette.generated.js +2 -2
  36. package/dist/foundation/startup/startup-descriptor.js +2 -2
  37. package/dist/integrations/pi/components/components.js +2 -2
  38. package/dist/integrations/pi/components/index.d.ts +2 -2
  39. package/dist/integrations/pi/components/index.js +1 -1
  40. package/dist/integrations/pi/components/modal-frame.js +3 -4
  41. package/dist/integrations/pi/components/models-dialog.js +5 -5
  42. package/dist/integrations/pi/components/prompt-input-port.d.ts +1 -0
  43. package/dist/integrations/pi/components/resources/builtin-themes.d.ts +2 -2
  44. package/dist/integrations/pi/components/resources/builtin-themes.js +2 -2
  45. package/dist/integrations/pi/components/share-operation-dialog.js +3 -3
  46. package/dist/integrations/pi/components/shell-editor-autocomplete.js +8 -7
  47. package/dist/integrations/pi/components/shell-extension-ui.d.ts +2 -0
  48. package/dist/integrations/pi/components/shell-extension-ui.js +20 -10
  49. package/dist/integrations/pi/components/shell-hotkey-sections.js +2 -2
  50. package/dist/integrations/pi/components/shell-presenters-info.js +10 -10
  51. package/dist/integrations/pi/components/shell-presenters-transcript.js +10 -10
  52. package/dist/integrations/pi/components/shell-selectors-dialogs.js +5 -6
  53. package/dist/integrations/pi/components/shell-shared-facade.js +1 -1
  54. package/dist/integrations/pi/components/skills-dialog.js +3 -3
  55. package/dist/integrations/pi/components/submitted-prompt-adapter.js +2 -3
  56. package/dist/integrations/pi/components/theme.d.ts +9 -0
  57. package/dist/integrations/pi/components/theme.js +30 -0
  58. package/dist/integrations/pi/components/tool-result-adapter.js +6 -1
  59. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.d.ts +1 -1
  60. package/dist/integrations/pi/components/upstream/adjacent/core/keybindings.js +1 -1
  61. package/dist/integrations/pi/components/upstream/assets/earendil-image.json +2 -2
  62. package/dist/integrations/pi/components/upstream/components/countdown-timer.d.ts +1 -1
  63. package/dist/integrations/pi/components/upstream/components/earendil-announcement.js +1 -1
  64. package/dist/integrations/pi/components/upstream/components/extension-editor.d.ts +1 -1
  65. package/dist/integrations/pi/components/upstream/components/extension-editor.js +6 -6
  66. package/dist/integrations/pi/components/upstream/components/extension-input.js +4 -4
  67. package/dist/integrations/pi/components/upstream/components/extension-selector.js +4 -4
  68. package/dist/integrations/pi/components/upstream/components/mermaid.js +1 -1
  69. package/dist/integrations/pi/components/upstream/components/owned-editor.d.ts +5 -5
  70. package/dist/integrations/pi/components/upstream/components/owned-editor.js +8 -5
  71. package/dist/integrations/pi/components/upstream/components/pi-logo.js +1 -1
  72. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.d.ts +1 -1
  73. package/dist/integrations/pi/components/upstream/components/scoped-models-selector.js +3 -3
  74. package/dist/integrations/pi/components/upstream/components/session-footer.js +1 -1
  75. package/dist/integrations/pi/components/upstream/components/session-selector-search.js +1 -1
  76. package/dist/integrations/pi/components/upstream/components/session-selector.js +9 -9
  77. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.d.ts +3 -2
  78. package/dist/integrations/pi/components/upstream/components/skill-invocation-message.js +6 -3
  79. package/dist/integrations/pi/components/upstream/components/status-indicator.d.ts +1 -1
  80. package/dist/integrations/pi/components/upstream/components/status-indicator.js +1 -1
  81. package/dist/integrations/pi/components/upstream/components/themed-text.d.ts +1 -1
  82. package/dist/integrations/pi/components/upstream/components/themed-text.js +1 -1
  83. package/dist/integrations/pi/components/upstream/components/thinking-selector.d.ts +1 -1
  84. package/dist/integrations/pi/components/upstream/components/thinking-selector.js +2 -2
  85. package/dist/integrations/pi/components/upstream/components/tool-execution.d.ts +4 -0
  86. package/dist/integrations/pi/components/upstream/components/tool-execution.js +11 -1
  87. package/dist/integrations/pi/components/upstream/components/tree-selector.d.ts +2 -2
  88. package/dist/integrations/pi/components/upstream/components/tree-selector.js +27 -18
  89. package/dist/integrations/pi/components/upstream/components/trust-selector.js +1 -1
  90. package/dist/integrations/pi/components/upstream/external-editor.js +1 -1
  91. package/dist/integrations/pi/components/upstream/history/editor-core.d.ts +4 -4
  92. package/dist/integrations/pi/components/upstream/history/editor-core.js +11 -7
  93. package/dist/integrations/pi/components/upstream/history/kill-ring.d.ts +1 -1
  94. package/dist/integrations/pi/components/upstream/history/kill-ring.js +1 -1
  95. package/dist/integrations/pi/components/upstream/history/printable-key.js +1 -1
  96. package/dist/integrations/pi/components/upstream/history/text-helpers.js +1 -1
  97. package/dist/integrations/pi/components/upstream/history/undo-stack.d.ts +1 -1
  98. package/dist/integrations/pi/components/upstream/history/undo-stack.js +1 -1
  99. package/dist/integrations/pi/components/upstream/history/word-navigation.js +1 -1
  100. package/dist/integrations/pi/components/upstream/model-search.d.ts +1 -1
  101. package/dist/integrations/pi/components/upstream/program-status-reporter.d.ts +31 -0
  102. package/dist/integrations/pi/components/upstream/program-status-reporter.js +89 -0
  103. package/dist/integrations/pi/components/upstream/theme/system-theme.d.ts +1 -1
  104. package/dist/integrations/pi/components/upstream/theme/system-theme.js +1 -1
  105. package/dist/integrations/pi/components/upstream/theme/theme-controller.d.ts +1 -1
  106. package/dist/integrations/pi/components/upstream/theme/theme.d.ts +18 -1
  107. package/dist/integrations/pi/components/upstream/theme/theme.js +207 -10
  108. package/dist/integrations/pi/engine/pi-settings-metadata.json +1 -1
  109. package/dist/integrations/pi/engine/resources/changelog.json +4 -4
  110. package/dist/integrations/pi/engine/session-events.js +10 -2
  111. package/dist/integrations/pi/engine/transcript-projection.js +2 -1
  112. package/dist/integrations/pi/startup-public.js +306 -290
  113. package/dist/integrations/pi/startup-public.manifest.json +289 -265
  114. package/dist/integrations/pi/tui-runtime/adapter.d.ts +2 -1
  115. package/dist/integrations/pi/tui-runtime/adapter.js +7 -0
  116. package/dist/integrations/pi/tui-runtime/contracts.d.ts +3 -1
  117. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.d.ts +1 -1
  118. package/dist/integrations/pi/tui-runtime/damage-aware-terminal.js +1 -1
  119. package/dist/native/darwin-arm64/manifest.json +1 -1
  120. package/dist/native/linux-x64/manifest.json +1 -1
  121. package/dist/native/win32-x64/manifest.json +2 -2
  122. package/dist/native/win32-x64/process-guardian.exe +0 -0
  123. package/dist/product-identity.d.ts +2 -2
  124. package/dist/product-identity.js +2 -2
  125. package/dist/product-identity.json +3 -1
  126. package/dist/runtime-payload-inventory.json +3092 -2878
  127. package/dist/ui/components/prompt-input.d.ts +2 -0
  128. package/dist/ui/components/prompt-input.js +5 -1
  129. package/dist/ui/components/text-selection.d.ts +1 -1
  130. package/dist/ui/components/text-selection.js +3 -1
  131. package/dist/ui/components/theme.d.ts +2 -0
  132. package/dist/ui/components/transcript-viewport.d.ts +6 -4
  133. package/dist/ui/components/transcript-viewport.js +94 -16
  134. package/dist/ui/components/value-menu.d.ts +2 -0
  135. package/dist/ui/components/value-menu.js +13 -8
  136. package/dist/ui/settings/declarations.d.ts +26 -14
  137. package/dist/ui/settings/declarations.js +24 -11
  138. package/dist/ui/settings/migrations.js +16 -0
  139. package/docs/architecture/project-structure.md +4 -3
  140. package/docs/architecture/prompt-suggestions.md +14 -10
  141. package/docs/local-worktree-cleanup.md +11 -5
  142. package/docs/openspec-archive-automation.md +6 -5
  143. package/package.json +3 -3
@@ -1,12 +1,14 @@
1
1
  # Prompt suggestion reliability
2
2
 
3
- Bare A1 predicts a short next user input with one isolated request using the selected model. A clear offered action consistent with recent user intent, such as `archive it` after accepted merged work, is preferred over abstaining merely because optional testing was also offered. Required validation, conflicting intent, and unresolved choices still allow no suggestion. There is no quoted-text extraction fallback.
3
+ Bare A1 predicts a short next user input with an isolated request using the selected model. A successful eligible response normally starts one pre-settlement request. If that prefetch was missed, settlement starts it; if the first current attempt produces no candidate, one sequential recovery request is allowed. A clear offered action consistent with recent user intent, such as `archive it` after accepted merged work, is preferred over abstaining merely because optional testing was also offered. Required validation, conflicting intent, unresolved choices, and exhausted recovery still allow no suggestion. There is no quoted-text extraction fallback.
4
4
 
5
5
  ## Request and editor boundaries
6
6
 
7
- The adapter resolves the lowest supported effort from the selected session model's available thinking levels, without changing the main thinking setting. Non-reasoning models omit reasoning controls; supported `off` also omits the optional reasoning argument, matching the model runtime's ordinary completion contract. Other supported efforts are supplied explicitly. Missing capabilities produce `unavailable`, not a guessed provider option. Provider mapping/clamping remains owned by the pinned model runtime; the diagnostic policy describes the applied request option, not inferred server-side computation.
7
+ The adapter preserves the selected session model's current reasoning level and request options without changing the main thinking setting. Non-reasoning models omit reasoning controls; supported `off` also omits the optional reasoning argument, matching the model runtime's ordinary completion contract. Missing capabilities produce `unavailable`, not a guessed provider option. Provider mapping/clamping remains owned by the pinned model runtime; the diagnostic policy describes the applied request option, not inferred server-side computation.
8
8
 
9
- The controller retains the 15-second deadline, does not retry or switch models, and retires a timeout before aborting. A provider that ignores abort cannot revive retired text. A timely candidate remains private until matching settlement. Ghost text is not editor content, approval, or a submission: Tab accepts it into the editor, and Enter must be pressed separately to submit it. Typing aborts pending generation but keeps a delivered suggestion: the ghost text is hidden while the editor holds a draft and reappears when the draft is cleared. Submission, session/model replacement, continuation, interruption, or disabling suggestions invalidates unaccepted work. `a1 pi` does not install this feature.
9
+ Each attempt retains the 15-second deadline and uses the same transformed conversation, tools, model, reasoning, budgets, session identity, transport, and payload hook. The controller never overlaps attempts or switches models. `empty`, `rejected`, `provider-failure`, `unavailable`, or timeout can start one retry after settlement; cancellation and stale identity cannot. A provider that ignores abort cannot revive its retired attempt.
10
+
11
+ A timely candidate remains private until matching settlement. A candidate blocked only by temporary readiness, focus, autocomplete, or prompt mode is retained and presented when the same editor becomes eligible. Ghost text is not editor content, approval, or a submission: Tab accepts it into the editor, and Enter must be pressed separately to submit it. Typing aborts pending generation but keeps a delivered suggestion: the ghost text is hidden while the editor holds a draft and reappears when the draft is cleared. Submission, session/model replacement, continuation, interruption, or disabling suggestions invalidates unaccepted work. `a1 pi` does not install this feature.
10
12
 
11
13
  ## Opt-in local diagnostics
12
14
 
@@ -26,12 +28,12 @@ While that session is running, inspect the selected JSON file using a local edit
26
28
  node -e 'const fs=require("node:fs"); console.table(JSON.parse(fs.readFileSync(process.argv[1],"utf8")).records)' .artifacts/suggestion-diagnostics.json
27
29
  ```
28
30
 
29
- The `prompt-suggestion-diagnostics-v1` snapshot contains at most 128 metadata records and 64 KiB. Capture is off by default. Records include process-local session/run/response/request numbers, bounded provider/model identifiers, applied reasoning policy, elapsed milliseconds, event, and optional reason. They exclude conversation/candidate text, tool data, raw errors, credentials, and session/worktree paths. There is no upload. Older records are evicted, so a retained terminal event can outlive its evicted start event. `request: 0` means no request was started for that decision.
31
+ The `prompt-suggestion-diagnostics-v1` snapshot contains at most 128 metadata records and 64 KiB. Capture is off by default. Records include process-local session/run/response/request numbers, bounded attempt number and trigger (`prefetch`, `settlement`, or `retry`), provider/model identifiers, applied reasoning policy, elapsed milliseconds, event, and optional reason. They exclude conversation/candidate text, tool data, raw errors, credentials, and session/worktree paths. There is no upload. Older records are evicted, so a retained terminal event can outlive its evicted start event. `request: 0` means no request was started for that decision.
30
32
 
31
33
  | Event | Meaning |
32
34
  | --- | --- |
33
35
  | `skipped` | No request started; inspect `reason`. |
34
- | `started` | One generator request was initiated; reasoning may be `unavailable` for an injected generator without policy metadata. |
36
+ | `started` | One generator request was initiated; inspect `attempt` and `trigger`. Reasoning may be `unavailable` for an injected generator without policy metadata. |
35
37
  | `displayed` | A current candidate was shown after settlement. |
36
38
  | `empty` | The provider returned no text; this does not reveal the model's private reason for abstaining. |
37
39
  | `rejected` | Returned content violated the candidate contract, contained tool calls, was truncated, or had invalid formatting/voice. |
@@ -40,7 +42,9 @@ The `prompt-suggestion-diagnostics-v1` snapshot contains at most 128 metadata re
40
42
  | `timeout` | The controller's 15-second deadline expired. |
41
43
  | `cancelled` | Work was invalidated or aborted, including typing or continuation. |
42
44
  | `stale-result` | A result or settlement identity did not match its request. |
43
- | `presentation-blocked` | A valid candidate could not be shown; inspect `reason`. |
45
+ | `presentation-blocked` | A valid candidate met a permanent presentation blocker; inspect `reason`. |
46
+ | `presentation-deferred` | A valid candidate is retained behind temporary readiness, focus, autocomplete, or prompt-mode state. |
47
+ | `retry-exhausted` | Both bounded attempts ended without a candidate. |
44
48
  | `late-result-discarded` | A retired request eventually resolved/rejected; this is not a second terminal outcome or request. |
45
49
 
46
50
  Eligibility/presentation reasons include `disabled`, `disposed`, `early-conversation`, `no-model`, `failed-response`, `incomplete-response`, `tool-continuation`, `replacement-input`, `modal`, `draft`, `not-ready`, `not-focused`, `autocomplete`, `prompt-mode`, and `stale-identity`. `ineligible` and `presentation-unavailable` cover injected ports without finer metadata. A missing suggestion alone is never evidence of timeout or intentional model abstention.
@@ -51,10 +55,10 @@ To disable capture, omit/unset `A1_SUGGESTION_DIAGNOSTICS` before the next launc
51
55
 
52
56
  ## Verification and evidence limits
53
57
 
54
- The focused tests cover the synthetic archive offer and required-testing/unresolved-choice counterexamples, unchanged primary thinking, typed outcomes, timeout/cancellation races, editor acceptance, and local privacy/bounds:
58
+ The focused tests cover settlement fallback, one-request success, bounded retry/exhaustion, timeout and late-result races, deferred presentation, request parity, editor acceptance, and local privacy/bounds:
55
59
 
56
60
  ```sh
57
- npx vitest run test/app/session-shell/prompt-suggestion-controller.test.ts test/integrations/pi/engine/adapter.test.ts test/features/prompt-suggestions/diagnostics.test.ts test/contracts/owned-ui/contracts.test.ts test/app/session-shell/session-shell.test.ts
61
+ npx vitest run test/app/session-shell/prompt-suggestion-controller.test.ts test/app/session-shell/session-shell-suggestions.test.ts test/integrations/pi/engine/adapter.test.ts test/integrations/pi/engine/session-events.test.ts test/features/prompt-suggestions/diagnostics.test.ts test/contracts/owned-ui/contracts.test.ts test/app/session-shell/session-shell.test.ts
58
62
  ```
59
63
 
60
64
  A separate opt-in probe uses the profile's saved model/authentication with a scratch cwd, in-memory sessions/settings, disabled extensions/skills/prompts, and no tools. It performs five original-policy predictions, five revised-policy predictions, and one required-testing counterexample. This makes **11 provider requests** and can consume quota. Run only with explicit authorization:
@@ -63,6 +67,6 @@ A separate opt-in probe uses the profile's saved model/authentication with a scr
63
67
  RUN_PROMPT_SUGGESTION_PROVIDER_TEST=1 PROMPT_SUGGESTION_AGENT_DIR="C:/path/to/agent-profile" npx vitest run test/integrations/pi/engine/prompt-suggestion-provider.integration.test.ts
64
68
  ```
65
69
 
66
- The probe reports outcome/latency summaries and archival-candidate counts, not raw text. It requires at least one revised archival continuation and rejects an archival continuation in the required-testing case. Five samples are a smoke comparison, not a statistical reliability guarantee. Missing credentials, model availability, or a successful real archival prediction leave provider acceptance pending; fake tests do not prove model quality.
70
+ The probe reports outcome/latency summaries and archival-candidate counts, not raw text. It requires at least one revised archival continuation and rejects an archival continuation in the required-testing case. Five samples are a smoke comparison, not a statistical reliability guarantee. It tests prompt quality, not settlement/retry lifecycle correctness; deterministic fault injection owns recovery acceptance. Missing credentials or model availability therefore do not block lifecycle evidence.
67
71
 
68
- For manual review, use a harmless synthetic conversation with two assistant responses: establish that an imaginary change was tested, accepted, and merged, then ask for a closeout offer containing `Say archive it` with optional extra testing. Do not ask the agent to perform real archival to test ghost text. Keep main thinking high and the editor untouched while waiting, inspect the outcome snapshot, check Tab/Enter separately, and repeat with typing cancellation and suggestions disabled. Verify required testing is not treated as optional. Real latency, model abstention, and existing context-reconstruction limitations can still leave the editor empty.
72
+ For manual review, repeat ordinary completed-work conversations with the editor untouched and diagnostics enabled. A normal candidate should show one `prefetch` request. A recovered miss should show either a first `settlement` request or a second `retry` request, followed by `displayed`; two failed attempts should end with `retry-exhausted`. Check that temporary focus/autocomplete blocking records `presentation-deferred` and reveals the same candidate after eligibility returns. Check Tab/Enter separately, then repeat with typing cancellation and suggestions disabled. Real latency, model abstention, and existing context-reconstruction limitations can still exhaust both attempts and leave the editor empty.
@@ -22,7 +22,13 @@ Every delivery session then starts, before it creates a worktree, with one bound
22
22
  node scripts/governance/local-worktree-cleanup.mjs sweep --repo D:/Git/a1
23
23
  ```
24
24
 
25
- `sweep` evaluates every released registration in one pass under the queue limits (100 registrations, 500 remote requests, a durable round-robin cursor) with a 180-second elapsed budget, since each merged candidate costs three evidence loads, and completes each candidate whose PR is verified merged using exactly the `complete` safeguards. It needs no `enable` and starts no process: its authority is each candidate's release. A candidate whose PR is open, draft, or not yet finalized reports `pending`; a candidate whose PR closed without merge reports `awaiting-discard` and is never touched; blockers report their exact reason. An old stop sentinel does not prevent a sweep, but `disable` run while a sweep is executing stops it before its next destructive step. If another session holds the mutation lock the sweep reports `mutation-busy` and the agent relays it as deferred rather than waiting. The JSON report carries a `lines` array with one relayable line per candidate and pruned branch, for example `#458 close-absent-and-skewed-cleanup: removed [worktree-removed, local-ref-removed]`. Pending or blocked results never delay the new delivery.
25
+ `sweep` evaluates every released registration in one pass under the queue limits (100 registrations, 500 remote requests, a durable round-robin cursor) with a 180-second elapsed budget, since each merged candidate costs three evidence loads, and completes each candidate whose PR is verified merged using exactly the `complete` safeguards. It needs no `enable` and starts no process: its authority is each candidate's release. A candidate whose PR is open, draft, or not yet finalized reports `pending`; a candidate whose PR closed without merge reports `awaiting-discard` and is never touched; blockers report their exact reason. An old stop sentinel does not prevent a sweep, but `disable` run while a sweep is executing stops it before its next destructive step. If another session is cleaning the same path or ref, sweep reports that candidate `deferred (resource-busy)` and continues with disjoint registrations. The JSON report carries a `lines` array with one relayable line per candidate and pruned branch, for example `#458 close-absent-and-skewed-cleanup: removed [worktree-removed, local-ref-removed]`. Pending, deferred, or blocked results never delay another candidate or the new delivery.
26
+
27
+ ### Concurrent cleanup scopes
28
+
29
+ Independent `complete`, `discard`, `retire-redundant`, sweep, and enabled queue operations may run at the same time when their canonical worktree paths and topic refs are disjoint. Long remote evidence reads, content traversal, Windows retry waits, generated-content purge, and non-force removal hold only the exact path/ref resource locks. The same candidate remains exclusive across ownership, hand-off, cleanup, empty-directory recovery, and branch pruning; a competing command receives `resource-busy` rather than racing the journal.
30
+
31
+ The existing `mutation.lock` now protects only brief atomic `state.json` transactions and remains at the same path so an older cleanup process already running during an upgrade is still honored. State-lock overlap is retried for a bounded interval. Fetches that update `refs/remotes/origin/develop` and exact local-ref changes coordinate only that shared ref operation; they do not serialize the surrounding candidate work. Every destructive boundary still repeats its existing evidence, identity, content, and compare-and-delete checks.
26
32
 
27
33
  ### Standalone documentation evidence
28
34
 
@@ -104,7 +110,7 @@ node scripts/governance/local-worktree-cleanup.mjs retire-redundant \
104
110
  --confirm-redundant
105
111
  ```
106
112
 
107
- The command requires an exact canonical, unlocked, non-current Git worktree on a normal topic branch; no cleanup registration; no pull request in any state for that branch; no remote topic ref; no unique commit beyond freshly fetched `origin/develop`; and no staged, unstaged, untracked, hidden-index, unknown ignored, nested-repository, submodule, or other protected content. Central-policy generated content is inspected and purged normally. Under the mutation lock the command revalidates every gate, journals the captured filesystem/HEAD/ref identity, removes the checkout non-forcibly, and compare-and-deletes only the unchanged local ref. It never removes a remote ref or enables broad cleanup authority.
113
+ The command requires an exact canonical, unlocked, non-current Git worktree on a normal topic branch; no cleanup registration; no pull request in any state for that branch; no remote topic ref; no unique commit beyond freshly fetched `origin/develop`; and no staged, unstaged, untracked, hidden-index, unknown ignored, nested-repository, submodule, or other protected content. Central-policy generated content is inspected and purged normally. Under its exact path/ref resource locks, with brief atomic state transactions, the command revalidates every gate, journals the captured filesystem/HEAD/ref identity, removes the checkout non-forcibly, and compare-and-deletes only the unchanged local ref. It never removes a remote ref or enables broad cleanup authority.
108
114
 
109
115
  A repeated exact confirmed invocation resumes a partial journal or reports `already-retired`. A branch with any pull request remains owned by `complete`/sweep or explicit closed-unmerged `discard`; an open, dirty, remote-backed, locally advanced, registered, locked, detached, protected, replaced, or ambiguous checkout remains retained. Agents never substitute manual deletion when this command blocks.
110
116
 
@@ -184,7 +190,7 @@ Before resuming a released worktree, acquire it:
184
190
  node scripts/governance/local-worktree-cleanup.mjs claim --repo D:/Git/a1 --id REGISTRATION_ID --generation CURRENT_GENERATION
185
191
  ```
186
192
 
187
- Use a private token for the new session. Claim, release, and cleanup share a per-repository lock. A successful claim prevents deletion until the new owner releases it. A deleting or completed candidate cannot be claimed. A path reused after completed cleanup requires a new explicit `register` operation.
193
+ Use a private token for the new session. Claim, release, hand-off, and cleanup share the candidate's exact path/ref resource locks and commit their state changes through brief repository-state transactions. A successful claim prevents deletion until the new owner releases it. A deleting or completed candidate cannot be claimed. A path reused after completed cleanup requires a new explicit `register` operation.
188
194
 
189
195
  A session crash does not release ownership. After separately confirming that the owner and all related processes have stopped, an explicit recovery claims it for review:
190
196
 
@@ -192,7 +198,7 @@ A session crash does not release ownership. After separately confirming that the
192
198
  node scripts/governance/local-worktree-cleanup.mjs recover --repo D:/Git/a1 --id REGISTRATION_ID --generation CURRENT_GENERATION --confirm-stopped
193
199
  ```
194
200
 
195
- Recovery never releases or deletes. The generation must still match. A Git worktree lock remains a veto. A leftover `mutation.lock` from a killed cleanup process is evicted only on proof: the holder writes its PID and refreshes a heartbeat in the lock every five seconds, and a later `handoff`, `sweep`, `complete`, `discard`, claim, or queue pass evicts the lock only when that heartbeat (or, for an old lock without one, the file's modification time) is more than two minutes old and the PID no longer exists. A live, unprobeable, or own PID keeps the lock, as does a fresh or unreadable-but-fresh file, and the operation reports `mutation-busy`. Each eviction is journaled as `lock-evicted-<time>-<id>.json` beside `state.json` with the evicted record; it releases no ownership and advances no journal step. A lock that stays `mutation-busy` therefore has a live holder: wait for it or stop that process first.
201
+ Recovery never releases or deletes. The generation must still match. A Git worktree lock remains a veto. A leftover repository-state or scoped resource lock from a killed cleanup process is evicted only on proof: the holder writes its PID and refreshes a heartbeat in the lock every five seconds, and a later operation needing that scope evicts the lock only when that heartbeat (or, for an old lock without one, the file's modification time) is more than two minutes old and the PID no longer exists. A live, unprobeable, or own PID keeps the lock, as does a fresh or unreadable-but-fresh file, and the affected operation reports `mutation-busy` for legacy/state contention or `resource-busy` for a candidate/ref scope. Each eviction is journaled as `lock-evicted-<time>-<id>.json` beside `state.json` with the evicted record; it releases no ownership and advances no journal step. A live scoped holder delays only commands needing that same path or ref.
196
202
 
197
203
  ## Outcomes and interruption handling
198
204
 
@@ -230,7 +236,7 @@ Stop watch with Ctrl+C, or request immediate stop authority from another termina
230
236
  node scripts/governance/local-worktree-cleanup.mjs disable --repo D:/Git/a1
231
237
  ```
232
238
 
233
- Disable writes a stop sentinel without waiting for a busy mutation lock. The worker checks it before each destructive step; an already executing Git operation cannot be undone. No later deletion starts while disabled. State and partial journals remain available for review and future explicit enablement.
239
+ Disable writes a stop sentinel without waiting for a busy state or resource lock. The worker checks it before each destructive step; an already executing Git operation cannot be undone. No later deletion starts while disabled. State and partial journals remain available for review and future explicit enablement.
234
240
 
235
241
  Dependency-free focused fixtures (temporary repositories only, no live PR mutation or watcher activation):
236
242
 
@@ -118,14 +118,15 @@ After finalization, replace it with the exact emitted paths:
118
118
  "version": 3,
119
119
  "change": "example-change",
120
120
  "archive": "openspec/changes/archive/2026-09-15-example-change/",
121
- "acceptanceManifest": "openspec/changes/archive/2026-09-15-example-change/acceptance.md"
121
+ "acceptanceManifest": "openspec/changes/archive/2026-09-15-example-change/acceptance.md",
122
+ "finalizedHead": "0123456789abcdef0123456789abcdef01234567"
122
123
  }
123
124
  ```
124
125
 
125
126
  </details>
126
127
  ````
127
128
 
128
- Version 3 forbids `specificationPr` and `archivePreparationTasks`. Both final paths must be absent for a draft or present and mutually consistent for a finalized candidate. Unknown fields, duplicate fences/JSON keys, unsafe paths, mismatched dates/change identities, and partial finalization fail closed.
129
+ Version 3 forbids `specificationPr` and `archivePreparationTasks`. The archive path, acceptance-manifest path, and exact `finalizedHead` binding must be absent for a draft or mutually consistent for a finalized candidate. Existing finalized records without the head binding are readable only for trusted normalization and cannot satisfy current-head validation. Unknown fields, duplicate fences/JSON keys, unsafe paths, mismatched dates/change identities, malformed SHAs, and partial finalization fail closed.
129
130
 
130
131
  ## Acceptance list
131
132
 
@@ -152,7 +153,7 @@ The `OpenSpec finalization` workflow (`.github/workflows/openspec-finalization.y
152
153
  - **Finalized but drifted:** a later commit edited the archived tasks, evidence, design, or deltas, or the acceptance list changed. Finalization reruns from the archived form under the same archive date; one `docs(openspec): refinalize <change>` commit replaces the manifest and resynchronized specs.
153
154
  - **Behind `develop`:** a restore commit returns the archive to its active form with the merge-base's spec bytes, a merge of `develop` follows, and finalization runs against the new tip. A merge conflict outside `openspec/` stops the run with `finalization-merge-conflict`; rebase or merge `develop` yourself and push.
154
155
 
155
- The commit is pushed with a lease on the head the run read, so a developer push in between makes the run report `retry` and the next event finishes the work. Only after the push does the workflow `PATCH` the body fence, and only when the body is unchanged since it was read. The workflow's own push and body edit trigger further runs that report `already-finalized`. Every pushed commit is either a merge of the exact `develop` tip or confined to the change's active path, its archive path, and its declared canonical specs.
156
+ The commit is pushed with a lease on the head the run read, so a developer push in between makes the run report `retry` and the next event finishes the work. Only after the push does the workflow `PATCH` the body fence with `finalizedHead` equal to that exact commit, and only when both the body and PR head still match the operation's expected values. The guaranteed head-binding change triggers native `pull_request: edited` validation even when a re-finalization reuses the same archive paths. The workflow's own body edit triggers further runs that report `already-finalized`; an intermediate run with a missing or stale binding defers before product suites and emits no protected aggregate. Every pushed commit is either a merge of the exact `develop` tip or confined to the change's active path, its archive path, and its declared canonical specs.
156
157
 
157
158
  Because the branch gains commits from the archive App, pull before pushing. A rebase that drops them is harmless: the next push is reconciled from whatever the head contains. Do not revert a finalization commit to make a fix; push the fix and let the workflow re-finalize. When the workflow fails, its summary names the finalization code (`tasks-incomplete`, `acceptance-*`, `delivery-known-gaps`, `openspec-operation`, `finalization-merge-conflict`, ...), nothing is pushed, and `Finalized delivery validation` on the unfinalized head reports that automated finalization is pending.
158
159
 
@@ -194,7 +195,7 @@ gh pr edit <pr> --body-file .artifacts/agent/openspec-pr-body.md
194
195
 
195
196
  Use repeated `--known-gap "exact disposition"` only for an actually reviewed explicit gap. Gaps remain visible in the committed manifest and do not become test results. Missing or ambiguous disposition blocks finalization.
196
197
 
197
- The operation validates the active change strictly, requires complete substantive tasks, runs the pinned OpenSpec archive/synchronization engine in isolation, verifies the resulting canonical specs, retains every archive artifact, computes deterministic content digests, writes `acceptance.md`, and applies only the allowed OpenSpec diff. Repeating it against identical finalized inputs is verification-only and byte-stable.
198
+ The operation validates the active change strictly, requires complete substantive tasks, runs the pinned OpenSpec archive/synchronization engine in isolation, verifies the resulting canonical specs, retains every archive artifact, computes deterministic content digests, writes `acceptance.md`, and applies only the allowed OpenSpec diff. Local preparation cannot know the eventual commit SHA, so trusted finalization normalizes the body with `finalizedHead` after the commit is present on the branch. Repeating the trusted operation against identical finalized inputs and a matching head binding is verification-only and byte-stable.
198
199
 
199
200
  Before a first finalization, canonical specs must still equal the selected target. On an already finalized head the command re-finalizes from the archived form: it resets the synchronized specs to the target's bytes, reapplies the deltas, and rewrites the manifest under the same archive path, reporting `refinalized` (or `would-refinalize` without `--write`). If `develop` advances, merge or rebase onto it and rerun; the archive engine, not a hand edit, must produce the synchronized spec and archive copy.
200
201
 
@@ -217,7 +218,7 @@ Unavailable, stale, automatic, unauthorized, conflicting, or contradictory prove
217
218
 
218
219
  Normal Development CI remains complete for the implementation once its reviewable candidate exists. An archive-shaped final diff does not select documentation-only validation because the authoritative version-3 association remains implementation-bound. Base-controlled readiness defers a ready head that still holds the active change without running test suites or emitting `Development validation required`. The finalization workflow pushes the finalized head and updates its implementation fence; that update starts one complete selected validation, while PR-level concurrency cancels any superseded eligible run.
219
220
 
220
- `pull_request` body edits rerun required CI. The ordinary finalized phase-free run exposes the stable protected aggregate directly; it does not wait for a lifecycle body edit. Documentation auto-merge's trusted owner also reevaluates lifecycle association. It preserves only an authorized human arm for the exact finalized version-3 head, never creates or exercises that arm, and disables stale, automated, malformed, draft, edited, or otherwise ineligible arms. Every publication entry point explicitly refuses version 3.
221
+ The trusted post-push `finalizedHead` body edit reruns required CI as a native pull-request event. The ordinary finalized phase-free run exposes every selected lane and the stable protected aggregate on the PR checks page. Manual `workflow_dispatch` remains branch diagnostic evidence: it lacks authoritative PR payload and cannot replace finalized-delivery, acceptance, selection, visibility, or protected aggregate evidence. Documentation auto-merge's trusted owner also reevaluates lifecycle association. It preserves only an authorized human arm for the exact finalized version-3 head, never creates or exercises that arm, and disables stale, automated, malformed, draft, edited, or otherwise ineligible arms. Every publication entry point explicitly refuses version 3.
221
222
 
222
223
  The OpenSpec archive workflow remains default-branch trusted. For version 3 it uses read-only contents, PR, and Actions access to report the integrated result; App credentials are unnecessary and are not minted for post-merge verification. For legacy candidates it retains its existing scoped App publication behavior. The OpenSpec finalization workflow is the one version-3 user of the archive App: it mints a short-lived installation token with `contents: write` and `pull_requests: write`, pushes only to the candidate's own branch under a lease, edits only the body fence, and revokes the token when the run ends.
223
224
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@timurproko/a1",
3
- "version": "0.2.6-dev.717",
3
+ "version": "0.2.6-dev.727",
4
4
  "description": "Standalone terminal workspace for supervised native and managed agents",
5
5
  "type": "module",
6
6
  "privateLaunchContract": "neutral-launch-v1",
@@ -65,8 +65,8 @@
65
65
  "typecheck:bin": "tsgo -p tsconfig.bin.json"
66
66
  },
67
67
  "dependencies": {
68
- "@earendil-works/pi-coding-agent": "1.0.4",
69
- "@earendil-works/pi-tui": "1.0.4",
68
+ "@earendil-works/pi-coding-agent": "1.1.0",
69
+ "@earendil-works/pi-tui": "1.1.0",
70
70
  "@silvia-odwyer/photon-node": "0.3.4",
71
71
  "chalk": "5.6.2",
72
72
  "cross-spawn": "7.0.6",