@evoclock/pi-agentic-driver 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/.agentic-driver/loop-until-done.defaults.json +5 -0
  2. package/README.md +315 -33
  3. package/config/merge-policy.v1.example.json +4 -0
  4. package/config/relay-authority.v1.example.json +7 -0
  5. package/config/treehouse.v1.json +4 -0
  6. package/docs/durable-driver-api.md +23 -0
  7. package/docs/durable-persistence.md +32 -0
  8. package/docs/merge-policy.md +45 -0
  9. package/docs/relay-authority.md +9 -0
  10. package/extensions/aidr.ts +3 -0
  11. package/extensions/harvest-lessons.ts +8 -0
  12. package/extensions/herdr-dispatch.ts +32 -3
  13. package/extensions/loop-until-done.ts +7 -0
  14. package/extensions/recovery-evidence.ts +3 -3
  15. package/extensions/treehouse-recycle.ts +47 -0
  16. package/janus/README.md +3 -6
  17. package/janus/janus/config.ts +2 -2
  18. package/janus/janus/providers.ts +2 -4
  19. package/janus/tests/providers.test.ts +2 -2
  20. package/{extensions → lib}/task-store-adapter.ts +1 -5
  21. package/package.json +161 -7
  22. package/scripts/enforcement/approval_ledger_pi.js +135 -0
  23. package/scripts/enforcement/attended_authority_guard.js +113 -0
  24. package/scripts/enforcement/board_claim_bridge_pi.js +10 -10
  25. package/scripts/enforcement/durable_assignment_pi.js +488 -0
  26. package/scripts/enforcement/durable_driver_pi.d.ts +51 -0
  27. package/scripts/enforcement/durable_driver_pi.js +254 -0
  28. package/scripts/enforcement/herdr_async_dispatch_pi.js +86 -20
  29. package/scripts/enforcement/herdr_async_seam_pi.js +90 -3
  30. package/scripts/enforcement/herdr_communication_pi.js +205 -31
  31. package/scripts/enforcement/herdr_lifecycle_pi.js +730 -81
  32. package/scripts/enforcement/linux_microvm_cutover_pi.js +3 -3
  33. package/scripts/enforcement/linux_microvm_remote_fixture.sh +0 -6
  34. package/scripts/enforcement/loop_until_done_guide.md +13 -0
  35. package/scripts/enforcement/loop_until_done_pi.js +214 -0
  36. package/scripts/enforcement/merge_policy.js +181 -0
  37. package/scripts/enforcement/outward_effect_pi.js +156 -0
  38. package/scripts/enforcement/recovery_evidence_review_pi.js +31 -3
  39. package/scripts/enforcement/storage_owner_lock_pi.js +66 -0
  40. package/scripts/enforcement/task_board_core_pi.js +13 -100
  41. package/scripts/enforcement/task_board_hash_pi.js +86 -0
  42. package/scripts/enforcement/treehouse_recycle_pi.js +145 -0
  43. package/scripts/harvest_lessons.js +219 -0
  44. package/scripts/relay/record_approval.mjs +10 -0
  45. package/scripts/relay/send_request.mjs +26 -0
  46. package/skills/architect-pi/SKILL.md +44 -0
  47. package/skills/arena-pi/SKILL.md +46 -0
  48. package/skills/automate-me-pi/SKILL.md +37 -0
  49. package/skills/benchmark-checklist-pi/SKILL.md +35 -0
  50. package/skills/blast-radius-pi/SKILL.md +24 -0
  51. package/skills/bro-pi/SKILL.md +17 -0
  52. package/skills/correct-pi/SKILL.md +36 -0
  53. package/skills/create-verification-skill-pi/LICENSE +21 -0
  54. package/skills/create-verification-skill-pi/SKILL.md +24 -0
  55. package/skills/decision-to-spec/SKILL.md +65 -0
  56. package/skills/figure-it-out-pi/SKILL.md +37 -0
  57. package/skills/harvest-lessons/SKILL.md +56 -0
  58. package/skills/harvest-lessons/harvest.example.json +8 -0
  59. package/skills/how/LICENSE +24 -0
  60. package/skills/how/WORKFLOW_BOUNDARIES.md +19 -0
  61. package/skills/how-pi/SKILL.md +33 -0
  62. package/skills/in-your-own-words-pi/SKILL.md +15 -0
  63. package/skills/interrogate-pi/SKILL.md +35 -0
  64. package/skills/ketchup-pi/SKILL.md +21 -0
  65. package/skills/maintain-verification-skill-pi/SKILL.md +37 -0
  66. package/skills/make-bot-ui-pi/SKILL.md +33 -0
  67. package/skills/no-comments-pi/SKILL.md +33 -0
  68. package/skills/poteto-help-pi/SKILL.md +50 -0
  69. package/skills/poteto-mode-pi/LICENSE +21 -0
  70. package/skills/poteto-mode-pi/SKILL.md +41 -0
  71. package/skills/poteto-mode-pi/playbooks.md +95 -0
  72. package/skills/principle-attack-the-premise-pi/SKILL.md +17 -0
  73. package/skills/principle-boundary-discipline-pi/SKILL.md +19 -0
  74. package/skills/principle-build-the-lever-pi/SKILL.md +19 -0
  75. package/skills/principle-encode-lessons-in-structure-pi/SKILL.md +17 -0
  76. package/skills/principle-exhaust-the-design-space-pi/SKILL.md +17 -0
  77. package/skills/principle-experience-first-pi/SKILL.md +17 -0
  78. package/skills/principle-explain-the-number-pi/SKILL.md +21 -0
  79. package/skills/principle-fix-root-causes-pi/SKILL.md +21 -0
  80. package/skills/principle-foundational-thinking-pi/SKILL.md +21 -0
  81. package/skills/principle-guard-the-context-window-pi/SKILL.md +19 -0
  82. package/skills/principle-laziness-protocol-pi/SKILL.md +19 -0
  83. package/skills/principle-make-operations-idempotent-pi/SKILL.md +21 -0
  84. package/skills/principle-migrate-callers-then-delete-legacy-apis-pi/SKILL.md +19 -0
  85. package/skills/principle-minimize-reader-load-pi/SKILL.md +19 -0
  86. package/skills/principle-model-the-domain-pi/SKILL.md +19 -0
  87. package/skills/principle-never-block-on-the-human-pi/SKILL.md +19 -0
  88. package/skills/principle-outcome-oriented-execution-pi/SKILL.md +19 -0
  89. package/skills/principle-prove-it-works-pi/SKILL.md +19 -0
  90. package/skills/principle-redesign-from-first-principles-pi/SKILL.md +19 -0
  91. package/skills/principle-separate-before-serializing-shared-state-pi/SKILL.md +19 -0
  92. package/skills/principle-sequence-verifiable-units-pi/SKILL.md +19 -0
  93. package/skills/principle-subtract-before-you-add-pi/SKILL.md +19 -0
  94. package/skills/principle-test-behavior-not-implementation-pi/SKILL.md +21 -0
  95. package/skills/principle-type-system-discipline-pi/SKILL.md +19 -0
  96. package/skills/public-pr-wording/SKILL.md +69 -0
  97. package/skills/public-pr-wording/pr_wording_config.example.json +22 -0
  98. package/skills/public-pr-wording/pr_wording_lint.py +249 -0
  99. package/skills/public-pr-wording/pr_wording_rules.json +60 -0
  100. package/skills/pulse/SKILL.md +4 -2
  101. package/skills/recall-pi/SKILL.md +29 -0
  102. package/skills/reflect-pi/SKILL.md +27 -0
  103. package/skills/setup-pstack-pi/SKILL.md +23 -0
  104. package/skills/setup-pstack-pi/assets/pstack-model-roles.v1.example.json +7 -0
  105. package/skills/sharpen-pi/SKILL.md +19 -0
  106. package/skills/show-me-your-work-pi/SKILL.md +27 -0
  107. package/skills/swarm-pi/SKILL.md +27 -0
  108. package/skills/tasks/SKILL.md +4 -2
  109. package/skills/tdd-pi/SKILL.md +21 -0
  110. package/skills/teach-pi/SKILL.md +21 -0
  111. package/skills/technical-writing-pi/SKILL.md +27 -0
  112. package/skills/typescript-best-practices-pi/SKILL.md +21 -0
  113. package/skills/unslop-pi/SKILL.md +27 -0
  114. package/skills/why-pi/SKILL.md +37 -0
  115. package/PROVENANCE.md +0 -83
  116. package/config/herdr-worker-repositories.v1.json +0 -9
  117. package/docs/codebase-inventory.md +0 -15
  118. package/docs/recovery-evidence-store.md +0 -9
