@evoclock/pi-agentic-driver 1.1.2 → 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.
- package/.agentic-driver/loop-until-done.defaults.json +5 -0
- package/README.md +302 -41
- package/config/merge-policy.v1.example.json +4 -0
- package/config/relay-authority.v1.example.json +7 -0
- package/config/treehouse.v1.json +4 -0
- package/docs/durable-driver-api.md +23 -0
- package/docs/durable-persistence.md +32 -0
- package/docs/merge-policy.md +45 -0
- package/docs/relay-authority.md +9 -0
- package/extensions/aidr.ts +3 -0
- package/extensions/harvest-lessons.ts +8 -0
- package/extensions/herdr-dispatch.ts +32 -3
- package/extensions/loop-until-done.ts +7 -0
- package/extensions/recovery-evidence.ts +3 -3
- package/extensions/treehouse-recycle.ts +47 -0
- package/janus/README.md +2 -4
- package/janus/janus/config.ts +2 -2
- package/janus/janus/providers.ts +2 -4
- package/janus/tests/providers.test.ts +2 -2
- package/{extensions → lib}/task-store-adapter.ts +1 -5
- package/package.json +158 -4
- package/scripts/enforcement/approval_ledger_pi.js +135 -0
- package/scripts/enforcement/attended_authority_guard.js +113 -0
- package/scripts/enforcement/board_claim_bridge_pi.js +10 -10
- package/scripts/enforcement/durable_assignment_pi.js +488 -0
- package/scripts/enforcement/durable_driver_pi.d.ts +51 -0
- package/scripts/enforcement/durable_driver_pi.js +254 -0
- package/scripts/enforcement/herdr_async_dispatch_pi.js +86 -20
- package/scripts/enforcement/herdr_async_seam_pi.js +90 -3
- package/scripts/enforcement/herdr_communication_pi.js +126 -12
- package/scripts/enforcement/herdr_lifecycle_pi.js +730 -81
- package/scripts/enforcement/linux_microvm_cutover_pi.js +3 -3
- package/scripts/enforcement/linux_microvm_remote_fixture.sh +0 -6
- package/scripts/enforcement/loop_until_done_guide.md +13 -0
- package/scripts/enforcement/loop_until_done_pi.js +214 -0
- package/scripts/enforcement/merge_policy.js +181 -0
- package/scripts/enforcement/outward_effect_pi.js +156 -0
- package/scripts/enforcement/recovery_evidence_review_pi.js +31 -3
- package/scripts/enforcement/storage_owner_lock_pi.js +66 -0
- package/scripts/enforcement/task_board_core_pi.js +13 -100
- package/scripts/enforcement/task_board_hash_pi.js +86 -0
- package/scripts/enforcement/treehouse_recycle_pi.js +145 -0
- package/scripts/harvest_lessons.js +219 -0
- package/scripts/relay/record_approval.mjs +10 -0
- package/scripts/relay/send_request.mjs +26 -0
- package/skills/architect-pi/SKILL.md +44 -0
- package/skills/arena-pi/SKILL.md +46 -0
- package/skills/automate-me-pi/SKILL.md +37 -0
- package/skills/benchmark-checklist-pi/SKILL.md +35 -0
- package/skills/blast-radius-pi/SKILL.md +24 -0
- package/skills/bro-pi/SKILL.md +17 -0
- package/skills/correct-pi/SKILL.md +36 -0
- package/skills/create-verification-skill-pi/LICENSE +21 -0
- package/skills/create-verification-skill-pi/SKILL.md +24 -0
- package/skills/decision-to-spec/SKILL.md +5 -5
- package/skills/figure-it-out-pi/SKILL.md +37 -0
- package/skills/harvest-lessons/SKILL.md +56 -0
- package/skills/harvest-lessons/harvest.example.json +8 -0
- package/skills/how/LICENSE +24 -0
- package/skills/how/WORKFLOW_BOUNDARIES.md +19 -0
- package/skills/how-pi/SKILL.md +33 -0
- package/skills/in-your-own-words-pi/SKILL.md +15 -0
- package/skills/interrogate-pi/SKILL.md +35 -0
- package/skills/ketchup-pi/SKILL.md +21 -0
- package/skills/maintain-verification-skill-pi/SKILL.md +37 -0
- package/skills/make-bot-ui-pi/SKILL.md +33 -0
- package/skills/no-comments-pi/SKILL.md +33 -0
- package/skills/poteto-help-pi/SKILL.md +50 -0
- package/skills/poteto-mode-pi/LICENSE +21 -0
- package/skills/poteto-mode-pi/SKILL.md +41 -0
- package/skills/poteto-mode-pi/playbooks.md +95 -0
- package/skills/principle-attack-the-premise-pi/SKILL.md +17 -0
- package/skills/principle-boundary-discipline-pi/SKILL.md +19 -0
- package/skills/principle-build-the-lever-pi/SKILL.md +19 -0
- package/skills/principle-encode-lessons-in-structure-pi/SKILL.md +17 -0
- package/skills/principle-exhaust-the-design-space-pi/SKILL.md +17 -0
- package/skills/principle-experience-first-pi/SKILL.md +17 -0
- package/skills/principle-explain-the-number-pi/SKILL.md +21 -0
- package/skills/principle-fix-root-causes-pi/SKILL.md +21 -0
- package/skills/principle-foundational-thinking-pi/SKILL.md +21 -0
- package/skills/principle-guard-the-context-window-pi/SKILL.md +19 -0
- package/skills/principle-laziness-protocol-pi/SKILL.md +19 -0
- package/skills/principle-make-operations-idempotent-pi/SKILL.md +21 -0
- package/skills/principle-migrate-callers-then-delete-legacy-apis-pi/SKILL.md +19 -0
- package/skills/principle-minimize-reader-load-pi/SKILL.md +19 -0
- package/skills/principle-model-the-domain-pi/SKILL.md +19 -0
- package/skills/principle-never-block-on-the-human-pi/SKILL.md +19 -0
- package/skills/principle-outcome-oriented-execution-pi/SKILL.md +19 -0
- package/skills/principle-prove-it-works-pi/SKILL.md +19 -0
- package/skills/principle-redesign-from-first-principles-pi/SKILL.md +19 -0
- package/skills/principle-separate-before-serializing-shared-state-pi/SKILL.md +19 -0
- package/skills/principle-sequence-verifiable-units-pi/SKILL.md +19 -0
- package/skills/principle-subtract-before-you-add-pi/SKILL.md +19 -0
- package/skills/principle-test-behavior-not-implementation-pi/SKILL.md +21 -0
- package/skills/principle-type-system-discipline-pi/SKILL.md +19 -0
- package/skills/public-pr-wording/SKILL.md +69 -0
- package/skills/public-pr-wording/pr_wording_config.example.json +22 -0
- package/skills/public-pr-wording/pr_wording_lint.py +249 -0
- package/skills/public-pr-wording/pr_wording_rules.json +60 -0
- package/skills/pulse/SKILL.md +6 -0
- package/skills/recall-pi/SKILL.md +29 -0
- package/skills/reflect-pi/SKILL.md +27 -0
- package/skills/setup-pstack-pi/SKILL.md +23 -0
- package/skills/setup-pstack-pi/assets/pstack-model-roles.v1.example.json +7 -0
- package/skills/sharpen-pi/SKILL.md +19 -0
- package/skills/show-me-your-work-pi/SKILL.md +27 -0
- package/skills/swarm-pi/SKILL.md +27 -0
- package/skills/tasks/SKILL.md +8 -2
- package/skills/tdd-pi/SKILL.md +21 -0
- package/skills/teach-pi/SKILL.md +21 -0
- package/skills/technical-writing-pi/SKILL.md +27 -0
- package/skills/typescript-best-practices-pi/SKILL.md +21 -0
- package/skills/unslop-pi/SKILL.md +27 -0
- package/skills/why-pi/SKILL.md +37 -0
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,18 +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 |
|
|
47
|
-
| `decision-to-spec` skill | Drafts one reviewable assignment from
|
|
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 |
|
|
48
61
|
| `agentic_herdr_communication` | Submits one prompt and observes delivery and marked reports; never grants authority. | shipped |
|
|
49
62
|
| `agentic_herdr_spawn_worker` | Starts one Pi worker in a pane or tab, with native confirmation. | shipped |
|
|
50
63
|
| `agentic_aidr` | A remedy for AI;DR. Reviews writing for clarity, simplicity, brevity, and humanity. | shipped |
|
|
51
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 |
|
|
52
|
-
| `agentic_worker_dispatch` | Runs bounded worker journeys and observes worker liveness
|
|
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 |
|
|
53
67
|
| `agentic_kanban_board` | Shows the workspace task board: lanes, flags, priorities, dependencies, and which cards can run. | shipped |
|
|
54
68
|
| `agentic_kanban_board_write` | Adds cards to the board through the trusted writer, which records who authorized the work. | shipped |
|
|
55
69
|
| `agentic_kanban_board_update` | Moves, closes, flags, edits, or removes cards on the board, always recording who authorized the change. | shipped |
|
|
56
70
|
| `agentic_kanban_board_dispatch` | Claims an eligible board card for automated contained work and creates its assignment envelope. | shipped |
|
|
57
71
|
| `agentic_kanban_pulse` | Checks for ready work and available capacity, then starts assignments under the board policy. | shipped |
|
|
58
|
-
|
|
|
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 |
|
|
59
73
|
| Router | Capacity/policy routing for Board Pulse: seats with scope statements, tunable policy values, and a binding dispatch gate. | shipped |
|
|
60
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 |
|
|
61
75
|
|
|
@@ -63,16 +77,14 @@ proofs for agentic workflows.
|
|
|
63
77
|
private evidence store are packaged foundations, not a running recovery
|
|
64
78
|
ladder. Herdr 0.9.1 has no proven modal-safe nudge channel. No terminal
|
|
65
79
|
`prompt` or `send-keys` fallback is enabled. Pending integrations below are
|
|
66
|
-
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.
|
|
67
84
|
|
|
68
85
|
## Code review and planning
|
|
69
86
|
|
|
70
|
-
*Extensions and
|
|
71
|
-
|
|
72
|
-
The packaged `decision-to-spec` skill turns an owner-selected decision into one
|
|
73
|
-
reviewable planning brief. Invoke `/skill:decision-to-spec` or explicitly ask
|
|
74
|
-
for the workflow; Pi can load the skill description without granting permission
|
|
75
|
-
to draft. It does not implement a plan or create a board task.
|
|
87
|
+
*Extensions that review, route, and control what an agent does.*
|
|
76
88
|
|
|
77
89
|
<details>
|
|
78
90
|
<summary><strong>code-phage, advisory code review</strong> <em>(released, 0.1.1)</em></summary>
|
|
@@ -122,20 +134,42 @@ The result is a clean handoff from a session plan to durable work. Selected task
|
|
|
122
134
|
<details>
|
|
123
135
|
<summary><strong>decision-to-spec, one reviewable assignment</strong> <em>(packaged skill)</em></summary>
|
|
124
136
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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.
|
|
134
164
|
|
|
135
165
|
The output is a private planning brief for the requester, **not** a dispatch
|
|
136
|
-
record. It cannot approve
|
|
137
|
-
create an issue, or change a provider route. Unresolved
|
|
138
|
-
explicit instead of becoming assumed requirements.
|
|
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).
|
|
139
173
|
|
|
140
174
|
</details>
|
|
141
175
|
|
|
@@ -188,7 +222,7 @@ contract, Keychain provisioning, and configuration.
|
|
|
188
222
|
template contains no accounts and one disabled placeholder seat, so it loads
|
|
189
223
|
out of the box and schedules nothing until you enable a real seat.
|
|
190
224
|
- **User profile (precedence over defaults):**
|
|
191
|
-
|
|
225
|
+
the configured router profile path, or the path in the
|
|
192
226
|
`AGENTIC_DRIVER_ROUTER_PROFILE` environment variable. A profile section
|
|
193
227
|
replaces the matching defaults section wholesale; the merged config must
|
|
194
228
|
validate or the router refuses to produce decisions (fails closed).
|
|
@@ -303,7 +337,7 @@ across versions rather than pinning one).
|
|
|
303
337
|
|
|
304
338
|
- **List and observe.** The integration checks a repository-local
|
|
305
339
|
`config/herdr-worker-repositories.v1.json` first, then the same file in the
|
|
306
|
-
active Pi profile under `PI_CODING_AGENT_DIR` (
|
|
340
|
+
active Pi profile under `PI_CODING_AGENT_DIR` (the installed agent profile directory). If no
|
|
307
341
|
registry is available, spawning is refused and communication trusts only
|
|
308
342
|
the current repository; malformed registries are rejected. Canonical path
|
|
309
343
|
checks reject unlisted or symlink-escaped repositories.
|
|
@@ -337,7 +371,19 @@ extension:
|
|
|
337
371
|
- creates the split pane or labelled tab;
|
|
338
372
|
- starts exactly one Pi agent in it;
|
|
339
373
|
- verifies the role, model arguments, pane identity, and canonical repository;
|
|
340
|
-
-
|
|
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;
|
|
341
387
|
- uses fixed argv with `shell: false` and no arbitrary Herdr or shell surface;
|
|
342
388
|
- returns explicit `pane_created`, `tab_created`, or `agent_started` partial
|
|
343
389
|
states when only part of the operation succeeds; and
|
|
@@ -386,6 +432,76 @@ silently, and return results as untrusted evidence.
|
|
|
386
432
|
|
|
387
433
|
</details>
|
|
388
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
|
+
|
|
389
505
|
|
|
390
506
|
<details>
|
|
391
507
|
<summary><strong>task board and Pulse, planned and automated work you can see</strong> <em>(released, 0.9.1)</em></summary>
|
|
@@ -423,6 +539,16 @@ worker journey does not claim board cards or mint board envelopes.
|
|
|
423
539
|
|
|
424
540
|
Consuming an envelope releases the claim. It does not mark the card done.
|
|
425
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
|
+
|
|
426
552
|
Claims and envelopes are kept in authenticated state beside `TASKS.md`. The
|
|
427
553
|
derived `board.md` view shows active claims but never becomes an authority
|
|
428
554
|
source.
|
|
@@ -436,8 +562,8 @@ host.
|
|
|
436
562
|
|
|
437
563
|
Coming next:
|
|
438
564
|
|
|
439
|
-
- contained Pulse execution through
|
|
440
|
-
-
|
|
565
|
+
- contained Pulse execution through an approved isolation boundary;
|
|
566
|
+
- expanded external board support for the shared card fields; and
|
|
441
567
|
- our own Obsidian plugin for canonical `TASKS.md`.
|
|
442
568
|
|
|
443
569
|
</details>
|
|
@@ -587,9 +713,6 @@ rules watch the same directories by design, and together they are stricter
|
|
|
587
713
|
than either alone, so the growth-sampling trip is covered by automated tests
|
|
588
714
|
rather than a live run.
|
|
589
715
|
|
|
590
|
-
Automated tests exercise the containment taxonomy, killswitch decisions, and
|
|
591
|
-
threshold behavior.
|
|
592
|
-
|
|
593
716
|
</details>
|
|
594
717
|
|
|
595
718
|
<details>
|
|
@@ -653,6 +776,29 @@ continues, and the agent does not retry behind your back. In headless runs
|
|
|
653
776
|
where no human can confirm, destructive commands are refused rather than
|
|
654
777
|
silently allowed.
|
|
655
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
|
+
|
|
656
802
|
</details>
|
|
657
803
|
|
|
658
804
|
**Under development in this theme:**
|
|
@@ -696,6 +842,49 @@ silently allowed.
|
|
|
696
842
|
|
|
697
843
|
*Extensions that keep routine Git low-friction and consequential Git guarded.*
|
|
698
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
|
+
|
|
699
888
|
**Planned:**
|
|
700
889
|
|
|
701
890
|
- **git workflow safeguards.** Not packaged yet. The planned capability covers
|
|
@@ -716,7 +905,7 @@ silently allowed.
|
|
|
716
905
|
injection, data-exfiltration endpoints, untrusted integrations, PII
|
|
717
906
|
leakage, and OWASP/MCP threat families, with accept/redact/reject
|
|
718
907
|
decisions. Built on the agent-scanner approach proven in Hillstar
|
|
719
|
-
Orchestrator and
|
|
908
|
+
Orchestrator and an isolation environment.
|
|
720
909
|
- **checkpoint storage lifecycle.** Compression, deduplication, retention,
|
|
721
910
|
and purging rules for capsule and index stores once a product ships, so
|
|
722
911
|
session evidence has a managed lifetime instead of growing without bound.
|
|
@@ -733,6 +922,53 @@ The `templates/AGENTS.md` file is a starting contract you can copy into any
|
|
|
733
922
|
repository that uses Pi agents. It asks for controlled tasks and realistic tests, and
|
|
734
923
|
treats agent reports as untrusted evidence. It never overwrites an existing `AGENTS.md`. Add your project rules below it.
|
|
735
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
|
+
|
|
736
972
|
## Install
|
|
737
973
|
|
|
738
974
|
From npm:
|
|
@@ -749,30 +985,55 @@ pi install git:github.com/evoclock/pi-agentic-driver@<latest-tag>
|
|
|
749
985
|
|
|
750
986
|
Released extensions load independently.
|
|
751
987
|
|
|
752
|
-
|
|
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.
|
|
989
|
+
|
|
990
|
+
### Choose which package resources load
|
|
753
991
|
|
|
754
|
-
A normal install loads
|
|
755
|
-
|
|
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:
|
|
756
995
|
|
|
757
996
|
```json
|
|
758
997
|
{
|
|
759
998
|
"packages": [
|
|
760
999
|
{
|
|
761
1000
|
"source": "npm:@evoclock/pi-agentic-driver",
|
|
762
|
-
"extensions": [
|
|
763
|
-
"extensions/aidr.ts",
|
|
764
|
-
"extensions/code-phage.js"
|
|
765
|
-
]
|
|
1001
|
+
"extensions": ["extensions/aidr.ts", "extensions/code-phage.js"]
|
|
766
1002
|
}
|
|
767
1003
|
]
|
|
768
1004
|
}
|
|
769
1005
|
```
|
|
770
1006
|
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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
|
|
774
1026
|
package. You are not required to use every extension.
|
|
775
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
|
+
|
|
776
1037
|
## License
|
|
777
1038
|
|
|
778
1039
|
AGPL-3.0-only with author-attribution additional terms (Section 7(b));
|
|
@@ -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.
|