@@ -0,0 +1,5 @@
1
+ {
2
+ "maxSteps": 200,
3
+ "maxDurationMs": 1800000,
4
+ "wake": { "mode": "until-done" }
5
+ }
package/README.md CHANGED
@@ -30,6 +30,16 @@ Features have different evidence levels: focused tests and independent review
30
30
  support the shipped contracts, while live host-dependent proof is stated only
31
31
  where it has actually run. A receipt or worker report is evidence, not approval.
32
32
 
33
+ Treehouse recycling is configured in `config/treehouse.v1.json`. Treehouse is a
34
+ separate Go CLI, not an npm dependency; the exact supported CLI version is pinned
35
+ there and checked before any return. Create worktrees from the repository root
36
+ with `treehouse get --lease --base main --branch <branch>`. The optional Pi
37
+ extension checks driver-held leases at session start and every five minutes.
38
+ It returns only idle, clean worktrees whose exact pushed head belongs to a
39
+ merged or closed PR. A missing PR, different head, unknown CLI version, dirty
40
+ checkout, or active process leaves the lease intact. It does not create
41
+ worktrees, clean files, merge branches, or remove slots.
42
+
33
43
  Extensions for [Pi](https://github.com/earendil-works/pi-coding-agent):
34
44
  advisory code review, controlled role communication, and governed isolation
35
45
  proofs for agentic workflows.
@@ -44,17 +54,22 @@ proofs for agentic workflows.
44
54
  |------|--------------|--------|
45
55
  | `code_phage` | Reviews a plan against a stated goal before code is written and advises your agent. | shipped |
46
56
  | `tasks` | Keeps a priority-ordered session plan, then lets the owner make selected items durable in `TASKS.md`. | shipped |
57
+ | `decision-to-spec` skill | Drafts one reviewable assignment from your explicit decision or a sourced, quoted relay, with observable outcomes and a stop point. | packaged skill |
58
+ | `agentic_decision_evidence` | Records the stated source of a decision (direct or relayed, with quote, where, when and relayer) for a private assignment; it verifies no identity and creates no authority. | merged, unreleased |
59
+ | `agentic_relay_check_request` | Checks whether a recorded, scoped relay covers each requested private action; read-only preflight, with authority checked again at the action boundary. | merged, unreleased |
60
+ | `agentic_merge_review_record` | Records independent Standards and Spec verdicts for an exact PR head; read only when the merge policy is `agents-after-checks-and-review`, and it merges nothing. | merged, unreleased |
47
61
  | `agentic_herdr_communication` | Submits one prompt and observes delivery and marked reports; never grants authority. | shipped |
48
62
  | `agentic_herdr_spawn_worker` | Starts one Pi worker in a pane or tab, with native confirmation. | shipped |
49
63
  | `agentic_aidr` | A remedy for AI;DR. Reviews writing for clarity, simplicity, brevity, and humanity. | shipped |
50
64
  | `agentic_linux_microvm_cutover` | Runs one job in a throwaway QEMU/KVM virtual machine on a Linux host, with a severity-tiered killswitch that stops escape attempts. | user-enabled, native confirmation |
51
- | `agentic_worker_dispatch` | Runs bounded worker journeys and observes worker liveness; recovery is not activated. | shipped |
65
+ | `agentic_worker_dispatch` | Runs bounded worker journeys and observes worker liveness. Each iteration is a durable checkpoint, so an interrupted journey resumes without resending an uncertain prompt; the `journeys` action lists the records. | shipped; durable recovery merged, unreleased |
66
+ | `agentic_journey_outward_effect` | Makes one confirmed Git push, or private draft PR create or update, for an existing journey; after an interruption it checks the remote and never repeats an uncertain write. | merged, unreleased, native confirmation |
52
67
  | `agentic_kanban_board` | Shows the workspace task board: lanes, flags, priorities, dependencies, and which cards can run. | shipped |
53
68
  | `agentic_kanban_board_write` | Adds cards to the board through the trusted writer, which records who authorized the work. | shipped |
54
69
  | `agentic_kanban_board_update` | Moves, closes, flags, edits, or removes cards on the board, always recording who authorized the change. | shipped |
55
70
  | `agentic_kanban_board_dispatch` | Claims an eligible board card for automated contained work and creates its assignment envelope. | shipped |
56
71
  | `agentic_kanban_pulse` | Checks for ready work and available capacity, then starts assignments under the board policy. | shipped |
57
- | `/agentic-recovery-evidence` | Opens private recovery evidence in the local TUI; it is not a model-callable tool or recovery authorization. | shipped, local review only |
72
+ | `agentic-recovery-evidence` | Opens private recovery evidence in the local TUI; it is not a model-callable tool or recovery authorization. | shipped, local review only |
58
73
  | Router | Capacity/policy routing for Board Pulse: seats with scope statements, tunable policy values, and a binding dispatch gate. | shipped |
59
74
  | Janus (bundled service) | Local Jev evaluation service on loopback: semantic ranking, the dispatch-gate judgment, redaction before every external call. See `janus/README.md`. | shipped |
60
75
 
@@ -62,7 +77,10 @@ proofs for agentic workflows.
62
77
  private evidence store are packaged foundations, not a running recovery
63
78
  ladder. Herdr 0.9.1 has no proven modal-safe nudge channel. No terminal
64
79
  `prompt` or `send-keys` fallback is enabled. Pending integrations below are
65
- not presented as live features.
80
+ not presented as live features. Worker dispatch persistence is on by
81
+ default and resumes interrupted journeys from durable checkpoints. Its crash
82
+ tests use a simulated Herdr adapter, a local Git remote and a simulated
83
+ GitHub endpoint, so recovery in a live deployed pane is not yet proven.
66
84
 
67
85
  ## Code review and planning
68
86
 
@@ -113,6 +131,48 @@ The result is a clean handoff from a session plan to durable work. Selected task
113
131
 
114
132
  </details>
115
133
 
134
+ <details>
135
+ <summary><strong>decision-to-spec, one reviewable assignment</strong> <em>(packaged skill)</em></summary>
136
+
137
+ The `decision-to-spec` skill turns a decision into one reviewable planning
138
+ brief. Invoke `/skill:decision-to-spec` or ask for the workflow explicitly.
139
+ Pi can load the skill description on its own; loading is not evidence of a
140
+ decision and is not permission to draft.
141
+
142
+ - **Source the decision.** The brief records either your direct instruction
143
+ or a relay that quotes your exact words with where and when you gave them.
144
+ A bare approval claim or an unsourced quote is not decision evidence.
145
+ `agentic_decision_evidence` structures the stated source; it verifies no
146
+ identity.
147
+ - **Relay private approvals.** A configured relayer can record your exact
148
+ answer, the question it answers, its source, channel, time, and bounded
149
+ scope. `agentic_relay_check_request` is a read-only preflight; each action
150
+ checks the record again. A relay attests that the configured relayer recorded
151
+ your words; it does not prove identity or authorize merges or publication.
152
+ See `docs/relay-authority.md`.
153
+ - **Settle what matters first.** The skill inspects the inventory, existing
154
+ behavior and the affected driver capabilities, then asks one round of
155
+ decisions that could change acceptance, scope, ownership, review or stop
156
+ conditions, each with a recommendation.
157
+ - **Make outcomes observable.** Success and meaningful failure are written as
158
+ Given/When/Then, with the highest feasible existing test seam and the
159
+ reason it would catch a broken implementation.
160
+ - **Bound the work.** Owner, author, an independent standards and boundary
161
+ reviewer, and an exact-artifact acceptance reviewer; exact paths and
162
+ effects, the model, data and runtime route, exclusions, and a stop point.
163
+ Implementation needs an identified Treehouse lease; drafting does not.
164
+
165
+ The output is a private planning brief for the requester, **not** a dispatch
166
+ record. It cannot approve a merge, release, deletion or force-push, write a
167
+ board card, create an issue, or change a provider route. Unresolved
168
+ decisions stay explicit instead of becoming assumed requirements.
169
+
170
+ Concept credit: Matt Pocock's `grill-with-docs`, `to-spec` and
171
+ domain-modeling skills, MIT (question-framing patterns only, no text
172
+ copied).
173
+
174
+ </details>
175
+
116
176
  **Available now:**
117
177
 
118
178
  - **task-ledger integration.** Agents can read and act within board card
@@ -162,7 +222,7 @@ contract, Keychain provisioning, and configuration.
162
222
  template contains no accounts and one disabled placeholder seat, so it loads
163
223
  out of the box and schedules nothing until you enable a real seat.
164
224
  - **User profile (precedence over defaults):**
165
- `~/.config/agentic-driver/router/profile.json`, or the path in the
225
+ the configured router profile path, or the path in the
166
226
  `AGENTIC_DRIVER_ROUTER_PROFILE` environment variable. A profile section
167
227
  replaces the matching defaults section wholesale; the merged config must
168
228
  validate or the router refuses to produce decisions (fails closed).
@@ -243,7 +303,7 @@ cd janus
243
303
  ```
244
304
 
245
305
  ```sh
246
- npm install
306
+ sfw npm install
247
307
  ```
248
308
 
249
309
  ```sh
@@ -275,9 +335,12 @@ configured Pi worker roles running under [Herdr](https://herdr.dev/) (validated
275
335
  against Herdr 0.9.1; the trust seam accepts the Homebrew-managed herdr binary
276
336
  across versions rather than pinning one).
277
337
 
278
- - **List and observe.** Worker roles are filtered to trusted repositories: a
279
- checked-in registry plus canonical-path validation. Unlisted or
280
- symlink-escaped repositories are denied.
338
+ - **List and observe.** The integration checks a repository-local
339
+ `config/herdr-worker-repositories.v1.json` first, then the same file in the
340
+ active Pi profile under `PI_CODING_AGENT_DIR` (the installed agent profile directory). If no
341
+ registry is available, spawning is refused and communication trusts only
342
+ the current repository; malformed registries are rejected. Canonical path
343
+ checks reject unlisted or symlink-escaped repositories.
281
344
  - **Submit once and observe separately.** A prompt returns an immediate,
282
345
  non-authorizing delivery receipt. The `delivery` action can report queued,
283
346
  delivered, answered, failed, unknown, or unattributed. A marked answer is
@@ -308,7 +371,19 @@ extension:
308
371
  - creates the split pane or labelled tab;
309
372
  - starts exactly one Pi agent in it;
310
373
  - verifies the role, model arguments, pane identity, and canonical repository;
311
- - requires native confirmation before changing layout or starting a process;
374
+ - resolves the model against the installed Pi roster and the active profile's
375
+ `config/model-route-policy.v1.local.json` before spawning; missing, invalid,
376
+ ambiguous, forbidden, or unbudgeted routes fail closed;
377
+ - optionally accepts one explicit worktree target: it must be the canonical Git
378
+ worktree root of that same registered repository and carry an active
379
+ Treehouse lease observed read-only, and it is revalidated after confirmation
380
+ and between mutations;
381
+ - requires native confirmation or a covering existing ledger grant before
382
+ changing layout or starting a process; native confirmation shows the exact
383
+ worktree target and lease identity when one is used;
384
+ - never treats a repository-only approval grant as covering a worktree target:
385
+ a ledger grant must cover the repository, action, model, role and exact target
386
+ path; a new target without that coverage requires native confirmation;
312
387
  - uses fixed argv with `shell: false` and no arbitrary Herdr or shell surface;
313
388
  - returns explicit `pane_created`, `tab_created`, or `agent_started` partial
314
389
  states when only part of the operation succeeds; and
@@ -357,6 +432,76 @@ silently, and return results as untrusted evidence.
357
432
 
358
433
  </details>
359
434
 
435
+ <details>
436
+ <summary><strong>loop-until-done, bounded session-task loops</strong> <em>(merged, unreleased)</em></summary>
437
+
438
+ `/loop-until-done <steps> <approved goal> :: <observable exit check>` gives
439
+ one already-authorized coordinator session task a fixed worker-dispatch budget.
440
+ Configure the limits and wake in `.agentic-driver/loop-until-done.defaults.json`.
441
+ The defaults allow at most 200 attempts or 30 minutes, whichever comes first.
442
+ Each attempt consumes a step before dispatch. After a successful exchange,
443
+ the default wake prompts a check immediately; an interval can delay that prompt.
444
+ Each wake asks the agent to check the original exit condition before another
445
+ dispatch. The per-session allowance cannot be extended or restarted. This
446
+ command does not dispatch board cards; Board Pulse owns those.
447
+
448
+ The extension, its defaults, and its guide are included in the 2.0.0 npm
449
+ package file list. See `scripts/enforcement/loop_until_done_guide.md`.
450
+
451
+ </details>
452
+
453
+ <details>
454
+ <summary><strong>durable dispatch, resume after interruption without resending</strong> <em>(merged, unreleased)</em></summary>
455
+
456
+ `agentic_worker_dispatch` keeps each journey on disk, so a crash or restart
457
+ in the middle of a journey neither loses its place nor repeats a prompt.
458
+ Persistence is on by default. It is built on Pi Durable, not on a separate
459
+ state machine.
460
+
461
+ - **One durable task per iteration.** Each journey iteration is one Pi
462
+ Durable task. The driver commits a checkpoint before every pane send, and
463
+ Pi Durable resumes the task after a restart. A caller can supply a
464
+ `journeyId` (letters, digits, `_` or `-`, up to 100 characters); ad-hoc
465
+ work gets a generated one.
466
+ - **A tag in every prompt.** Each dispatched prompt ends with
467
+ `[journey:<id>:<iteration>]`. After a restart the driver looks for that
468
+ tag in the pane before it decides anything.
469
+ - **Block, never resend.** An iteration that had not yet sent its prompt
470
+ rechecks worker liveness and asks for native confirmation again before it
471
+ sends. Once a send has started, recovery only observes. One clean tag
472
+ followed by one attributable role report settles the iteration. A tag
473
+ without a report is polled for about two minutes, then blocks. A missing
474
+ tag or an unreadable pane blocks: a missing tag is never proof that the
475
+ prompt did not arrive. An abandoned pending task with no caller waits at
476
+ most 30 seconds, then settles blocked.
477
+ - **Inspect journeys.** The `journeys` action lists bounded journey records
478
+ for the current repository (journey, iteration, state, task and card),
479
+ optionally filtered by role or journey ID. It grants nothing.
480
+ - **One store per session, one owner per store.** The extension opens a
481
+ private store for each stable Pi session ID on first use, not at import. A
482
+ lock records the owner's PID, hostname, process start time and release
483
+ token. A live competing owner is refused. A stale lock is reclaimed only
484
+ when its same-host owner is proven dead; unknown ownership, or a host whose
485
+ `ps` cannot report a process start time, refuses.
486
+ - **Keep the record small.** The index holds bounded identities and
487
+ attributed report bodies, not briefs or terminal snapshots. A report is
488
+ evidence, never owner completion.
489
+
490
+ **Evidence.** Crash tests kill the dispatch process before the send, after
491
+ the send and after the result, then restart it without help. Pane-history
492
+ tests cover five cases: tag found, tag found with a late report, tag
493
+ missing, history too short, and a wrapped tag. Only the first two settle;
494
+ the rest block. CI runs these on every pull request. They
495
+ exercise the real dispatch code against a simulated Herdr adapter. Recovery
496
+ in a live deployed pane, an independent live restart, and power-loss
497
+ durability (SQLite runs in NORMAL mode) are not yet proven. Node.js 22.19.0
498
+ or newer is required. See `docs/durable-persistence.md`.
499
+
500
+ Credit: Pi Durable (`@earendil-works/pi-durable`) 1.0.4 by Earendil Works,
501
+ MIT. It is installed as a pinned dependency, not copied into driver source.
502
+
503
+ </details>
504
+
360
505
 
361
506
  <details>
362
507
  <summary><strong>task board and Pulse, planned and automated work you can see</strong> <em>(released, 0.9.1)</em></summary>
@@ -394,6 +539,16 @@ worker journey does not claim board cards or mint board envelopes.
394
539
 
395
540
  Consuming an envelope releases the claim. It does not mark the card done.
396
541
 
542
+ External board clients submit `agentic-driver.board-claim-request.v2` to
543
+ `claimFromBoardRequest`. Receipts use `agentic-driver.board-claim-receipt.v2`,
544
+ route digests use `agentic-driver.board-claim-route.v2`, and asynchronous
545
+ transport receipts use `agentic-driver.async-transport.receipt.v2`. Clients
546
+ must use these names; prior request, receipt, and route contracts are rejected,
547
+ with no compatibility aliases. Unsupported request schemas fail closed.
548
+ Workspace capabilities live at
549
+ `.agentic-driver/capabilities.json`; route seats and providers use `board-`
550
+ and `board-router` identities.
551
+
397
552
  Claims and envelopes are kept in authenticated state beside `TASKS.md`. The
398
553
  derived `board.md` view shows active claims but never becomes an authority
399
554
  source.
@@ -407,8 +562,8 @@ host.
407
562
 
408
563
  Coming next:
409
564
 
410
- - contained Pulse execution through Testudo;
411
- - Vogelkop support for the shared card fields; and
565
+ - contained Pulse execution through an approved isolation boundary;
566
+ - expanded external board support for the shared card fields; and
412
567
  - our own Obsidian plugin for canonical `TASKS.md`.
413
568
 
414
569
  </details>
@@ -558,17 +713,6 @@ rules watch the same directories by design, and together they are stricter
558
713
  than either alone, so the growth-sampling trip is covered by automated tests
559
714
  rather than a live run.
560
715
 
561
- The repository carries an acceptance matrix of 79 scenarios, one executable
562
- test per row, covering every rule, every way a command can be invoked, and
563
- every threshold boundary. It grows as new scenarios come to light.
564
-
565
- ### A note on review quality
566
-
567
- A quantized GLM-5.3-Flash model running locally on an NVIDIA DGX Spark
568
- reviewed this work alongside two frontier reviews at high reasoning. The
569
- local model matched their coverage and found one issue they all missed.
570
- Small models on your own desk are worth taking seriously as reviewers.
571
-
572
716
  </details>
573
717
 
574
718
  <details>
@@ -632,6 +776,29 @@ continues, and the agent does not retry behind your back. In headless runs
632
776
  where no human can confirm, destructive commands are refused rather than
633
777
  silently allowed.
634
778
 
779
+ **Merge policy** *(merged, unreleased)*. The guard also enforces one merge
780
+ setting, read from `{PI_CODING_AGENT_DIR}/config/merge-policy.v1.json`
781
+ (the installed agent config directory) and never from the working tree.
782
+
783
+ - **`human-merges-only` is the default.** You merge on GitHub; every agent
784
+ merge command is refused. A missing, unreadable or unknown policy falls
785
+ back to this value.
786
+ - **`agents-after-checks-and-review` is opt-in.** An agent may run a plain
787
+ `gh pr merge <number or URL>` only when every check on the PR head has
788
+ passed and `agentic_merge_review_record` holds a Standards PASS and a Spec
789
+ PASS for that exact head. Neither reviewer may be the PR author or the
790
+ merging session. A new push needs a new record.
791
+ - **Bypasses are refused under both values.** Admin and auto merges, the
792
+ REST merge endpoint, the `mergePullRequest` mutation, and writes to branch
793
+ protection or rulesets. A refusal starts with
794
+ `pi-agentic-driver.merge-policy.v1: needs-owner-approval:` so a
795
+ coordinator can pass it up as your decision.
796
+
797
+ This is a workflow guard, not a security boundary. It reads command text, so
798
+ a merge through shell indirection, another tool or the GitHub web page is
799
+ not seen. Branch protection on GitHub remains the real gate. See
800
+ `docs/merge-policy.md`.
801
+
635
802
  </details>
636
803
 
637
804
  **Under development in this theme:**
@@ -675,6 +842,49 @@ silently allowed.
675
842
 
676
843
  *Extensions that keep routine Git low-friction and consequential Git guarded.*
677
844
 
845
+ **Available now:**
846
+
847
+ <details>
848
+ <summary><strong>outward effects, confirmed Git push and draft PR recovery</strong> <em>(merged, unreleased, native confirmation)</em></summary>
849
+
850
+ The `agentic_journey_outward_effect` tool makes one consequential Git or
851
+ GitHub write for an existing journey: a push, a private draft pull request
852
+ creation, or a draft pull request update. It records what it is about to do
853
+ before it does it, so a restart can tell a planned write from an uncertain
854
+ one.
855
+
856
+ - **Record intent first.** Each effect is its own Pi Durable task, committed
857
+ before Git or GitHub is called. A push binds the branch, the expected
858
+ remote head (none for a new branch) and the target commit. A PR call binds
859
+ the head, branch, base, and digests of the title and body; an update also
860
+ binds the PR number and the prior body digest. The PR body is used for
861
+ that call only and is not stored.
862
+ - **Observe, never replay.** After an interruption the tool reads the remote
863
+ with `git ls-remote` and `gh api` and never repeats an in-flight write. The
864
+ exact target commit, or the exact PR fields, verify the effect. A missing
865
+ branch is not proof that a push failed. A changed branch, more than one
866
+ matching PR, or mismatched PR text blocks the journey.
867
+ - **Keep transient failures safe.** If observation fails after a write
868
+ landed, that effect ID settles blocked and cannot retry. A newly confirmed
869
+ effect ID can then observe the landed result and verify it without a
870
+ second write.
871
+ - **Stay narrow.** Every call needs native TUI confirmation and a verified
872
+ private GitHub repository, and the tool cannot push to `main` or `master`.
873
+ Each Git or GitHub subprocess has a 15-second timeout and a 16 MiB output
874
+ limit; a timeout or oversized output blocks and proves nothing either way.
875
+ - **Cover driver-owned calls only.** Git and GitHub commands that a worker
876
+ runs in its own shell are not intercepted.
877
+
878
+ **Evidence.** Crash tests kill the process before and after each push, PR
879
+ creation and PR update. They also cover a changed branch, multiple matching
880
+ PRs, mismatched PR text, and exact results that already landed despite local
881
+ drift. They use a local Git remote and a simulated GitHub endpoint, not a
882
+ live repository. The tool reuses the durable dispatch store and its owner
883
+ lock (see durable dispatch under Multi-agent communication). See the
884
+ outward effects section of `docs/durable-persistence.md`.
885
+
886
+ </details>
887
+
678
888
  **Planned:**
679
889
 
680
890
  - **git workflow safeguards.** Not packaged yet. The planned capability covers
@@ -695,7 +905,7 @@ silently allowed.
695
905
  injection, data-exfiltration endpoints, untrusted integrations, PII
696
906
  leakage, and OWASP/MCP threat families, with accept/redact/reject
697
907
  decisions. Built on the agent-scanner approach proven in Hillstar
698
- Orchestrator and Testudo.
908
+ Orchestrator and an isolation environment.
699
909
  - **checkpoint storage lifecycle.** Compression, deduplication, retention,
700
910
  and purging rules for capsule and index stores once a product ships, so
701
911
  session evidence has a managed lifetime instead of growing without bound.
@@ -712,6 +922,53 @@ The `templates/AGENTS.md` file is a starting contract you can copy into any
712
922
  repository that uses Pi agents. It asks for controlled tasks and realistic tests, and
713
923
  treats agent reports as untrusted evidence. It never overwrites an existing `AGENTS.md`. Add your project rules below it.
714
924
 
925
+ ## Packaged skills
926
+
927
+ *Methods your agent loads on request. A skill never grants authority.*
928
+
929
+ <details>
930
+ <summary><strong>Packaged skills, driver workflows and pstack ports for Pi</strong> <em>(merged, unreleased)</em></summary>
931
+
932
+ The package registers 59 skills alongside the extensions. Invoke one with
933
+ `/skill:<name>`. Pi may load a skill description on its own; loading is not
934
+ approval to act, and no skill grants permission to merge, publish, install
935
+ or change a model route.
936
+
937
+ - **Driver workflows.** `tasks` (session plan to durable board), `pulse`
938
+ (Board Pulse), `decision-to-spec` (one reviewable assignment),
939
+ `harvest-lessons` and `public-pr-wording`.
940
+ - **harvest-lessons.** Mines past corrections in an approved repository: PR
941
+ reviews, revert and fixup commits, failed CI and session logs. It groups
942
+ them into patterns, ranks them, and hands the top ones to `correct-pi` as
943
+ guarded draft PRs. Its `agentic_harvest_lessons` collector is read-only and
944
+ never writes files, posts or opens PRs.
945
+ - **public-pr-wording.** Drafts PR titles and bodies in four sections, in
946
+ the repository owner's first person, with no bot or model names and no
947
+ local paths. The bundled lint enforces length caps and wording rules; your
948
+ own names go in a personal config that is never committed.
949
+ - **correct-pi.** Turns an owner-selected recurring correction into a
950
+ structural guard, then requires a check that catches a real past failure.
951
+ - **pstack ports for Pi.** 54 skills adapted from Lauren Tan's pstack, each
952
+ with a `-pi` suffix so it does not collide with an installed upstream
953
+ copy: 30 workflow skills, such as `poteto-mode-pi`, `tdd-pi`,
954
+ `blast-radius-pi` and `show-me-your-work-pi`, and 24 `principle-*-pi`
955
+ skills. All follow the shared rules in `skills/how/WORKFLOW_BOUNDARIES.md`.
956
+ A skill that delegates to a model role reads the project-local
957
+ `.pi/pstack-model-roles.v1.json` created by `/skill:setup-pstack-pi`, and
958
+ stops if the role is missing; it never picks a model on its own.
959
+ - **Packaged-skill checks.** Public CI verifies skill frontmatter and package
960
+ registration, then loads the packed artifact in a fresh consumer and checks
961
+ for Pi skill diagnostics. This is release validation, not an install-time
962
+ permission or security boundary.
963
+
964
+ `docs/pstack-pi-ports-comparison.md` compares these ports with the other
965
+ published pstack ports for Pi.
966
+
967
+ Credit: pstack by Lauren Tan, MIT, adapted from `cursor/plugins` at
968
+ `99743e9`. Each ported skill names its upstream source and commit.
969
+
970
+ </details>
971
+
715
972
  ## Install
716
973
 
717
974
  From npm:
@@ -728,30 +985,55 @@ pi install git:github.com/evoclock/pi-agentic-driver@<latest-tag>
728
985
 
729
986
  Released extensions load independently.
730
987
 
731
- ### Choose which extensions load
988
+ For optional structural code search, see the separate [public ast-sgrep fork](https://github.com/evoclock/ast-sgrep). It is not bundled with or installed by Pi Agentic Driver. The Pi BigInt and host TypeBox corrections are on the fork's `main`; a follow-up Pi peer-range correction remains on [upstream PR #36](https://github.com/AdityaVG13/ast-sgrep/pull/36), not yet on the fork's `main`. These source changes have not been published as an npm package. The driver does not select or install a code-search provider.
732
989
 
733
- A normal install loads all packaged extensions by default. You can load only the
734
- extensions you want with the object form in your Pi settings:
990
+ ### Choose which package resources load
991
+
992
+ A normal install loads the package's declared extensions and skills. Use the
993
+ object form in your Pi settings to select only the resources you need. For
994
+ example, load two extensions:
735
995
 
736
996
  ```json
737
997
  {
738
998
  "packages": [
739
999
  {
740
1000
  "source": "npm:@evoclock/pi-agentic-driver",
741
- "extensions": [
742
- "extensions/aidr.ts",
743
- "extensions/code-phage.js"
744
- ]
1001
+ "extensions": ["extensions/aidr.ts", "extensions/code-phage.js"]
745
1002
  }
746
1003
  ]
747
1004
  }
748
1005
  ```
749
1006
 
750
- The package includes the Herdr communication, lifecycle, and dispatch
751
- extensions, the board and Tasks extensions, and `linux-microvm.ts`. Use
752
- `pi config` to enable or disable individual resources from an installed
1007
+ To load one skill and no extensions, set `extensions` to an empty list:
1008
+
1009
+ ```json
1010
+ {
1011
+ "packages": [
1012
+ {
1013
+ "source": "npm:@evoclock/pi-agentic-driver",
1014
+ "extensions": [],
1015
+ "skills": ["skills/harvest-lessons"]
1016
+ }
1017
+ ]
1018
+ }
1019
+ ```
1020
+
1021
+ The same package can provide skills without extensions; there is no separate
1022
+ skills-only package. Select another declared skill or omit `skills` to load all
1023
+ of them. The package also includes the Herdr communication, lifecycle, and
1024
+ dispatch extensions, the board and Tasks extensions, and `linux-microvm.ts`.
1025
+ Use `pi config` to enable or disable individual resources from an installed
753
1026
  package. You are not required to use every extension.
754
1027
 
1028
+ <details>
1029
+ <summary>Curate an agents-to-public sync</summary>
1030
+
1031
+ From a clean agents branch based on `main`, provide private deny terms with `--terms PATH`, `PUBLIC_SYNC_TERMS_FILE`, `config/public-sync-terms.local.json`, or `~/.config/pi-agentic-driver/public-sync-terms.local.json`, in that order. The scanner also reads private repository names from the existing repository registry. Missing local terms block the run. Existing public test fixtures have exact path-and-value-hash allowances in `config/public-sync-fixture-allowances.json`; all other secret matches block the run. The regenerated public lockfile has hashed, entry-scoped allowances for third-party dependency metadata; all other model or provider matches still block. Run `python3 scripts/public_sync.py --public /path/to/public-checkout` to inspect a dry-run report. The checked-in `config/public-sync-exclusions.json` filters private-only paths. Any sensitive content or excluded `package.json` file entry blocks the run; resolve it separately before retrying.
1032
+
1033
+ After a clean report, `--apply --branch sync/agents-curated` requires the source checkout at agents `main` with a clean tracked tree. It leases a fresh Treehouse worktree from the existing public checkout's `main`, copies the filtered tree, checks `npm test` and `npm pack --dry-run`, checks packed filenames, and makes one freshly authored commit. The script never pushes, creates remotes, or changes push URLs. Repeated clean dry runs are read-only; a no-change apply does nothing. Inspect the diff and packed contents independently before any owner-controlled public handoff. A failed apply leaves its leased worktree for inspection.
1034
+
1035
+ </details>
1036
+
755
1037
  ## License
756
1038
 
757
1039
  AGPL-3.0-only with author-attribution additional terms (Section 7(b));
@@ -0,0 +1,4 @@
1
+ {
2
+ "schema": "pi-agentic-driver.merge-policy.v1",
3
+ "policy": "human-merges-only"
4
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "schema": "relay-authority.v1",
3
+ "githubOwner": null,
4
+ "relayers": [],
5
+ "revokedIds": [],
6
+ "denyRepositories": ["external-board-*", "self-healing-swarm"]
7
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "version": "v3.1.3",
3
+ "pollIntervalMs": 300000
4
+ }
@@ -0,0 +1,23 @@
1
+ # Durable driver run protocol v1
2
+
3
+ Production import: `@evoclock/pi-agentic-driver/scripts/enforcement/durable_driver_pi.js` (the package includes this path). `openDurableDriver({ directory })` opens a private, canonical, same-owner directory containing a pi-durable 1.0.4 SQLite database. One process owns the directory at a time; `close()` releases it. After a process crash, reopening uses the existing exclusive owner-lock reclamation and the **real** pi-durable `Harness.resume()`; neither a fixture transport nor an opt-in persistence flag is involved. A concurrent owner or ambiguous lock fails closed.
4
+
5
+ Recovery tests cover process crashes only. The SQLite adapter uses `PRAGMA synchronous = NORMAL`; power-loss and host-crash durability are neither tested nor guaranteed here.
6
+
7
+ `stepIds` are stable ledger correlation identities, not execution task IDs. Runs written by older versions retain their registration `taskId` values and return those IDs on repeated admission; those tasks do not prove host execution. New runs have no registration tasks. Host execution state and submitted evidence are stored in the run ledger, read through `status()`, and updated through `report()` or `reconcile()`. The consumer type contract is `scripts/enforcement/durable_driver_pi.d.ts`; old run records can lack the per-run `schema` field, while new runs store `agentic-driver.run.v1`.
8
+
9
+ `schema` and exported `DURABLE_DRIVER_PROTOCOL` are `agentic-driver.run.v1`. Methods:
10
+
11
+ - `start({runId, steps:[{id,kind}]})`: atomically create a run and ledger correlation ID per step. `kind` is `internal` or `external`. Repeated identical admission returns the same `{runId,digest,stepIds}`; changed identity fails. At most 64 active or held runs are admitted; finished and cancelled history remains addressable and does not consume live capacity.
12
+ - `resume(runId)`: return the run and make only its first unfinished step ready. For an external step it first persists `intent` and returns the stable `requestKey`; repeated `resume` by the same open driver returns the live intent without parking it. After reopening with a new owner, `resume` parks an unreceipted intent as `held`. Never infer from a second resume that the external effect should be sent again.
13
+ - `status(runId)`: read a snapshot with `state` and ordered `steps` (`id`, `kind`, `correlationId` or legacy `taskId`, `requestKey`, `state`, optional `receipt`). An unknown valid run returns `null`; an invalid ID still rejects.
14
+ - `report(runId,{stepId,requestKey,receipt?})`: finish the active internal step after its work is verified, or record a verified external receipt `{requestKey,verified:true,reference}` against the persisted intent. Duplicate identical reports are idempotent. A report is *evidence submitted by the caller*, not proof the effect happened.
15
+ - `reconcile(runId,{stepId,requestKey,verified,reference?})`: after host observation of a remote effect, finish an intent/held step only with matching identity and positive evidence; otherwise leave it held. A cancelled run rejects reconciliation without changing its steps; the host must retain independent evidence for an already sent effect. **Never resend on an unknown outcome**. A host must authenticate the observation and validate the exact request identity and remote reference before passing it here.
16
+ - `release(runId,{stepId,requestKey,verifiedNotSent:true,reference})`: explicitly reopen a held external intent only after the host independently proves the effect was never sent. Persist the proof reference and replace the request key with a fresh attempt identity; old-key reports are rejected. This is not a recovery path for an unknown outcome. The caller-provided flag/reference is not independent proof or authorization.
17
+ - `cancel(runId)`: durably park the run; does not cancel or undo an already issued outward action. The host must reconcile independently before interpreting cancellation as external non-delivery.
18
+ - `invokeWorker({requestId,goal,transport,signal})`: persist one external intent for the entire host-owned worker/review/verification invocation before calling the configured transport. The opaque, already-compacted `goal` and stable `requestId` determine the immutable step identity. The transport receives that identity, a driver request key and the abort signal; it must return a host-verified reference and its application-owned result. A changed goal for the same request refuses. A repeated finished invocation returns its step receipt, **not** its result: the host must recover that from its own durable result store. An interrupted intent is held on reopen and is never dispatched again. `reconcile()` accepts a matching independently observed reference; release remains subject to verified non-delivery. This does not checkpoint individual turns or verify effects inside the host.
19
+ - `close()`: finish local storage use and release the exclusive lock; refuses while a worker invocation is active.
20
+
21
+ The driver persists identities, checkpoints and receipts and calls a required application-supplied worker transport; it does not possess a forge credential, independently verify the host's receipt, or grant admission authority. Hosts must verify their destination and effects, keep the returned request key stable, and serialize external execution with the owner lock. They must not treat a caller-provided `verified` bit as authorization or independent proof. This interface never selects a transport on its own; attaching a configured, authorized live transport and reconciling its internal effects are the consuming application's responsibility. Per-turn checkpoints are deferred unless a later crash drill shows they are needed. Cancelled runs refuse further ledger reconciliation.
22
+
23
+ A process-kill test exercises the installed pi-durable storage: the first process commits an internal completion and an outward intent, dies without closing, and a new process reopens the same directory. The finished step stays finished; the outward intent becomes held until an explicit observation is supplied.
@@ -0,0 +1,32 @@
1
+ # Driver dispatch persistence
2
+
3
+ The driver uses Pi Durable 1.0.4 tasks and a small document index. No separate skill is required. The worker-dispatch extension opens its private store on the first dispatch or journey listing, not during import or extension registration. The separate async transport seam remains explicit opt-in.
4
+
5
+ Only `openDurableSeamStore` is the supported export. The colocated `openDurableAssignmentFixture` export is development-only and exercised from test fixtures; production never calls it.
6
+
7
+ Trusted integration code can import `openDurableSeamStore` from `scripts/enforcement/durable_assignment_pi.js` and explicitly open an already-created, canonical private directory owned by the current user. Close the returned handle during teardown. The dispatch extension creates a separate private directory for each stable Pi session ID. A lock records the owner's PID, hostname, process start time and release token. A competing live owner refuses. A proven dead same-host owner (or a different process at that PID) permits one gated stale-lock reclaim; a concurrent or interrupted reclaim refuses until reconciled. Normal close releases the lock. The gate protects the rename-and-claim window; unknown ownership still refuses.
8
+
9
+ Worker dispatch accepts a caller-supplied `journeyId` or generates one for ad-hoc work. `journeys` lists bounded records for the repository (optionally filtered by role or journey ID). Every dispatched prompt carries a journey/iteration marker; an interrupted send does not automatically resend. A missing marker or unreadable pane blocks the iteration without replaying a prompt. One clean journey tag followed by one attributable role report settles the interrupted iteration. With a tag but no report, the tool polls for up to about two minutes, then blocks without resending. An admitted checkpoint has not sent a prompt; after restart it checks worker liveness and requests fresh native confirmation before sending. In-flight and later checkpoints only observe the pane and never resend. An abandoned pending task without a caller waits at most 30 seconds before Pi Durable settles it blocked. The offline async transport seam still requires an injected test process adapter. The process-kill tests exercise actual dispatch code with a simulated Herdr adapter, not a live pane or deployed restart.
10
+
11
+ `defineTask` owns each iteration checkpoint, and `runtime.commit` records effect intent before the Herdr call; `Harness.resume()` and `waitForTask()` schedule and await recovery.
12
+
13
+ ### Outward effects
14
+
15
+ The production dispatch extension registers `agentic_journey_outward_effect` for an existing journey. It commits a separate durable task before a confirmed push, private draft PR creation, or draft PR update.
16
+
17
+ - A push binds its branch, expected remote head (or null for a new branch), and target commit.
18
+ - A PR call binds its remote head, branch, base, and title/body digests. Updates also bind the PR number and prior body digest.
19
+ - Recovery observes the remote state. It never repeats an in-flight effect. Missing, changed, or multiple PR observations block; a missing branch is not proof that a push failed.
20
+ - A transient observation failure after a landed effect settles that effect ID as blocked. Reusing it cannot retry. A newly confirmed effect ID can observe the exact landed result and verify without another write.
21
+
22
+ If apply fails locally but observation proves that the exact target commit or PR fields already exist, verification is intentional. The tests cover both a changed local HEAD with an already-pushed target and a stale prior-body digest with an already-updated PR. The tool requires native TUI confirmation and verified private GitHub identity; it cannot push to main or master. Worker shell commands are not intercepted. The PR body is supplied only for that call, not stored in the index.
23
+
24
+ Each synchronous Git/GitHub subprocess has a 15-second timeout and a 16-MiB output limit, enough for the requested 100-PR page. It blocks the harness event loop and cannot immediately honor a cancellation signal. Timeout or oversized output fails closed; neither proves an effect did not happen.
25
+
26
+ The small document index maps stable journey identities to durable task IDs. Storage holds bounded identities and attributed report bodies, not briefs or terminal snapshots. Both namespaces stop at 64 records without evicting send tombstones. Losing original attribution after restart holds rather than replaying. Already-committed proven reports can reopen. A report is evidence, never owner completion. Cancellation remains best-effort; received acknowledgements cannot retract effects.
27
+
28
+ Node >=22.19.0 is required. Pi Durable and chord are pinned runtime dependencies. The driver does not directly import or declare Pi AI; Pi Durable itself depends on Pi AI transitively. That dependency includes provider SDKs but this document-store path creates no model registry and makes no provider calls. Host extension dependency interoperability remains a review/test concern, not a claim of a shared host runtime instance. The store supplies every method of the pinned Models interface with a fail-closed implementation that throws on model/auth operations. It does not omit the required Harness option or import a provider registry; no generation operations are supported by this store.
29
+
30
+ SQLite NORMAL-mode operation is not power-loss proof. The owner lock reads the process start time via `ps -p <pid> -o lstart=`; hosts without compatible `ps` output fail closed at dispatch-store open. Live pane recovery, hostile same-user filesystem interference and deployed containment remain unproved. Verified means a report was attributed at the transport boundary; it is not owner approval or task completion.
31
+
32
+ Credit: Pi Durable by Earendil Works, version 1.0.4, MIT, https://github.com/earendil-works/pi/tree/28dcce2ba45ce4a9efeb0f5b686f0be830fd89b9/packages/durable (accessed 2026-10-05). Upstream is installed as a dependency, not copied into driver source. Preserve upstream licence notices when distributing dependencies. Complete dependency licence auditing remains required before publication.
@@ -0,0 +1,45 @@
1
+ # Merge policy
2
+
3
+ The driver has one merge setting. It is the first piece of driver-native setup.
4
+
5
+ | Value | Meaning |
6
+ | --- | --- |
7
+ | `human-merges-only` (default) | The owner merges on GitHub. Every agent merge command is refused. |
8
+ | `agents-after-checks-and-review` | An agent may run `gh pr merge <number or URL>` once every check on the PR head has passed and a Standards PASS and a Spec PASS are recorded for that exact head. |
9
+
10
+ ## Where it is stored
11
+
12
+ Copy `config/merge-policy.v1.example.json` to `{PI_CODING_AGENT_DIR}/config/merge-policy.v1.json` (fallback to the installed agent config directory) and set `policy`. The guard never reads a policy from the working tree. A missing file means `human-merges-only`. An unreadable file, an unknown value or a missing schema also falls back to `human-merges-only`.
13
+
14
+ ## How it is enforced
15
+
16
+ `attended-authority-guard` checks every bash call for a pull-request merge: `gh pr merge`, the REST merge endpoint and the `mergePullRequest` mutation. The answer never depends on a prompt, so a headless bot session and direct Pi use behave the same. A refusal starts with `pi-agentic-driver.merge-policy.v1: needs-owner-approval:` so a coordinator can pass it up as an owner decision.
17
+
18
+ ## Bypasses are refused under both values
19
+
20
+ These are refused whatever the policy says: `gh pr merge --admin`, `gh pr merge --auto` (alone or with `--admin`), writes to branch protection or rulesets through `gh api`, edits to protection bypass lists, and the GraphQL `enablePullRequestAutoMerge`, `mergeBranch` and branch protection rule mutations. The REST merge endpoint and `mergePullRequest` cannot be checked, so they are refused too. Reading protection or rulesets is allowed.
21
+
22
+ ## The agents value
23
+
24
+ The guard reads the PR head, its author and its checks through the existing `gh` login. It reads the review record from `{profile}/merge-reviews/<head sha>.json`, written by `agentic_merge_review_record`. Only a simple `gh pr merge <ref>` can be checked, and never one with `--repo` or `-R`, since that could target a different repository from the one the evidence comes from. A new push changes the head, so the review must be recorded again.
25
+
26
+ Each verdict carries its reviewer identity or session id. The tool adds the recording session id from the host, not from the model, and refuses to record a verdict whose reviewer is the recording session. At merge time a PASS counts only when:
27
+
28
+ - every check on the head has passed;
29
+ - both reviewer identities are present, and neither is the PR author's login or the merging session's id;
30
+ - the record was not written by the merging session.
31
+
32
+ If the merging session has no id, or the PR author is unknown, the merge is refused.
33
+
34
+ ## Limits
35
+
36
+ This is a workflow guard, not a security boundary.
37
+
38
+ - Reviewer identities are strings supplied by whoever records them. The guard checks that they differ from the author and the merging session. It cannot prove that a separate reviewer really ran. Two cooperating sessions can still record a review for each other.
39
+ - Session ids come from the Pi host. A process outside Pi can write the record file directly.
40
+ - Detection reads command text. A merge hidden behind shell indirection, another tool or the GitHub web page is not seen.
41
+ - Branch protection on GitHub, with required checks and required reviews, is the real gate. This setting does not change any repository settings.
42
+
43
+ ## Relation to the PR wording check
44
+
45
+ The PR wording check is a GitHub check like any other. Under `agents-after-checks-and-review` it must pass on the PR head before an agent merge is allowed, because every reported check must pass. A PR with no reported checks is refused. To make the wording check binding for human merges too, the owner marks it required in branch protection. The driver does not do that.