gentle-pi 4.2.0 → 4.3.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/README.md +336 -137
- package/assets/agents/gentle-ai-explore.md +1 -0
- package/assets/agents/gentle-ai-verify.md +6 -4
- package/assets/agents/gentle-ai-worker.md +7 -1
- package/assets/orchestrator-delegation-reference.md +163 -0
- package/assets/orchestrator-delegation.md +25 -176
- package/assets/orchestrator-tracking.md +1 -1
- package/assets/orchestrator-verification.md +14 -8
- package/assets/orchestrator-writer.md +5 -1
- package/assets/orchestrator.md +9 -9
- package/bin/gentle-shell.mjs +170 -22
- package/contracts/review-provider-contract-mirror/provider-contract.lock.json +8 -7
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/README.md +7 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/manifest.json +11 -2
- package/contracts/review-provider-contract-mirror/v1.4.0/bundle/routing/pi.md +92 -0
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-capabilities.baseline.json +8 -1
- package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/generated/provider-roles.baseline.json +1 -1
- package/docs/assets/diagrams/agent-orchestration.svg +54 -15
- package/docs/assets/diagrams/gentleman-workflow.svg +69 -11
- package/docs/assets/diagrams/measured-routing.svg +49 -0
- package/docs/assets/diagrams/native-review.svg +52 -12
- package/docs/assets/diagrams/odd-workflow.svg +128 -66
- package/docs/assets/diagrams/permission-modes.svg +51 -0
- package/docs/assets/features/audio-notifications.png +0 -0
- package/docs/assets/features/command-palette.png +0 -0
- package/docs/bundled-install.md +33 -3
- package/docs/delegated-verification.md +10 -8
- package/docs/gentle-agents-activity.md +11 -5
- package/docs/gentle-shell-uninstall.md +8 -1
- package/docs/gentle-shell.md +5 -4
- package/docs/install-wizard.md +8 -1
- package/docs/permission-modes.md +1 -1
- package/docs/readme-reference.md +107 -19
- package/docs/yolo-mode.md +21 -8
- package/extensions/codegraph-tools.ts +68 -6
- package/extensions/gentle-agents.ts +99 -12
- package/extensions/gentle-ai.ts +284 -52
- package/extensions/gentle-herdr-activity.ts +2 -3
- package/extensions/gentle-shell.ts +27 -12
- package/extensions/gentle-todo.ts +43 -9
- package/extensions/quiet-tools.ts +1 -1
- package/extensions/session-scratch.ts +365 -0
- package/extensions/startup-banner.ts +2 -3
- package/lib/agent-home.ts +8 -3
- package/lib/agents-protocol.ts +3 -0
- package/lib/agents-runner.ts +55 -6
- package/lib/agents-view.ts +3 -1
- package/lib/bounded-writer-admission.ts +51 -0
- package/lib/command-palette-catalog.ts +0 -4
- package/lib/commit-slice-signal.ts +100 -0
- package/lib/context-backstop.ts +84 -0
- package/lib/delivered-test-lint.ts +80 -0
- package/lib/destructive-command-guard.ts +120 -9
- package/lib/gauge-theme.ts +146 -0
- package/lib/gentle-ai-binary.ts +28 -7
- package/lib/gentle-shell-bundled.ts +39 -3
- package/lib/gentle-shell-import-pi.ts +233 -0
- package/lib/gentle-shell-launcher.ts +145 -11
- package/lib/gentle-shell-uninstall.ts +18 -1
- package/lib/herdr-activity.ts +24 -0
- package/lib/native-review-cli.ts +9 -1
- package/lib/orchestrator-discovery.ts +5 -1
- package/lib/orchestrator-presence.ts +9 -0
- package/lib/orchestrator-scope.ts +21 -3
- package/lib/provider-contract-bundle.ts +99 -53
- package/lib/review-candidate-view-owner.ts +29 -19
- package/lib/review-candidate-view.ts +36 -2
- package/lib/review-risk-assessment.ts +43 -38
- package/lib/review-session-standing-permission.ts +3 -1
- package/lib/session-profile-freeze.ts +30 -1
- package/lib/session-scratch.ts +474 -0
- package/lib/shell-bar.ts +25 -25
- package/lib/shell-changes-view.ts +80 -13
- package/lib/shell-gauge.ts +49 -6
- package/lib/shell-todo.ts +23 -4
- package/lib/shell-usage-view.ts +5 -1
- package/lib/shell-usage.ts +7 -7
- package/lib/verifier-admission.ts +29 -0
- package/lib/yolo-temp-scratch.ts +69 -0
- package/package.json +1 -2
- package/runtime/gentle-ai-binary.mjs +28 -7
- package/runtime/gentle-shell-bundled.mjs +38 -2
- package/runtime/gentle-shell-import-pi.mjs +234 -0
- package/runtime/gentle-shell-launcher.mjs +143 -9
- package/runtime/gentle-shell-uninstall.mjs +18 -1
- package/runtime/native-review-cli.mjs +8 -0
- package/runtime/review-risk-assessment.mjs +43 -38
- package/scripts/build-runtime-modules.mjs +1 -0
- package/scripts/bundled-engram-migration.mjs +125 -0
- package/scripts/gentle-ai-installer.mjs +10 -10
- package/scripts/installer-server.mjs +14 -4
- package/scripts/main-channel.mjs +3 -2
- package/scripts/run-test-suite.mjs +5 -0
- package/scripts/verify-package-files.mjs +19 -13
- package/skills/gentle-ai/SKILL.md +3 -3
- package/skills/work-unit-commits/SKILL.md +5 -3
- package/tests/agent-risk-escalation.test.ts +37 -10
- package/tests/agents-runner.test.ts +260 -3
- package/tests/autonomous-guard.test.ts +122 -0
- package/tests/bounded-writer-admission.test.ts +11 -3
- package/tests/card-content-policy.test.ts +19 -9
- package/tests/child-safety.test.ts +39 -0
- package/tests/codegraph-tools.test.ts +123 -2
- package/tests/command-palette.test.ts +14 -4
- package/tests/commit-slice-signal.test.ts +162 -0
- package/tests/context-backstop.test.ts +159 -0
- package/tests/delegate-for-reason-contract.test.ts +91 -24
- package/tests/delivered-test-lint.test.ts +105 -0
- package/tests/destructive-command-guard.test.ts +85 -1
- package/tests/engram-safety.test.ts +1 -1
- package/tests/fixtures/primary-session-prompt/gentleman.golden.md +143 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/README.md +29 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/manifest.json +83 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/orchestration/pi.md +70 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/routing/pi.md +92 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/lens.schema.json +16 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/refuter.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/schemas/targeted-validator.schema.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/lens.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/refuter.json +1 -0
- package/tests/fixtures/provider-contract-bundle/v1.4.0/vectors/targeted-validator.json +1 -0
- package/tests/gauge-theme.test.ts +120 -0
- package/tests/gentle-agents.test.ts +282 -3
- package/tests/gentle-ai-binary.test.ts +1 -1
- package/tests/gentle-ai-child-guards.test.ts +61 -0
- package/tests/gentle-ai-doctor-herdr.test.ts +94 -0
- package/tests/gentle-ai-installer.test.ts +51 -51
- package/tests/gentle-shell-bin.test.ts +163 -0
- package/tests/gentle-shell-bundled.test.ts +169 -6
- package/tests/gentle-shell-import-pi.test.ts +262 -0
- package/tests/gentle-shell-launcher.test.ts +79 -0
- package/tests/gentle-shell-uninstall.test.ts +30 -0
- package/tests/gentle-shell.test.ts +42 -9
- package/tests/gentle-todo.test.ts +179 -0
- package/tests/headless-delivery-question.test.ts +58 -0
- package/tests/herdr-activity.test.ts +34 -1
- package/tests/installer-server.test.ts +10 -2
- package/tests/installer-windows-bootstrap.test.ts +33 -4
- package/tests/native-review-capability-contract.test.ts +7 -1
- package/tests/native-review-parity.test.ts +22 -1
- package/tests/no-self-pause-contract.test.ts +5 -2
- package/tests/odd-integration.test.ts +5 -1
- package/tests/odd-routing-canonical-ratchet.test.ts +127 -162
- package/tests/odd-routing-contract.test.ts +115 -30
- package/tests/orchestrator-budget.test.ts +19 -8
- package/tests/orchestrator-discovery.test.ts +29 -0
- package/tests/orchestrator-rdd-ownership.test.ts +3 -1
- package/tests/orchestrator-scope.test.ts +66 -0
- package/tests/orchestrator-status-publication-contract.test.ts +12 -8
- package/tests/package-manifest.test.ts +24 -20
- package/tests/pi-update-launcher.test.ts +21 -2
- package/tests/primary-session-prompt.test.ts +101 -0
- package/tests/provider-contract-bundle.test.ts +118 -0
- package/tests/provider-contract-mirror.test.ts +24 -0
- package/tests/quiet-tool-rendering.test.ts +23 -0
- package/tests/rdd-aware-verification-contract.test.ts +29 -11
- package/tests/rdd-status-line.test.ts +79 -0
- package/tests/review-agent-end-preflight.test.ts +6 -0
- package/tests/review-candidate-view.test.ts +49 -0
- package/tests/review-contract-prompt.test.ts +5 -2
- package/tests/review-controller-native-routing.test.ts +14 -1
- package/tests/review-controller.test.ts +1 -1
- package/tests/review-host-relay-routing.test.ts +59 -2
- package/tests/review-loadout-rdd-off-sdk.test.ts +237 -0
- package/tests/review-owner-identity.test.ts +132 -0
- package/tests/review-risk-assessment.test.ts +163 -76
- package/tests/review-session-standing-permission-controller.test.ts +14 -1
- package/tests/review-session-standing-permission.test.ts +27 -1
- package/tests/run-test-suite.test.ts +15 -0
- package/tests/runtime-harness.mjs +16 -6
- package/tests/session-changes-shell.test.ts +1 -1
- package/tests/session-profile-freeze.test.ts +23 -0
- package/tests/session-scratch.test.ts +945 -0
- package/tests/session-subject-routing-contract.test.ts +2 -1
- package/tests/shell-bar.test.ts +7 -0
- package/tests/shell-changes-view.test.ts +137 -1
- package/tests/shell-gauge.test.ts +42 -0
- package/tests/small-task-inline-contract.test.ts +37 -0
- package/tests/standalone-config-home.test.ts +324 -0
- package/tests/startup-banner.test.ts +5 -4
- package/tests/startup-home-repo.test.ts +34 -0
- package/tests/support/orchestrator-modules.ts +1 -0
- package/tests/task-size-routing-contract.test.ts +69 -9
- package/tests/test-scratch-contract.test.ts +102 -0
- package/tests/test-strength-contract.test.ts +36 -1
- package/tests/tool-batching-contract.test.ts +64 -0
- package/tests/verification-proportionality.test.ts +46 -0
- package/tests/verifier-admission.test.ts +137 -0
- package/tests/verify-agent-contract.test.ts +8 -8
- package/tests/windows-hidden-processes.test.ts +5 -0
- package/tests/writer-edit-surface-scope.test.ts +3 -0
- package/tests/writer-feature-document-admission.test.ts +156 -0
- package/tests/yolo-mode.test.ts +18 -0
- package/tests/yolo-temp-scratch.test.ts +71 -0
- package/scripts/mirror-odd-routing.mjs +0 -242
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/orchestration/pi.md +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/lens.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/refuter.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/schemas/targeted-validator.schema.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/lens.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/refuter.json +0 -0
- /package/contracts/review-provider-contract-mirror/{v1.3.0 → v1.4.0}/bundle/vectors/targeted-validator.json +0 -0
package/docs/readme-reference.md
CHANGED
|
@@ -11,12 +11,14 @@ Organic Driven Development (ODD) keeps explore → implement → proportionate c
|
|
|
11
11
|
|
|
12
12
|
ODD runs on every request, without the user asking for a workflow, a plan, or task tracking.
|
|
13
13
|
|
|
14
|
+
The primary-session prompt renders steps 1-7, the phase-reporting line, and the resume line verbatim from Gentle AI's ODD routing block for Pi, published in the provider contract bundle as `routing/pi.md` (contract 1.4.0) and mirrored in `contracts/review-provider-contract-mirror/`. The prompt verifies the mirrored file against the mirror lock's SHA-256 and fails closed when it is missing, tampered, or reshaped. The list below summarizes those steps.
|
|
15
|
+
|
|
14
16
|
1. **Authorize** — read-only unless implementation is authorized; ask one clarification when intent is ambiguous.
|
|
15
17
|
2. **Explore** — read existing code and requirements first, proportionately to the request.
|
|
16
18
|
3. **Resolve uncertainty** — optional research or one focused product question only for a real unresolved decision.
|
|
17
19
|
4. **Classify** — by the Task Size section: small when understood, risk is contained, and the work could be resumed from the request plus `git diff`; large only when that resume test fails. Counts never classify.
|
|
18
20
|
5. **Track before the first write** — create the feature document and Engram mirror automatically for large work, and tell the user in one line.
|
|
19
|
-
6. **Implement task by task** — route each task through the smallest safe workflow, with configured TDD and applicable checks.
|
|
21
|
+
6. **Implement task by task** — route each task through the smallest safe workflow, with configured TDD and applicable checks. Commit work units in reviewable slices of about 400 authored changed lines on the feature branch (branch first when on the default branch), with tests and docs alongside the behavior and a Conventional Commit message; a unit whose Risk line names a high-risk item gets its own commit so native review can take it alone, and the last slice closes the feature; the feature document records the commit that holds each task as evidence.
|
|
20
22
|
7. **Close** — report the verified outcome, failed/pending checks, and the next step. The native review candidate is a work-unit commit or a PR slice, never a TODO checkbox and never the accumulated feature branch.
|
|
21
23
|
|
|
22
24
|
- **One feature document:** `odd/tasks/<feature-name>.md` holds objective, problem, why, scope, constraints, actionable checklist with stable IDs and acceptance criteria, verification evidence, progress, and next step. Project-scoped Engram topic `odd/<feature-name>/tasks` mirrors the full document and repository-relative locator. Keep concise rationale for meaningful accepted changes here, not a separate plan or exhaustive journal. Accepted user, review, or verification changes update intent and tasks together; preserve valid completed work, add new tasks or reopen invalidated items with reasons. Findings alone do not authorize expansion or acceptance. Routine corrections stay with their tasks; checkoffs require observed proof.
|
|
@@ -33,6 +35,8 @@ ODD runs on every request, without the user asking for a workflow, a plan, or ta
|
|
|
33
35
|
2. **Verified default/main branch chain** — `chain_strategy=stacked-to-main`; land slices in order on the verified destination default branch, not an assumed name.
|
|
34
36
|
3. **One single PR — least recommended** — `delivery_strategy=single-pr`; review the entire oversized change together.
|
|
35
37
|
|
|
38
|
+
In a session where no human can answer questions (`pi -p`, `pi --mode json`, or `pi --mode rpc` without `GENTLE_SHELL_INTERACTIVE_HOST=1`, the same rule `ask_user_question` applies), `ask-on-risk` does not ask: the harness tells the orchestrator to use `feature-branch-chain`, record the choice in the feature document's `## Log`, and continue. Consent, maintenance, and opaque-token prompts keep their own rules.
|
|
39
|
+
|
|
36
40
|
Generate the complete user-facing question and every option label, description, and recommendation marker in the active user's conversation language (English for an English user, Spanish for a Spanish user, etc.). Machine strategy tokens remain unchanged and untranslated. These English examples are illustrative and localizable, not mandatory copy.
|
|
37
41
|
|
|
38
42
|
The third choice overrides the pending chaining path: clear the chain choice as inapplicable and suppress later chain prompts. `single-pr` is not a `chain_strategy` token; do not automatically select `exception-ok`. Least recommended is scoped to the oversized menu: larger single PRs increase reviewer load, slow feedback, and couple rollback. A focused ≤400-line single PR remains reasonable. Follow the destination repository's documented contribution/size policy; `size:exception` is a Gentle-owned repository policy, not a universal label requirement. Do not request or add it for generic users unless the destination policy uses it; keep applicable maintainer acceptance and protected-label authorization gates. Single-PR output requires no tracker, child dependency diagram, or Chain Context. Choosing shape does not authorize push, PR creation, merge, or review-mode/consent changes. Cache both choices, and record slice boundaries (which commits each PR holds) in the feature document for chains, or the whole-PR scope for single PR. Resolve the `work-unit-commits` and `chained-pr` skills by registry name before planning or creating any PR.
|
|
@@ -57,7 +61,7 @@ flowchart TD
|
|
|
57
61
|
TC --> M[Implement next authorized task]
|
|
58
62
|
M --> N[Applicable functional checks]
|
|
59
63
|
N --> O[Record truthful results; update tracked intent, tasks and mirror]
|
|
60
|
-
O --> OC[
|
|
64
|
+
O --> OC[Commit at a slice boundary; a high-risk unit alone]
|
|
61
65
|
OC --> P{Authorized work remains?}
|
|
62
66
|
P -->|Yes| M
|
|
63
67
|
P -->|No| Q{RDD enabled at work-unit commit boundary?}
|
|
@@ -98,6 +102,9 @@ missing Herdr/socket contexts do not publish. The packaged extension loads throu
|
|
|
98
102
|
|
|
99
103
|
Herdr 0.8.2/protocol 20 needs **two separate rows**, not a newline inside a token:
|
|
100
104
|
add `['$summary'], ['$summary2']` to the Agents sidebar rows in your config.
|
|
105
|
+
Inside a Herdr pane, `/gentle:doctor` shows whether publishing is active and this
|
|
106
|
+
read-only setup hint; Gentle Shell never edits Herdr config. Configure the Herdr
|
|
107
|
+
client that displays the session (also over SSH), then reload or restart Herdr.
|
|
101
108
|
The first row is `◐ title`; the extension prefixes the optional continuation with
|
|
102
109
|
two spaces, though Herdr may normalize leading whitespace.
|
|
103
110
|
Text wraps by display cells, preferring word boundaries unless a grapheme-safe
|
|
@@ -150,8 +157,8 @@ otherwise `herdr` from `PATH`, without a shell.
|
|
|
150
157
|
| **Skill creation workflow** | Provides the `gentle-ai-skill-creator`/`gentle-ai-skill-improver` skills, `/skill-creation` prompt, and packaged style guide for LLM-first skills. |
|
|
151
158
|
| **Delivery skills** | Includes issue-first PRs, chained PRs, work-unit commits, cognitive docs, comment writing, and Judgment Day review. |
|
|
152
159
|
| **Bounded native review** | Freezes one candidate, dispatches only controller-selected lenses, and records native authority. Review outcomes are informational; delivery follows ordinary repository policy. |
|
|
153
|
-
| **Verified native runtime** | The current source checkout provisions the exact package-local Gentle AI v4.
|
|
154
|
-
| **Runtime safety** | Blocks destructive shell commands, asks for confirmation for sensitive operations, and blocks direct read/write/edit access to sensitive paths. |
|
|
160
|
+
| **Verified native runtime** | The current source checkout provisions the exact package-local Gentle AI v4.2.0 runtime: signed, SHA-256-pinned release archives on Darwin/Linux and a Go SumDB-verified source build on Windows x64/arm64. It validates package-local integrity and rejects PATH, global, sibling, symlink, and mode fallbacks. |
|
|
161
|
+
| **Runtime safety** | Blocks destructive shell commands, asks for confirmation for sensitive operations, and blocks direct read/write/edit access to sensitive paths. Blocks writing a test file that reads git history at a literal commit or points `TMPDIR` inside the working directory (including a relative path such as `.tmp`, `./.tmp`, or `tmp/x`), in the primary session and in subagents. Refuses native review tools (`gentle_review*`) called from inside another tool, such as a codemode script: they run only as direct tool calls. |
|
|
155
162
|
|
|
156
163
|
## Audio notifications
|
|
157
164
|
|
|
@@ -199,7 +206,7 @@ gentle-shell
|
|
|
199
206
|
gentle-shell --link
|
|
200
207
|
```
|
|
201
208
|
|
|
202
|
-
`gentle-shell` alone starts in its own home, `~/.gentle-shell/agent
|
|
209
|
+
`gentle-shell` alone starts in its own home, `~/.gentle-shell/agent`, with its own settings in `~/.gentle-shell/gentle-ai` ([config home](#config-home)). `gentle-shell --link` reuses `~/.pi/agent` as-is. Run `gentle-shell home link` to make `--link` the default, or `gentle-shell home import-pi` to copy your pi sign-ins into the separate home instead ([`home` subcommand](#home-subcommand-and-gentle-shellconfigjson)). Full flags, env vars, and modes: [gentle-shell launcher](#gentle-shell-launcher).
|
|
203
210
|
|
|
204
211
|
### Path B: inside an existing pi
|
|
205
212
|
|
|
@@ -211,7 +218,7 @@ This installs the current npm release; select an explicit version if you need a
|
|
|
211
218
|
|
|
212
219
|
### Source checkout
|
|
213
220
|
|
|
214
|
-
This checkout declares `gentle-pi` `4.0.0` with a package-local Gentle AI `v4.
|
|
221
|
+
This checkout declares `gentle-pi` `4.0.0` with a package-local Gentle AI `v4.2.0` pin. Checkout metadata alone is not proof of npm publication; verify the registry version and its release workflow.
|
|
215
222
|
|
|
216
223
|
### Pi compatibility
|
|
217
224
|
|
|
@@ -238,7 +245,7 @@ pi install npm:gentle-pi@3.5.1
|
|
|
238
245
|
|
|
239
246
|
RDD remains opt-in. Enable it only through an explicit user decision with `/gentle:review-mode enable`; `status` lets you inspect the mode without changing it. The `.git/gentle-ai/candidate-views` parent must sit on a filesystem that honors private POSIX modes (or equivalent Windows ACLs); WSL DrvFS mounts without metadata can reject START before lineage creation.
|
|
240
247
|
|
|
241
|
-
The source checkout's RDD integration installs Gentle AI only into its private `.gentle-ai/` directory. Darwin and Linux use pinned release assets with asset and executable SHA-256 verification (signed archives for source pin `v4.
|
|
248
|
+
The source checkout's RDD integration installs Gentle AI only into its private `.gentle-ai/` directory. Darwin and Linux use pinned release assets with asset and executable SHA-256 verification (signed archives for source pin `v4.2.0`; raw prerelease binaries only under a prerelease pin). Windows x64 and arm64 build the exact `v4.2.0` source tag with a local Go 1.25.10+ toolchain, a sealed Go environment, `GOTOOLCHAIN=local`, and `GOSUMDB=sum.golang.org`; it does not download Go automatically (the [installation wizard](install-wizard.md#pinned-go) can put its own pinned Go first on PATH for that build). Go runs in a short private directory under the system temp directory, never inside the package, because a deep pnpm store path pushes Go's working directories past MAX_PATH; a failed build prints its whole cause chain with the failing command's output tail. Windows provenance is Go-toolchain plus SumDB evidence and postinstall tamper detection, **not** Authenticode or protection against a malicious joint binary-and-manifest replacement. Package-private locks coordinate cooperative concurrent or crashed installers; their tombstones fail closed. A malicious same-user process with write access to package-private `node_modules` is outside that protocol because it can already replace package code, binary, or manifest, and portable Node has no pathname-delete CAS. It never uses `PATH` or a global `gentle-ai` installation. For development or offline installs only, set `GENTLE_PI_SKIP_GENTLE_AI_INSTALL=1`; native review operations then fail closed with an actionable `package-local-binary-missing` error. To recover explicitly, if `GENTLE_PI_SKIP_GENTLE_AI_INSTALL` is set, remove or unset it before changing to the installed `gentle-pi` package directory. Then run `node scripts/install-gentle-ai.mjs`. This invokes the package-owned installer without relying on a global binary or npm configuration change. A missing binary can result from skipped lifecycle scripts, but does not prove that lifecycle scripts were disabled.
|
|
242
249
|
|
|
243
250
|
Recommended companion packages, into the standalone `gentle-shell` home:
|
|
244
251
|
|
|
@@ -305,7 +312,7 @@ gentle-shell self-uninstall [--dry-run] [--yes] [--include-shared]
|
|
|
305
312
|
| Flag | Effect |
|
|
306
313
|
| --- | --- |
|
|
307
314
|
| `--link` | Home is your own pi home: `GENTLE_SHELL_USER_PI_HOME` when a Gentle Shell session recorded it (so `--link` run from inside an isolated session reaches your original pi, not the isolated home), otherwise `PI_CODING_AGENT_DIR` or `~/.pi/agent`. Reuses your existing pi sign-ins, models, and chats; never writes to its `settings.json`. |
|
|
308
|
-
| `--isolated` | Home is `GENTLE_SHELL_HOME` or `~/.gentle-shell/agent`. No credential seeding. Default when nothing else is configured. |
|
|
315
|
+
| `--isolated` | Home is `GENTLE_SHELL_HOME` or `~/.gentle-shell/agent`, with its own settings in `~/.gentle-shell/gentle-ai` ([config home](#config-home)); a custom `GENTLE_SHELL_HOME` outside a `.gentle-shell` folder keeps `~/.pi/gentle-ai`. No credential seeding. Default when nothing else is configured. |
|
|
309
316
|
| `--home <path>` | Home is the given directory. |
|
|
310
317
|
| `--package-root <dir>` | Force this directory as the gentle-pi package to load, taking over from any conflicting package the target `settings.json` already declares (see "Loading the package" below). |
|
|
311
318
|
| `--help`, `-h` | Print usage (flags, commands, env vars) and exit 0. |
|
|
@@ -314,10 +321,57 @@ gentle-shell self-uninstall [--dry-run] [--yes] [--include-shared]
|
|
|
314
321
|
|
|
315
322
|
`--link`, `--isolated`, and `--home` are mutually exclusive; combining two is a usage error, as is `--home` or `--home=` with an empty value. Effective-home precedence: an explicit flag wins, then the persisted `home` subcommand choice, then the `--isolated` default. Every argument gentle-shell does not recognize — `--mode rpc`, `-p "..."`, etc. — is forwarded to pi unchanged.
|
|
316
323
|
|
|
324
|
+
### Config home
|
|
325
|
+
|
|
326
|
+
Gentle Shell keeps its settings (profiles, models, banner, vim, permission mode
|
|
327
|
+
and the other `/gentle:*` preferences) in one config home. Every reader uses the
|
|
328
|
+
same rule, in this order:
|
|
329
|
+
|
|
330
|
+
1. A bundled install: `<prefix>/config/gentle-ai`.
|
|
331
|
+
2. `GENTLE_PI_CONFIG_HOME`, when set and not empty.
|
|
332
|
+
3. The isolated home (`--isolated`, the default) at `<dir>/.gentle-shell/agent`:
|
|
333
|
+
the `gentle-ai` folder next to it, `~/.gentle-shell/gentle-ai` by default. The
|
|
334
|
+
launcher tells Pi through `GENTLE_SHELL_CONFIG_HOME`.
|
|
335
|
+
4. Otherwise `~/.pi/gentle-ai`: `--link`, a custom `--home <path>`, a custom
|
|
336
|
+
`GENTLE_SHELL_HOME` outside a `.gentle-shell` folder (its parent may be any
|
|
337
|
+
folder, such as your home, so Gentle Shell never claims a `gentle-ai` folder
|
|
338
|
+
there), and gentle-pi installed inside Pi (Path B).
|
|
339
|
+
|
|
340
|
+
So a standalone install and a Pi install with gentle-pi no longer share one
|
|
341
|
+
store. Wherever this document names `~/.pi/gentle-ai` for a user setting, the
|
|
342
|
+
isolated home uses `~/.gentle-shell/gentle-ai` instead.
|
|
343
|
+
|
|
344
|
+
**First launch after the move.** When `~/.gentle-shell/gentle-ai` does not exist
|
|
345
|
+
yet, the launcher creates it (mode `0700`) and copies the regular files from
|
|
346
|
+
`~/.pi/gentle-ai` into it once, printing one line that names both folders. It
|
|
347
|
+
never overwrites, never changes or deletes the source (Pi still uses it), and
|
|
348
|
+
skips symbolic links and subfolders. A failed copy is reported and the launch
|
|
349
|
+
continues. Once the folder exists, nothing is copied again. With an explicit
|
|
350
|
+
`GENTLE_PI_CONFIG_HOME` nothing is copied.
|
|
351
|
+
|
|
352
|
+
**Files that stay in `~/.pi/gentle-ai`.** The main channel's install-level state
|
|
353
|
+
(`channel.json`, `dev-binary.json`, `main/` and `tools/go`) is written by the
|
|
354
|
+
installer and `gentle-shell upgrade`, outside any session, so it keeps
|
|
355
|
+
`GENTLE_PI_CONFIG_HOME` or `~/.pi/gentle-ai` for every home and is not copied.
|
|
356
|
+
Gentle AI runs that Gentle Shell starts from the isolated home get the same
|
|
357
|
+
folder the bundled way (`GENTLE_AI_PI_CONFIG_DIR`, plus `GENTLE_PI_CONFIG_HOME`
|
|
358
|
+
set to its parent for Gentle AI 4.0.0). A Gentle AI release that ignores both
|
|
359
|
+
still writes `persona.json` to `~/.pi/gentle-ai`, so `setup` restores both
|
|
360
|
+
persona files.
|
|
361
|
+
|
|
317
362
|
### `home` subcommand and `~/.gentle-shell/config.json`
|
|
318
363
|
|
|
319
364
|
`gentle-shell home` alone prints the effective mode and directory (`<mode> <dir>`) without persisting anything. `gentle-shell home link`, `gentle-shell home isolated`, or `gentle-shell home <path>` persists that choice to `~/.gentle-shell/config.json` as `{"home": "link" | "isolated" | "<path>"}`, so a later plain `gentle-shell` picks it up; a flag on a given invocation still overrides the persisted config without rewriting it.
|
|
320
365
|
|
|
366
|
+
`gentle-shell [--isolated | --home <path>] home import-pi [--dry-run]` copies your pi sign-ins into a separate home instead of sharing them with `--link`. The source is your own pi home, resolved like `--link` (`GENTLE_SHELL_USER_PI_HOME`, then `PI_CODING_AGENT_DIR`, then `~/.pi/agent`); the target is the resolved home (a flag, then the persisted choice, then the isolated default). It copies only:
|
|
367
|
+
|
|
368
|
+
- `auth.json`, written with mode `0600`, and `mcp.json`, with its source mode, each only when the target has no such file;
|
|
369
|
+
- `defaultProvider`, `defaultModel` and `defaultThinkingLevel` from the source `settings.json`, each only when the target `settings.json` lacks that key. Every other target key stays as it was; the file is rewritten atomically, and only when a key is added.
|
|
370
|
+
|
|
371
|
+
It never overwrites, never follows a symbolic link, never copies `packages`, `extensions`, `theme`, `npmCommand`, caches (`mcp-cache.json`, `mcp-npx-cache.json`), `AGENTS.md` or sessions, and never writes to the source. A target that does not exist yet (or is empty) is first set up exactly as a first launch would ([first run](#first-run-in-an-isolated-or-custom-home)), so the imported files never make it look foreign and the next launch still provisions it. It prints one line per item (`copied`, `already present, kept`, `not found`, `skipped: <reason>`, `failed: <reason>`) and a summary; `--dry-run` prints the same plan and writes nothing. It exits 0 when it succeeds or has nothing to do, 1 when a write fails (items already copied stay) or when the target is your pi home itself (`--link`, or a `--home` pointing at it), and 2 on any other argument. Running it again is safe.
|
|
372
|
+
|
|
373
|
+
Tradeoff: `--link` shares one credential store with pi; `import-pi` duplicates it. A provider that rotates refresh tokens on use may sign out whichever copy refreshes second; run `/login` again in that home.
|
|
374
|
+
|
|
321
375
|
### Managing packages
|
|
322
376
|
|
|
323
377
|
`gentle-shell install npm:<pkg>`, `gentle-shell remove ...`, `gentle-shell list`, `gentle-shell update ...`, `gentle-shell config`, and `gentle-shell auth ...` run pi's own commands against the resolved home — the `--isolated` home by default, or your own pi home with `--link`. A launcher flag before the subcommand (`--link`, `--isolated`, `--home <path>`) still selects which home the subcommand runs against. Running `gentle-shell install npm:gentle-pi` inside the isolated home is unnecessary: the launcher already loads the Gentle Shell package itself (see "Loading the package" below).
|
|
@@ -348,11 +402,13 @@ with `PI_CODING_AGENT_DIR` and `GENTLE_PI_AGENT_HOME` set to the resolved home,
|
|
|
348
402
|
|
|
349
403
|
Requires the package-local gentle-ai pin at v3.6.0 or newer — the pin that adds `PI_CODING_AGENT_DIR` support to `gentle-ai install`. `setup` enforces this before spawning anything: an older pinned gentle-ai ignores that variable and would silently install into `~/.pi/agent` instead of the target home, so `setup` exits 1 with `gentle-shell: setup needs the package-local gentle-ai v3.6.0 or newer (pinned: <version>); this build cannot provision a home without touching ~/.pi/agent` instead of spawning it.
|
|
350
404
|
|
|
351
|
-
gentle-ai
|
|
405
|
+
`setup` runs gentle-ai with `GENTLE_AI_PI_EXCLUDE_PACKAGES=npm:gentle-pi` (merged with any value you already set), so a Gentle AI that supports package exclusion (gentle-ai #5503) never installs the launcher-owned `npm:gentle-pi` at all. An older Gentle AI ignores the variable and its managed Pi stack declares `npm:gentle-pi` itself in the home's `settings.json` as part of that install. Once gentle-ai exits 0, `setup` removes any such declaration again immediately: this launcher always loads its own gentle-pi (its own package root, or a take-over — see "Loading the package" below), never the one gentle-ai's stack just installed, so leaving that declaration in place would let the home drift onto whatever gentle-pi npm last installed — or, for a developer running from a source checkout, onto the published npm package — instead of the running launcher's own copy. `setup` also removes `npm:@juicesharp/rpiv-ask-user-question` the same way, if gentle-ai declared it: that package conflicts with gentle-pi's own first-party `ask_user_question` tool, and Pi refuses to load two providers for the same tool name (tracked upstream as gentle-ai #4820). `--dry-run` only reports both pending removals instead of running them. A home only ever keeps a `npm:gentle-pi` declaration — and so only ever stops getting the launcher's own injection (see "Loading the package" below) — when something puts it back after `setup` runs: a hand-edited `settings.json`, or `gentle-shell install npm:gentle-pi` run manually; in that case the home behaves like a regular Pi agent home with gentle-pi installed, and `gentle-shell update npm:gentle-pi` updates it like any other package.
|
|
406
|
+
|
|
407
|
+
**Configuring MCP servers**: `gentle-shell mcp <command>` runs Pi's own `mcp` command against the resolved home, so a standalone install configures MCP without a separate `pi` command. For example, `gentle-shell mcp add <name> -- <command> [args...]` adds a stdio server, `gentle-shell mcp add <name> --url <url>` adds a remote one, and `gentle-shell mcp list` shows what the home's `mcp.json` defines. Restart Gentle Shell to load new servers.
|
|
352
408
|
|
|
353
409
|
**MCP migration**: Pi >= 0.99.0 provides built-in MCP; gentle-ai retires `pi-mcp-adapter` instead of installing it. After successful setup and package cleanup, Gentle Shell warns on stderr if `settings.json` still contains `"-builtin:mcp"`, `mcp.json` defines servers, and no active adapter remains. The warning names both files and the entry to remove. Setup never removes that entry or edits `mcp.json`: disabling MCP may be intentional. To enable those servers, remove only `"-builtin:mcp"` from `extensions` in the named `settings.json`, preserve other entries, and restart Pi. A declaration with `autoload: false` and no enabling extension patterns, an empty `extensions` list, or an ordinary `extensions: ["!**"]` filter without force-includes is inactive. Other nonempty filters require matching actual package resources, so setup reports uncertain adapter activity instead of silently treating the adapter as active. This read-only check also runs after automatic provisioning, but not during `--dry-run`. Errors encountered by the diagnostic produce an inspection warning without changing a successful setup's exit code; existing install and cleanup failures still propagate normally.
|
|
354
410
|
|
|
355
|
-
**Known limitation**: gentle-ai
|
|
411
|
+
**Known limitation**: the pinned gentle-ai writes its persona file to the shared `~/.pi/gentle-ai/persona.json` without honoring `PI_CODING_AGENT_DIR` (for the isolated home it may also write `~/.gentle-shell/gentle-ai/persona.json`; both are restored), so the persona is shared across every home `gentle-shell setup` provisions, not per-home. `setup` keeps that file byte-identical across the run regardless — snapshotting it before spawning gentle-ai and restoring it afterward, in manual, `--dry-run`, and automatic first-run modes alike — because the pinned gentle-ai still writes the Pi persona outside `PI_CODING_AGENT_DIR`. gentle-ai also records the running binary's managed-asset digest in the shared `~/.gentle-ai/state.json` (`managed_asset_digest`), again regardless of `PI_CODING_AGENT_DIR`, so a `setup` run otherwise leaves the user's own (unrelated, on-`PATH`) gentle-ai reporting its managed assets as outdated and demanding `gentle-ai sync`. `setup` restores just that one field afterward, the same way and in the same modes — but never the whole file, since `state.json` also carries fields (like the installed-agents list) the pinned gentle-ai is meant to update, and it never creates or deletes `state.json` itself.
|
|
356
412
|
|
|
357
413
|
**Test/development only**: `GENTLE_SHELL_GENTLE_AI_BIN` overrides which gentle-ai executable `setup` runs, bypassing the pinned package-local resolution. `GENTLE_SHELL_GENTLE_AI_PIN` overrides the pin version `setup` (and automatic first-run provisioning, below) checks against `MIN_SETUP_GENTLE_AI_VERSION` (3.6.0), independent of `GENTLE_SHELL_GENTLE_AI_BIN`. `GENTLE_SHELL_GENTLE_AI_INSTALLER` overrides the script path `setup` runs to self-heal a missing package-local binary, instead of the real `scripts/install-gentle-ai.mjs`. `GENTLE_SHELL_CONFIG` overrides the launcher config.json path (normally `<homedir>/.gentle-shell/config.json`), read and written by the `home` subcommand and by automatic first-run provisioning's marker. `GENTLE_SHELL_AUTO_SETUP_TIMEOUT_MS` overrides automatic first-run provisioning's 15-minute per-child timeout ceiling (manual `setup` never has one). All five exist for the test suite and for exercising a different gentle-ai build/pin/installer/config/timeout; end users never need them.
|
|
358
414
|
|
|
@@ -377,6 +433,8 @@ If none resolve, `gentle-shell` exits 1 naming all three options. Once a runtime
|
|
|
377
433
|
| `PI_CODING_AGENT_DIR` | Read to resolve the `--link` home when `GENTLE_SHELL_USER_PI_HOME` is unset; also set on the pi child process to the effective home. |
|
|
378
434
|
| `GENTLE_SHELL_USER_PI_HOME` | Set on the pi child process to your own pi home (recorded before isolating). Read back first to resolve the `--link` home and by `/gentle:stats`, so a nested `gentle-shell` keeps pointing at your original pi. |
|
|
379
435
|
| `GENTLE_PI_AGENT_HOME` | Set on the pi child process to the effective home; gentle-pi's own home resolution reads it back. |
|
|
436
|
+
| `GENTLE_SHELL_CONFIG_HOME` | Set on the pi child process only for the isolated home, to its config folder ([config home](#config-home)); removed for `--link`, `--home` and bundled launches. |
|
|
437
|
+
| `GENTLE_PI_CONFIG_HOME` | Your own override of the config home for every mode except a bundled install; outranks the isolated home's folder. |
|
|
380
438
|
| `GENTLE_SHELL_NO_AUTO_SETUP` | Set to `1` to skip automatic first-run provisioning (see "First run in an isolated or custom home" below). |
|
|
381
439
|
| `ENGRAM_STARTUP_POLICY` | Bundled startup recomputes `allow` or `disabled` from safety evidence. Explicit empty/invalid values remain disabled; user `allow` does not override unknown data. Standalone provider behavior with the variable absent is unchanged. |
|
|
382
440
|
| `ENGRAM_STARTUP_NOTICE` | Display-only recovery guidance; never executed. |
|
|
@@ -529,7 +587,7 @@ refusals and exit codes: [Removing Gentle Shell](gentle-shell-uninstall.md).
|
|
|
529
587
|
|
|
530
588
|
### First run in an isolated or custom home
|
|
531
589
|
|
|
532
|
-
The first time `gentle-shell` resolves to an isolated or `--home <path>` home that does not already exist, it creates the directory, writes `"tuiMode": "fullscreen"` and, unless the home's `settings.json` already declares one, `"theme": "Gentleman-Cute"` into its `settings.json`, writes a small ownership marker file at `<home>/.gentle-shell-home` (a one-line JSON object naming the `gentle-pi` version that created it), and prints one hint to stderr pointing at `--link
|
|
590
|
+
The first time `gentle-shell` resolves to an isolated or `--home <path>` home that does not already exist, it creates the directory, writes `"tuiMode": "fullscreen"` and, unless the home's `settings.json` already declares one, `"theme": "Gentleman-Cute"` into its `settings.json`, writes a small ownership marker file at `<home>/.gentle-shell-home` (a one-line JSON object naming the `gentle-pi` version that created it), and prints one hint to stderr pointing at `--link` (and at `gentle-shell home import-pi` when your pi home has an `auth.json`). A `--link` home is never bootstrapped this way — it is assumed to already exist as your pi agent home. Later runs against the same home skip the write and the hint. The default theme is also re-applied after automatic or manual setup if gentle-ai's own managed install wrote a different theme into a home that had none before that run; a home (or `--link`) that already declares its own theme is never touched.
|
|
533
591
|
|
|
534
592
|
Right after that bootstrap, and on every later launch, a plain `gentle-shell` against an isolated or `--home <path>` home (never `--link`, and never a pi subcommand like `gentle-shell install/remove/list/...`) also runs the same flow as `gentle-shell setup` automatically before starting pi, so you never have to know `setup` exists. It runs when the home has never been provisioned, was provisioned with a gentle-ai pin different from the package-local pin this `gentle-pi` ships — for example after upgrading `gentle-pi` to a version pinned to a newer gentle-ai — or was provisioned by a different `gentle-pi` version than the one now running — for example after `npm i -g gentle-pi` upgrades the launcher itself, so the home re-syncs to match it. A completed run is recorded as `provisioned: {"<realpath of the home>": {"gentleAi": "<pin>", "gentlePi": "<running gentle-pi version>", "at": "<ISO timestamp>"}}` in the launcher's config.json (`~/.gentle-shell/config.json` by default, or `GENTLE_SHELL_CONFIG` when overridden — see "Environment variables" above), preserving every other key already in that file (including the persisted `home` mode and any other home's marker). A marker written before this `gentlePi` field existed always counts as needing provisioning too, so the very next launch backfills it.
|
|
535
593
|
|
|
@@ -598,7 +656,7 @@ Synthetic tests prove the observer's behavior, not the cause of issue #213. A ne
|
|
|
598
656
|
|
|
599
657
|
```text
|
|
600
658
|
/gentle:status Check package assets and global model config.
|
|
601
|
-
/gentle:doctor Run read-only diagnostics for assets, config, tools, and
|
|
659
|
+
/gentle:doctor Run read-only diagnostics for assets, config, tools, guards, and Herdr setup.
|
|
602
660
|
/gentle:models Assign global model/effort routing to packaged/custom agents.
|
|
603
661
|
/gentle:profiles Create, switch, and manage global agent-model profiles.
|
|
604
662
|
/gentle:persona Switch between gentleman and neutral persona modes.
|
|
@@ -645,11 +703,11 @@ Size and uncertainty can call for scoped exploration or delegation within ODD. T
|
|
|
645
703
|
| Trigger | Required behavior |
|
|
646
704
|
| --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
|
|
647
705
|
| Understanding needs more than the evidence budget (one parallel batch of at most 3 calls, ~10k tokens) or more than ~5 sequential lookups | Launch `scout`, `context-builder`, or the closest read-only mapping subagent; it returns a handoff of at most ~2k tokens with `path:line` evidence. Never force delegation for a small targeted question. |
|
|
648
|
-
| A large (tracked) task | Track it in the logbook; delegate a writer only for a reason (parallel units or context), never for size, file count, or a price ratio. |
|
|
649
|
-
| A high-risk
|
|
706
|
+
| A large (tracked) task | Track it in the logbook; delegate a writer only for a reason (parallel units or context), never for size, file count, or a price ratio. A parallel unit is one independent piece of behavior (its code, tests, and docs together) that needs several minutes of work; smaller or dependent changes stay inline. Each writer prompt names the existing helpers and invariants its unit must reuse. |
|
|
707
|
+
| A change that touches a named high-risk item (assess `escalate`) | Run an independent verifier after the change's own checks; otherwise the focused test and suite run inline. A native high tier, a failed assessment, or the writer's model never adds one by itself. |
|
|
650
708
|
| Commit, push, or PR after code changes | Follow the loaded native instruction, or ordinary repository policy when none is supplied. |
|
|
651
709
|
| Wrong cwd, worktree/git accident, merge recovery, confusing test/env issue | Stop, preserve the affected scope, and investigate separately before resuming. |
|
|
652
|
-
|
|
|
710
|
+
| The runtime signals the context backstop | Pause and delegate the next bounded unit that needs several steps (its reads, edits, and checks), or stop and explain the exact blocker. Single small edits and state checks stay inline; keep command output bounded; a small task still runs its focused test and suite inline. |
|
|
653
711
|
|
|
654
712
|
The intended balanced loop for a bounded bugfix is:
|
|
655
713
|
|
|
@@ -661,6 +719,28 @@ parent git/status + clarify → one worker writes authorized fixes → focused v
|
|
|
661
719
|
|
|
662
720
|
"Runtime-enforced" means an admission-time check inside one Pi process: when `subagent_run` or `subagent_continue` queues a bounded writer, it is rejected if a queued or running writer of the same parent process, in the same worktree, has an overlapping `## Allowed edit surfaces` entry. Writers are keyed by their canonical worktree root (the realpath of the Git worktree root, or the realpath of the cwd outside Git), so a subdirectory cwd, the root itself, and a symlinked spelling count as one worktree. It is not a write-time guard: it does not stop a writer from editing outside its declared surfaces, and separate Pi parent processes do not coordinate with each other. Overlap is conservative: an entry also covers everything under it, a whole-segment `**` covers any depth, while `**` inside a segment (for example `src/**.ts`) matches within that one segment, like minimatch. Entries compare in Unicode NFC. Entries with bracket classes, extglob groups, backslashes, or nested, unbalanced, or `/`-spanning braces overlap every other entry, and a wildcard never proves two entries disjoint when either contains non-ASCII characters. A quarantined writer whose process exit is unconfirmed keeps its surfaces until the exit is confirmed; a process that survives quarantine keeps both its surface claim and its concurrency slot until its exit is observed, which can last for the parent's lifetime. Any RDD-specific actor behavior belongs to the runtime instruction supplied by Gentle AI, not to this README.
|
|
663
721
|
|
|
722
|
+
A second admission check stops small-task delegation: in the primary session, a `gentle-ai-worker` or `worker` launch is admitted only when its task or context has a line that starts with `Spec: odd/tasks/<feature>.md` and names a task `T<n>` later on that line (for example `, T2, S3` or the delegation card's `(read until "## Log"). Do T2; S3.`), where the document is a regular file under the writer root's `odd/tasks/` after resolving symlinks, or when the parent's measured context reaches the context backstop described below. Otherwise the launch is rejected with an instruction to write the unit inline. Judgment Day fix agents, read-only subagents, and continuations are not affected.
|
|
723
|
+
|
|
724
|
+
A third admission check keeps the independent verifier for high-risk changes: in the primary session, a `gentle-ai-verify` launch is admitted only when its task or context has a line that starts with `Risk:` (optionally as a list item) and names a Task Size high-risk item first, in the form `Risk: item N` with N from 1 to 6 (several items are allowed, for example `Risk: item 2, item 3 (reason)`), or when it carries a feature-document task line the writer check above accepts, or when the parent's measured context reaches the context backstop. `Risk: none` or no line is rejected with an instruction to run the change's focused test and suite inline, not to retry the launch, and never to add a Risk line or create a feature document only to get a verifier admitted. Subagent children, continuations, writers, Judgment Day agents, native review relays, and read-only explorers are not affected.
|
|
725
|
+
|
|
726
|
+
Every subagent child runs with its own `TMPDIR` (also `TMP` and `TEMP` on Windows): a fresh `gaXXXXXX` directory created under the inherited temp directory (joined to the inherited value as given, never its real path), so scratch the child makes with `mktemp` stays out of the worktree. The runtime removes that directory and everything in it when the child ends, including a failed launch. A quarantined child, whose process exit is unconfirmed, keeps its directory under the system temp directory, because a surviving descendant may still use it; once its exit is proven, including a process group that ends after the child's own exit, the directory is removed and the slot is freed. Each continuation is a new child with a new directory.
|
|
727
|
+
|
|
728
|
+
After a successful `git commit` run through `bash` in the primary session, the runtime appends one line to the tool result with the branch's authored changed lines since its merge base with the default branch (`origin/HEAD`, else `main` or `master`), counting additions plus deletions (a moved file counts only the lines the move changed) and excluding lock files and binary files, whatever characters their paths hold, and the commit count. The count runs in the directory of `git -C <dir>` under any `cd <dir>` that precedes the commit through `;`, `&&`, `||`, or a new line. Below about 400 lines it says how many remain; past them it says to apply the feature's delivery strategy before the next commit. Commits on the default branch, outside Git, or from subagents get no line, and neither do commits run as background jobs (their result arrives before the commit exists) or a count whose `git` query fails or times out. Only a `git` invocation that starts a shell command or segment is a commit; text that mentions `git commit` (an `echo`, a message) is not. The count runs asynchronously, so the parent stays responsive while `git` answers.
|
|
729
|
+
|
|
730
|
+
The context backstop is measured, not estimated. Pi's `getContextUsage()` reports the parent's context tokens and the model's context window, and the backstop is `min(absolute cap, fraction × context window)`: 150,000 tokens and 0.15 by default, overridable with `GENTLE_PI_CONTEXT_BACKSTOP_TOKENS` (a positive number of tokens) and `GENTLE_PI_CONTEXT_BACKSTOP_FRACTION` (greater than 0, at most 1); invalid values keep the defaults. The cap holds for any model because carried context is paid again on every remaining turn whatever the provider; the fraction holds because answer quality and headroom depend on how full the window is, so a window reaches the backstop at 15% of its size until the cap applies (about 41k for a 272k window; the 150,000-token cap from a 1M window up). The 0.15 default comes from the bench: on a wide five-feature task, delegating at that point cost less and finished sooner, while small tasks never reached it. An unknown window leaves only the cap; unknown tokens (for example right after compaction, before the next response) never count as reaching it. In the primary session, the first tool result after the measured context reaches the backstop gets one appended notice with the measured tokens, the percent of the window, and the threshold, telling the parent to delegate the next bounded unit that needs several steps with a short handoff while single small edits and state checks stay inline. The tool output and structured content are kept and the error flag is never changed; a commit result carries the slice line first. The notice is sent once per crossing and re-armed when the measured context drops below the backstop again, for example after compaction. Subagents never get it. The writer and verifier admission checks above use the same threshold.
|
|
731
|
+
|
|
732
|
+
### Session scratch and artifact ledger
|
|
733
|
+
|
|
734
|
+
At primary session start the runtime creates a private `gpXXXXXX` directory under the inherited temp directory and points the Pi process's `TMPDIR` (also `TMP` and `TEMP` on Windows) at it, spelled as the inherited value joined with that name, never its real path, so every `mktemp` the model runs and every subagent's own `TMPDIR` land under it. Delegated children never create one. If the directory cannot be created, the session keeps the inherited `TMPDIR`. A reload keeps the same directory: Pi re-evaluates extension modules on reload, so the runtime recovers the live scratch from the ledger when it names this process, the directory passes the removal checks below, and `TMPDIR` still points into it. When a reload cannot recover it, the new scratch is not nested in the old one: the runtime uses the recorded `inherited` values of this process's ledger, else `TMPDIR` when it is not a `gpXXXXXX` directory, and the parent of that directory when it is. Startup never removes a scratch of its own process.
|
|
735
|
+
|
|
736
|
+
The runtime records the session in a ledger outside Engram: `<config home>/scratch/<session id>.json` (the config home is `GENTLE_PI_CONFIG_HOME` when set), written atomically (temp file, then rename) with private permissions. It holds `version`, `sessionId`, `pid`, `processStartedAt` and `processStartToken` of the owning process (the start token is the OS's own record of when the process started: the start tick from `/proc/<pid>/stat` on Linux, the `ps -o lstart=` text on macOS and the BSDs; absent on Windows or when it cannot be read), `cwd`, `tempRoot` (the real temp directory the scratch was created directly under), `inherited` (the temp variables before the session changed them, restored at shutdown), `scratchDir`, and `artifacts`: each clone or worktree as `kind` (`clone` or `worktree`), its real `path`, the `command` that created it, and `recordedAt`. Before a foreground `bash` command in the primary session runs, the runtime resolves the destination of `git clone ... [<dir>]` (git's default directory name when omitted) and the path of `git worktree add ... <path>` against the command's `cd` segments and `git -C`; after the command succeeds it records each one that did not exist before the command and is a directory afterwards, so a fallback such as `git clone URL dir || (cd dir && git pull)` over an existing clone records nothing. The session directory, the home directory, the config home, and their ancestors are never recorded.
|
|
737
|
+
|
|
738
|
+
Cleanup is automatic for scratch only. At session shutdown (other than a reload) the runtime removes its scratch dir, restores the inherited `TMPDIR`, and deletes the ledger when no artifacts remain. The `TMPDIR` of a live or quarantined subagent (still running, or its exit not yet confirmed) stays, with the scratch dir around it, while the rest of the scratch is removed; the ledger then keeps the scratch for a later startup sweep. At the next primary session start it sweeps ledgers of ended sessions, whose owning process is not alive or has a start token other than the one its ledger recorded (the OS may reuse a pid; tokens are compared as recorded, never through the wall clock, so a clock step cannot make a live session look ended), and whose ledger is older than one hour. When the ledger has no start token or the current one cannot be read, as on Windows, a live pid counts as the ledger's process. The sweep removes their scratch dirs and keeps their artifact entries for the command. A scratch dir is removed only when it is the exact real directory its ledger recorded, lies directly under the temp root its ledger recorded (a temp root inside the home directory only when it is the current session's own), is named `gpXXXXXX`, is a directory rather than a symlink, and belongs to the current user; removal never follows symlinks, and a scratch dir that holds a recorded artifact is kept. The name carries no session id, so a scratch dir recorded by the ledger of a live session (its owning process alive with the start token its ledger recorded, or the current session) is never removed through another session's ledger, by the sweep or by `clean`, and neither is a directory that holds one.
|
|
739
|
+
|
|
740
|
+
The names are short because Unix socket paths created under `TMPDIR` are limited to 104 bytes on macOS, including the terminating NUL (108 on Linux), and a macOS real path adds `/private`. With a 48-byte inherited `TMPDIR` such as `/var/folders/xx/<30 characters>/T`, a subagent's `TMPDIR` is 66 bytes, which leaves room for a 32-byte socket name such as Python multiprocessing's `pymp-XXXXXXXX/listener-XXXXXXXX` (98 of at most 103 bytes).
|
|
741
|
+
|
|
742
|
+
`/gentle:scratch list` shows the session's scratch dir with its size and every recorded artifact of this and ended sessions; ledgers of other live sessions are not shown. `/gentle:scratch clean` removes ended sessions' scratch dirs and empties this session's (keeping subagent scratch, the scratch of other live sessions, and entries with anything changed in the last minute before the command started, which a running command may be using) without asking. After one confirmation that lists them and warns that ignored files inside them are deleted too, it removes recorded worktrees and clones with no uncommitted changes and no unpushed commits (every commit of a clone is on a remote; a detached worktree's commits are on a branch); a clone that has linked worktrees other than prunable ones is kept as "has linked worktrees", since removing it would orphan them; the others are reported with the reason and kept. Worktrees are removed with `git worktree remove` and then pruned; clones with a recursive delete of the recorded path only, refused when the path is now a symlink or no longer the recorded Git root. Without a UI, `clean` removes scratch only and reports the artifacts.
|
|
743
|
+
|
|
664
744
|
### Review authority recovery and reset safety
|
|
665
745
|
|
|
666
746
|
Legacy pre-graph authority is never migrated. `gentle_review inspect` reports an exact repository-bound destructive reset challenge for legacy corruption; after that fresh interactive authorization, RESET and RECOVER_LOCK route to the audited native `gentle-ai review reclaim` operation and RECOVER routes to native `gentle-ai review recover`, so every destructive transition is executed and audited by the native authority store. Native inputs the request did not carry return a `native-input-required` envelope instead of being invented. Existing graph-v1 ordinary lineages remain readable and gate-validatable but are read-only; Judgment Day remains mutable on graph-v1.
|
|
@@ -730,7 +810,7 @@ flowchart TD
|
|
|
730
810
|
|
|
731
811
|
VALIDATE is informational. Commit, push, PR, and release commands follow ordinary repository policy; RDD never authorizes, rewrites, consumes review state for, or blocks them. Dangerous-command safety and destructive-review consent remain independent.
|
|
732
812
|
|
|
733
|
-
For the source checkout, native contract pairing is exact: this adapter resolves only the integrity-verified package-local Gentle AI v4.
|
|
813
|
+
For the source checkout, native contract pairing is exact: this adapter resolves only the integrity-verified package-local Gentle AI v4.2.0 executable, independently hashes it, then negotiates `gentle-ai.review-integration/v2` outside the repository. Capabilities are cached by that executable digest. Every START, target status, FINALIZE, and validate request passes the same contract identifier. Negotiated envelopes decode exactly against the vendored schemas; `recover` routes only the provider-selected `action_disposition`, and optional additions require a future compatible schema/minor that the provider explicitly advertises and the consumer negotiates.
|
|
734
814
|
|
|
735
815
|
Contract `/v2` replaces the Base64 `candidate_diff` reviewer transport of `/v1` with immutable `base_tree`/`candidate_tree` plus an ordered `changed_path_manifest` and never an inline patch. `gentle-pi` negotiates `/v2` only, with no dual-lane fallback; the cutover landed as one atomic commit against gentle-ai v2.2.2 (tracked by the `migrate-review-integration-v2` change), and the `/v1` schemas stay packaged because the `/v2` schemas `$ref` into their fragments. This provider contract version is unrelated to Pi's own internal "compact-v2" review-authority naming used below — the shared digit is coincidental, not a version pairing.
|
|
736
816
|
|
|
@@ -740,7 +820,7 @@ Candidate views materialize tracked Git symlinks from their frozen blobs even wh
|
|
|
740
820
|
|
|
741
821
|
On POSIX, if START rejects a group- or world-accessible `.git/gentle-ai/candidate-views` parent, Pi reports `candidate-owner-parent-privacy` before native START. When a sanitized probe shows the filesystem cannot represent private POSIX modes (for example WSL DrvFS mounts without `metadata`), Pi instead reports `candidate-owner-parent-chmod-ineffective` with guidance to move the Git common directory to a POSIX-metadata filesystem or enable metadata support. Inspect that parent's ownership and permissions and correct them out of band before retrying; Pi does not change them automatically. Other owner-preparation failures retain a generic diagnostic rather than exposing filesystem errors.
|
|
742
822
|
|
|
743
|
-
Once the source checkout's pinned gentle-ai runtime (currently v4.
|
|
823
|
+
Once the source checkout's pinned gentle-ai runtime (currently v4.2.0) has written review authority, rollback MUST preserve every native store and receipt and MUST NOT run a downgraded binary against that repository. Disable the Pi route or roll forward to a compatible authority-aware release instead; deleting authority data or reinstalling an older binary is not a rollback path.
|
|
744
824
|
|
|
745
825
|
### FINALIZE wrapper input
|
|
746
826
|
|
|
@@ -810,6 +890,8 @@ An ordinary `gentle_review` START input may carry the agent lens selection: `{"m
|
|
|
810
890
|
|
|
811
891
|
Review outcomes and receipt state are informational; commit, push, pull-request, and release delivery follow ordinary repository policy. No one-shot command authorization, publication-target revalidation, or receipt gate is required for delivery, and Pi does not inspect RDD mode or native authority to decide a Bash delivery command.
|
|
812
892
|
|
|
893
|
+
When grouped review capture returns `native-status-failed`, a decoded native failure is preserved as `native_failure`, including its phase, code, retry policy, and optional cause/context. `replayability` and `next_action` expose the provider's guidance; they do not trigger a retry. Group progress and mutation reporting still describe reviewers already prepared/submitted. Untyped errors retain their message-only behavior; raw process stdout/stderr and arbitrary error fields are not exposed.
|
|
894
|
+
|
|
813
895
|
Dangerous-command safety remains independent and authoritative. Destructive-review-maintenance consent remains separate from delivery. Review operations and informational VALIDATE perform no commit, push, pull-request, release, or publication operation.
|
|
814
896
|
|
|
815
897
|
The Pi host relay bounds each locked-down reviewer subprocess by materialized prompt size rather than by one fixed number: a 15-minute floor plus 15 minutes per mebibyte of prompt, clamped to a 2-hour ceiling. Set `GENTLE_PI_REVIEW_RELAY_PI_TIMEOUT_MS` to a positive decimal to replace that derived bound with your own; malformed values are ignored and the same 2-hour ceiling still applies, so no configuration turns a foreground finalize into an unbounded child process. A reviewer killed by the bound reports `pi-host-relay-timeout` with the elapsed time and the limit it was measured against, and it explicitly does not ask you to relaunch the identical slot — that would re-spend the model tokens to reach the same wall. Reviewer results admitted earlier in the same finalize stay admitted and are not re-run.
|
|
@@ -1116,6 +1198,8 @@ The notice appears once per distinct change: further launches stay quiet until t
|
|
|
1116
1198
|
|
|
1117
1199
|
**Subagent children** never freeze, compare, or warn: their model comes from the parent's launch.
|
|
1118
1200
|
|
|
1201
|
+
**Task card layer token.** Every agents task card shows which layer supplied the launch's model, right after the model itself (for example `nan/glm5.3 · binding`): `binding` (selected in this session), `frozen:local` / `frozen:repo` / `frozen:global` (the startup snapshot and its origin), `pin:local` / `pin:repo` (a repository pin won), `declaration` (a non-Git repository declaration won), `profile` (the globally active profile routing), `definition` (the agent's own frontmatter), or `default` (Pi's default model). This makes staleness visible: a launch through a frozen session keeps showing its frozen token even after the editable layers below it change on disk.
|
|
1202
|
+
|
|
1119
1203
|
**Current limits.** The frozen profile is kept in memory for the life of the Pi process, so `/reload` keeps it. Resuming a session in a new process (`--resume`, `--continue`, `--session`), `/new`, and `/fork` freeze again from the defaults current at that moment, and the drift notice memory starts empty, so a pending change is announced once more. Persisting the frozen profile in the session file is planned.
|
|
1120
1204
|
|
|
1121
1205
|
## Commands
|
|
@@ -1123,7 +1207,7 @@ The notice appears once per distinct change: further launches stay quiet until t
|
|
|
1123
1207
|
| Command | What it does |
|
|
1124
1208
|
| -------------------------------- | ------------------------------------------------------------------- |
|
|
1125
1209
|
| `/gentle:status` | Shows package assets and global model config status. |
|
|
1126
|
-
| `/gentle:doctor` | Runs read-only diagnostics for assets, model/persona config, memory tools, and
|
|
1210
|
+
| `/gentle:doctor` | Runs read-only diagnostics for assets, model/persona config, memory tools, safety guards, and (inside Herdr) the task-summary sidebar setup. |
|
|
1127
1211
|
| `/gentle:models` | Opens global model + effort assignment UI. Press `x` to export, `r` to restore saved routing, and `u` to save routing and capture the session in the current profile. |
|
|
1128
1212
|
| `/gentle:profiles` | Opens global agent-model profiles: apply live, create, snapshot, duplicate, rename, delete, export, and import. |
|
|
1129
1213
|
| `/gentle:commands` | Opens the command palette (default `alt+k`): a curated, grouped menu (Configuration, Session, Diagnostics, Skills) of registered Gentle commands; search and run by label. |
|
|
@@ -1134,6 +1218,7 @@ The notice appears once per distinct change: further launches stay quiet until t
|
|
|
1134
1218
|
| `/gentle:vim` | Shows or sets opt-in prompt Vim mode (`status\|enable\|disable`); no argument opens a selector. |
|
|
1135
1219
|
| `/gentle:telemetry` | Shows or changes the local Gentle AI telemetry trigger (`status\|enable\|disable\|preview`). |
|
|
1136
1220
|
| `/gentle:review-mode` | Shows or sets the receipt-driven development mode (`status\|enable\|disable`); user-initiated only, Pi automation never toggles it. |
|
|
1221
|
+
| `/gentle:scratch` | Lists (`list`, the default) or cleans (`clean`) this session's scratch dir and the clones and worktrees recorded by this and ended sessions; see [Session scratch and artifact ledger](#session-scratch-and-artifact-ledger). |
|
|
1137
1222
|
| `/gentle:status-timing` | Arms timing for the next authorized STATUS-bearing tool call, clears it, or manually shows the last summary (`enable\|disable\|show`). Off by default, session-only, no native invocation. |
|
|
1138
1223
|
| `/gentle:banner` | Configures startup banner rose, text logo, and color preset. |
|
|
1139
1224
|
| `/gentle:toggle-rose` | Toggles the startup rose. |
|
|
@@ -1316,8 +1401,11 @@ To opt out:
|
|
|
1316
1401
|
| `contracts/review-integration/v2/` | Byte-identical provider schemas and conformance fixtures for contract `review-integration/v2` (immutable `base_tree`/`candidate_tree`, ordered `changed_path_manifest`, no inline candidate diff), hash-checked before packaging. |
|
|
1317
1402
|
| `extensions/startup-banner.ts` | Shows and configures the startup intro, color presets, and compact runtime panel. |
|
|
1318
1403
|
| `extensions/skill-registry.ts` | Maintains `.atl/skill-registry.md` from project/user skills and closes file watchers on shutdown. |
|
|
1404
|
+
| `extensions/codegraph-tools.ts` | Runs workspace-scoped CodeGraph init/query/explore and keeps `.codegraph/` out of git through the worktree's local `info/exclude` (never a tracked file). |
|
|
1405
|
+
| `contracts/review-provider-contract-mirror/` | Verified mirror of the Gentle AI provider contract bundle: the `orchestration/pi.md` review execution contract and the `routing/pi.md` ODD steps. Refresh it only with `node scripts/mirror-provider-contract.mjs --bundle <local archive or tree>`; `pnpm run check:provider-contract` checks it offline against its lock. |
|
|
1319
1406
|
| `assets/orchestrator.md` | Parent-session orchestration contract (always-on core). |
|
|
1320
|
-
| `assets/orchestrator-delegation.md` | Lazy-loaded delegation
|
|
1407
|
+
| `assets/orchestrator-delegation.md` | Lazy-loaded delegation card read when the parent delegates: the mirrored gentle-ai triggers, handoff, launch, and Key Learnings rules. |
|
|
1408
|
+
| `assets/orchestrator-delegation-reference.md` | Lazy-loaded delegation reference read only when named: per-action table, Work Routing Ladder, background policy, runtime bindings, canonical workflows. |
|
|
1321
1409
|
| `assets/orchestrator-tracking.md` | Lazy-loaded large-task ODD tracking: authorization, research, checks and consent, phase signaling, RDD assess, delivery. |
|
|
1322
1410
|
| `assets/orchestrator-verification.md` | Lazy-loaded Verification rule, native risk tiers, and writer verification contract. |
|
|
1323
1411
|
| `assets/orchestrator-writer.md` | Lazy-loaded allowed edit surfaces and Judgment Day fix dispatch. |
|
package/docs/yolo-mode.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# YOLO permission
|
|
2
2
|
|
|
3
|
-
> 🚀 **Full speed, destructive actions still ask.** YOLO lets the agent implement, commit, push and open PRs within the already authorized task without asking each time, which suits long autonomous runs. Destructive operations still require fresh confirmation.
|
|
3
|
+
> 🚀 **Full speed, destructive actions still ask.** YOLO lets the agent implement, commit, push and open PRs within the already authorized task without asking each time, which suits long autonomous runs. Destructive operations still require fresh confirmation; the one exception is recursive removal of the session's own temp scratch (see [Temp scratch removal](#temp-scratch-removal)).
|
|
4
4
|
|
|
5
5
|
Use `/gentle:yolo enable` in the interactive primary Pi TUI to stop repeated permission questions for ordinary work **within your already authorized task**. YOLO is **OFF** unless you select it or save YOLO as the user-global default. [Normal, Auto and global defaults](permission-modes.md) are managed separately from the live-session toggle. The active status and a separate editor widget both say **🚀 YOLO ON 🔥 — destructive confirmations remain**, even when Gentle Shell hides its status layout.
|
|
6
6
|
|
|
@@ -29,17 +29,29 @@ The live YOLO toggle does not save preferences. The separate **Default permissio
|
|
|
29
29
|
|
|
30
30
|
YOLO supplies standing human permission for ordinary already-scoped implementation, checks, commits, non-force pushes and PR creation. Its active-only system instruction qualifies the default commit/push/PR confirmation clauses; it also tells the primary agent to make ordinary reversible implementation choices without needless interviews.
|
|
31
31
|
|
|
32
|
-
The shell guard's routine default confirmation is skipped for reversible delivery only. Every guarded segment of the command is judged on its own, so `git commit -m "..." && git push -u origin feature`, `git push && gh pr create --fill`, `git -C <dir> push origin main` and `git fetch && git rebase origin/main && git push` run without a prompt, while the other segments keep their ordinary guard (`git push && rm -rf data` still asks for the `rm`). A push segment is eligible with plain remote/ref arguments, `src:dst` refspecs with both sides present, and the options `-u`/`--set-upstream`, `-q`/`--quiet`, `-v`/`--verbose`, `--tags`, `--follow-tags`, `--no-verify` and `--porcelain`. A rebase segment is eligible with plain refs, `--onto`, `--continue`, `--abort`, `--skip` and `--autostash`; it is recoverable from the reflog. Anything that deletes or rewrites a remote ref, publishes, or runs arbitrary commands never qualifies: `--delete`/`-d`, `:branch` and `+branch` refspecs, `--mirror`, `--prune`, `--all`, every force spelling (hard-blocked), `--receive-pack`/`--exec`, `--repo`, `-o`, `sudo`, shell wrappers such as `sh -c`, interactive or `--exec` rebases, `git branch -D`, `pi remove` and `npm publish`. Full command evaluation, including the recognized data-loss guards, runs first. Explicit push or rebase confirmations or blocks in either configuration layer remain restrictions, including when legacy persistent autonomy or its environment override would ignore them. YOLO never changes persistent `autonomousMode`.
|
|
32
|
+
The shell guard's routine default confirmation is skipped for reversible delivery only. Every guarded segment of the command is judged on its own, so `git commit -m "..." && git push -u origin feature`, `git push && gh pr create --fill`, `git -C <dir> push origin main` and `git fetch && git rebase origin/main && git push` run without a prompt, while the other segments keep their ordinary guard (`git push && rm -rf data` still asks for the `rm`). A push segment is eligible with plain remote/ref arguments, `src:dst` refspecs with both sides present, and the options `-u`/`--set-upstream`, `-q`/`--quiet`, `-v`/`--verbose`, `--tags`, `--follow-tags`, `--no-verify` and `--porcelain`. A rebase segment is eligible with plain refs, `--onto`, `--continue`, `--abort`, `--skip` and `--autostash`; it is recoverable from the reflog. Either segment may also carry output redirections that write nothing (`2>&1`, `>&2`, `>/dev/null`, `2>/dev/null`, `&>/dev/null`) and may be piped into the read-only filters `tail`, `head`, `cat`, `grep` and `wc` with plain or literally quoted arguments, so `git push -u origin feature 2>&1 | tail -5` runs without a prompt. Redirection to a file, a pipe into any other command (`| sh`, `| tee file`, `| xargs`) and a trailing background `&` still ask: Git output carries text the remote controls. Anything that deletes or rewrites a remote ref, publishes, or runs arbitrary commands never qualifies: `--delete`/`-d`, `:branch` and `+branch` refspecs, `--mirror`, `--prune`, `--all`, every force spelling (hard-blocked), `--receive-pack`/`--exec`, `--repo`, `-o`, `sudo`, shell wrappers such as `sh -c`, interactive or `--exec` rebases, `git branch -D`, `pi remove` and `npm publish`. Full command evaluation, including the recognized data-loss guards, runs first. Explicit push or rebase confirmations or blocks in either configuration layer remain restrictions, including when legacy persistent autonomy or its environment override would ignore them. YOLO never changes persistent `autonomousMode`.
|
|
33
33
|
|
|
34
34
|
## What still requires a decision
|
|
35
35
|
|
|
36
|
-
- Destructive operations retain fresh confirmations or hard denial: recognized SQL DROP/TRUNCATE and broad deletion, recursive removal, destructive Git and other guarded operations are not waived. Explicit configured confirmations/blocks and sensitive-path protection remain.
|
|
36
|
+
- Destructive operations retain fresh confirmations or hard denial: recognized SQL DROP/TRUNCATE and broad deletion, recursive removal, destructive Git and other guarded operations are not waived. The only exception is [temp scratch removal](#temp-scratch-removal). Explicit configured confirmations/blocks and sensitive-path protection remain.
|
|
37
37
|
- Current explicit human restrictions, repository policy, project trust and authorized scope still apply. Scope expansion, privacy-sensitive disclosure and genuinely unresolved consequential product choices require a human decision.
|
|
38
38
|
- Ambiguous destinations or credentials remain unresolved. YOLO does not invent a remote, deployment destination, account or credential, nor authorize discovery or reuse of ambient credentials.
|
|
39
39
|
- `ask_user` tools, provider consent, maintenance/recovery authorization and opaque-token choices are never automatically answered. Native review, RDD and review standing permission remain separate and unchanged.
|
|
40
40
|
|
|
41
41
|
Children retain only their bounded delegated scope. They cannot activate or inherit YOLO, and receive no independent delivery permission.
|
|
42
42
|
|
|
43
|
+
### Temp scratch removal
|
|
44
|
+
|
|
45
|
+
In the primary session with YOLO active, a recursive `rm` whose every target is the session's own temp scratch runs without the data-loss confirmation. For example, `rm -rf /tmp/tmp.8phya1BpFr /tmp/build.log; git log --oneline -1` runs directly when both targets qualify. A target qualifies only when all of these hold:
|
|
46
|
+
|
|
47
|
+
- The segment is a plain `rm` with no wrapper (`sudo`, `env`, `xargs`, assignments), not inside `sh -c` or `find -exec`, and without `--no-preserve-root`.
|
|
48
|
+
- The removal runs before anything else in the command. The command must start with one or more such `rm` segments joined by `;`, `&&`, `||` or newlines. Nothing may precede them: no `mv`, `cp`, `ln`, redirection, subshell or other command. They also may not be backgrounded with `&` or piped with `|`. Otherwise an earlier step could put other data into a target that looked like scratch when the command was checked, so `mv ~/data /tmp/tmp.abcdef1234 && rm -rf /tmp/tmp.abcdef1234` still asks. Commands after the removals, such as `cd … && git log`, are allowed.
|
|
49
|
+
- The target is an unquoted literal absolute path made only of letters, digits, `/` and `._-+@%=:,`. It has no `$`, backtick, `~`, glob, brace or redirection characters, no `.`/`..` components and no trailing `/`.
|
|
50
|
+
- The target is exactly one path component under `/tmp`, `/var/tmp` or the resolved `os.tmpdir()`. The temp root itself never qualifies, and a temp root that is `/`, your home directory, one of its ancestors or a directory inside your home is ignored.
|
|
51
|
+
- When the command is checked, the target is missing, a symlink, a regular file you own, or a directory you own named like `mktemp` output (`tmp.` followed by six or more letters or digits).
|
|
52
|
+
|
|
53
|
+
Anything else keeps asking, including another user's directory, a directory not named like `mktemp` output, an unreadable path and `$TMPDIR/...`. Every other effect in the same command keeps its ordinary guard: `rm -rf /tmp/tmp.abcdef1234 && rm -rf data` still asks, and `rm -rf /tmp/tmp.abcdef1234 ~/foo` is still hard-blocked. Normal and Auto modes still ask, and children still block. The check runs when the command is admitted; it does not lock the path until `rm` runs.
|
|
54
|
+
|
|
43
55
|
## Lifetime and limits
|
|
44
56
|
|
|
45
57
|
Live permission is held in the extension instance, bound to the exact live SessionManager, session ID and canonical Git common-directory identity, or canonical workspace directory outside Git. Sibling worktrees in one clone may retain it; an unrelated workspace or session identity cannot. Scope loss revokes rather than hiding permission until you return.
|
|
@@ -50,7 +62,7 @@ This is permission guidance and shell-guard defense in depth, **not a sandbox or
|
|
|
50
62
|
|
|
51
63
|
## Recognized data-loss boundary
|
|
52
64
|
|
|
53
|
-
Recognized destructive database and broad filesystem commands require **fresh primary confirmation** and are **blocked in package-owned children**, independently of YOLO and existing autonomous-mode configuration.
|
|
65
|
+
Recognized destructive database and broad filesystem commands require **fresh primary confirmation** and are **blocked in package-owned children**, independently of YOLO and existing autonomous-mode configuration. The only YOLO exception is [temp scratch removal](#temp-scratch-removal).
|
|
54
66
|
|
|
55
67
|
| Recognized executable form | Primary session | Delegated child |
|
|
56
68
|
|---|---|---|
|
|
@@ -58,12 +70,13 @@ Recognized destructive database and broad filesystem commands require **fresh pr
|
|
|
58
70
|
| SQL `DELETE FROM` without `WHERE`, or a trailing literal `WHERE 1=1` / `WHERE TRUE` | Fresh confirmation | Block |
|
|
59
71
|
| Recursive `rm` (`-r`, `-R`, combined short flags, `--recursive`), `find -delete`, recognized destructive `find -exec/-execdir` and `xargs` invocations | Fresh confirmation | Block |
|
|
60
72
|
| Recursive `rm` targeting `/`, `/*`, `.`, `..`, home or home descendants | Hard block | Block |
|
|
73
|
+
| Temp scratch recursive removal ([conditions](#temp-scratch-removal)) | Waived under YOLO (primary only); fresh confirmation otherwise | Block |
|
|
61
74
|
| Git force push, hard reset, forced clean | Hard block | Block |
|
|
62
75
|
| Git forced branch deletion, reset/clean/restore/rebase, checkout with `--` or force, stash drop/clear | Existing primary policy remains | Block |
|
|
63
76
|
|
|
64
77
|
SQL recognition is limited to literal arguments passed to `psql`, `mysql`, `mariadb`, or `sqlite3`, literal `echo`/`printf` pipelines into those clients, and simple named heredocs. SQL string literals and comments are excluded from the new SQL recognition; printing or searching destructive prose alone does not trigger it.
|
|
65
78
|
|
|
66
|
-
Command recognition follows simple shell separators, pipelines and parenthesized command groups. It handles executable paths, leading assignments, common `env`, `sudo`, `command`, `exec`, `nohup`, `timeout` and `xargs` wrappers, and literal `sh/bash/zsh/dash -c` payloads (up to five recognition levels). Every recognized operation in a compound command participates; an earlier allowed delivery action cannot hide later data loss.
|
|
79
|
+
Command recognition follows simple shell separators, pipelines and parenthesized command groups. It handles executable paths, leading assignments, common `env`, `sudo`, `command`, `exec`, `nohup`, `timeout` and `xargs` wrappers, and literal `sh/bash/zsh/dash -c` payloads (up to five recognition levels). A command after the unquoted reserved words `then`, `else`, `elif`, `do`, `{`, `!`, `if`, `while` and `until` is recognized like a command at the start of a segment, so `if true; then rm -rf data; fi` and `for d in a b; do rm -rf "$d"; done` are guarded like `rm -rf data`, and a root or home target behind them is still hard-blocked. Backtick substitutions outside single quotes (also inside double quotes) and literal `eval` payloads, quoted or bare words, are recognized like literal `sh -c` payloads within the same recognition levels; `eval "$x"` and other expanded payloads are not resolved. A recursive `rm` behind a reserved word, in backticks or in `eval` never qualifies for [temp scratch removal](#temp-scratch-removal). Every recognized operation in a compound command participates; an earlier allowed delivery action cannot hide later data loss.
|
|
67
80
|
|
|
68
81
|
Ordinary builds, tests, read-only SQL, predicate-bounded literal deletes, Git status/log and plain pushes are unchanged by the shared data-loss classifier. A build command that explicitly includes recursive deletion is still guarded.
|
|
69
82
|
|
|
@@ -71,15 +84,15 @@ Ordinary builds, tests, read-only SQL, predicate-bounded literal deletes, Git st
|
|
|
71
84
|
|
|
72
85
|
1. Existing hard denies and newly recognized hard-deny forms win across the command.
|
|
73
86
|
2. Explicit configured blocks retain their existing semantics and win before data-loss confirmation. Active YOLO additionally honors explicit push restrictions in either configuration layer.
|
|
74
|
-
3. Recognized database/filesystem data loss always requires fresh confirmation, even when autonomous mode or a matched delivery action is configured to allow.
|
|
87
|
+
3. Recognized database/filesystem data loss always requires fresh confirmation, even when autonomous mode or a matched delivery action is configured to allow. The only exception is [temp scratch removal](#temp-scratch-removal) under active YOLO in the primary session.
|
|
75
88
|
4. No UI, cancellation, a non-true answer or a dialog error cannot authorize execution. Approval is not cached; permission and Herdr blocker lifecycle events remain balanced.
|
|
76
89
|
|
|
77
90
|
Children receive the lightweight `child-safety.ts` entry alongside `child-context.ts`; they do not need the full primary extension. The safety entry registers nothing outside `GENTLE_PI_AGENTS_CHILD=1`, preventing duplicate primary prompts during package auto-discovery. If the full primary extension is explicitly loaded in a child, its destructive-command path also blocks instead of prompting.
|
|
78
91
|
|
|
79
92
|
### Detection limitations
|
|
80
93
|
|
|
81
|
-
- This is deterministic lexical recognition, **not** shell or SQL evaluation. Aliases, functions, dynamically assembled commands, variable/command expansion inside quoted payloads, unusual wrapper flags, complex heredocs and deeply nested wrappers can evade recognition. Simple recursive removal of a variable target is guarded, but its expanded target cannot reliably be identified as a hard-deny path.
|
|
82
|
-
- Guarded-command confirmations and hard-denied patterns ignore text that only appears as an argument of a known data command (`grep`, `rg`, `cat`, `echo`, `printf`, `head`, `tail`, `jq`, `sort`, `uniq`, `wc` and similar), including the body of a real heredoc fed to one. Searching a file for a push or printing a reset command does not open a dialog. The exemption applies only when every command in the line is such a data command or an inert one (`cd`, `pwd`, `test`, `mkdir`, `touch`), optionally after plain `VAR=value` assignments or a flagless `env`. Anything else anywhere in the line keeps the full lexical match: any other command or wrapper (`sed`, `sh`, `python`, `awk`, `xargs`, `sudo`, `exec`, `at`, `./script`), command or process substitution, a `<<` that is not an unquoted heredoc, and quoting the tokenizer cannot account for. `sed` is excluded because GNU sed can execute its script.
|
|
94
|
+
- This is deterministic lexical recognition, **not** shell or SQL evaluation. Aliases, functions, other reserved words (`time`, `function`, `coproc`), commands inside double-quoted `$(...)`, dynamically assembled commands, variable/command expansion inside quoted payloads, unusual wrapper flags, complex heredocs and deeply nested wrappers can evade recognition. Simple recursive removal of a variable target is guarded, but its expanded target cannot reliably be identified as a hard-deny path.
|
|
95
|
+
- Guarded-command confirmations and hard-denied patterns ignore text that only appears as an argument of a known data command (`grep`, `rg`, `cat`, `echo`, `printf`, `head`, `tail`, `jq`, `sort`, `uniq`, `wc` and similar), including the body of a real heredoc fed to one. Searching a file for a push or printing a reset command does not open a dialog. The exemption applies only when every command in the line is such a data command or an inert one (`cd`, `pwd`, `test`, `mkdir`, `touch`), optionally after plain `VAR=value` assignments or a flagless `env`. Plain assignment segments (`f=x;`) and simple command substitutions whose body is itself only data or inert commands (`f=$(ls -t *.jsonl | head -40); grep -l "git push" $f`) keep the exemption. Anything else anywhere in the line keeps the full lexical match: any other command or wrapper (`sed`, `sh`, `python`, `awk`, `xargs`, `sudo`, `exec`, `at`, `./script`), a command substitution that runs anything else or nests quotes, expansions or another substitution, a command substitution together with a heredoc, process substitution, backticks, a `<<` that is not an unquoted heredoc, and quoting the tokenizer cannot account for. `sed` is excluded because GNU sed can execute its script.
|
|
83
96
|
- Scripts, migration files, redirected SQL files, arbitrary interpreter programs (Python/Node/etc.), encoded payloads and remote execution are not inspected. Non-recursive wildcard deletion, file truncation, other database clients and nontrivial tautological SQL predicates are not comprehensively classified. A `WHERE` clause is not proof of bounded impact.
|
|
84
97
|
- Only `bash` tool calls reach this data-loss boundary, including nested calls dispatched through Pi's tool-event pipeline. Direct process execution, user shell commands, MCP tools and other tools are not covered by these hooks.
|
|
85
98
|
- The older primary regex safeguards remain authoritative and may be more conservative about text than the shared recognizer. Missing or explicitly overridden child extension paths remove the lightweight boundary; a valid package installation must include the safety entry.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { execFile, execFileSync } from "node:child_process";
|
|
2
|
-
import { existsSync, lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, realpathSync, statSync } from "node:fs";
|
|
3
3
|
import { homedir, tmpdir } from "node:os";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
4
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
|
|
@@ -38,11 +38,18 @@ export interface CodeGraphRunOptions {
|
|
|
38
38
|
maxBuffer: number;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
interface CodeGraphLocalExclude {
|
|
42
|
+
status: "added" | "present" | "failed";
|
|
43
|
+
path?: string;
|
|
44
|
+
reason?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
interface CodeGraphFallbackDetails {
|
|
42
48
|
status: CodeGraphStatus;
|
|
43
49
|
operation: CodeGraphOperation;
|
|
44
50
|
cwd: string;
|
|
45
51
|
fallback: string;
|
|
52
|
+
localExclude?: CodeGraphLocalExclude;
|
|
46
53
|
}
|
|
47
54
|
|
|
48
55
|
export type CodeGraphRunner = (
|
|
@@ -109,6 +116,55 @@ function assertSafeIndexDirectory(cwd: string): void {
|
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
118
|
|
|
119
|
+
function indexExists(cwd: string): boolean {
|
|
120
|
+
try {
|
|
121
|
+
return lstatSync(join(cwd, ".codegraph")).isDirectory();
|
|
122
|
+
} catch {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function gitOutput(cwd: string, args: readonly string[]): string {
|
|
128
|
+
return execFileSync("git", [...args], {
|
|
129
|
+
cwd,
|
|
130
|
+
encoding: "utf8",
|
|
131
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
132
|
+
windowsHide: true,
|
|
133
|
+
}).trim();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// CodeGraph writes .codegraph/.gitignore with `*` and `!.gitignore`, so that file
|
|
137
|
+
// stays untracked and enters every diff. Keep the whole index out of git through
|
|
138
|
+
// the worktree's local exclude file; tracked files and .gitignore are never edited.
|
|
139
|
+
const LOCAL_EXCLUDE_PATTERN = ".codegraph/";
|
|
140
|
+
const EQUIVALENT_EXCLUDE_PATTERNS = new Set([".codegraph", ".codegraph/", "/.codegraph", "/.codegraph/"]);
|
|
141
|
+
|
|
142
|
+
function excludeIndexLocally(cwd: string): CodeGraphLocalExclude | undefined {
|
|
143
|
+
let path: string | undefined;
|
|
144
|
+
try {
|
|
145
|
+
if (gitOutput(cwd, ["rev-parse", "--is-inside-work-tree"]) !== "true") return undefined;
|
|
146
|
+
// --git-path resolves the exclude file a linked worktree actually reads.
|
|
147
|
+
path = resolve(cwd, gitOutput(cwd, ["rev-parse", "--git-path", "info/exclude"]));
|
|
148
|
+
const current = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
149
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
150
|
+
path = join(realpathSync(dirname(path)), basename(path));
|
|
151
|
+
if (current.split(/\r?\n/).some((line) => EQUIVALENT_EXCLUDE_PATTERNS.has(line.trim()))) {
|
|
152
|
+
return { status: "present", path };
|
|
153
|
+
}
|
|
154
|
+
const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
|
|
155
|
+
appendFileSync(path, `${separator}${LOCAL_EXCLUDE_PATTERN}\n`);
|
|
156
|
+
return { status: "added", path };
|
|
157
|
+
} catch (error: unknown) {
|
|
158
|
+
const reason = error instanceof Error ? error.message.split("\n")[0] : String(error);
|
|
159
|
+
return { status: "failed", ...(path ? { path } : {}), reason };
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function withLocalExcludeNote(text: string, localExclude: CodeGraphLocalExclude | undefined): string {
|
|
164
|
+
if (localExclude?.status !== "failed") return text;
|
|
165
|
+
return `${text}\n\n.codegraph/ is not excluded from git locally (${localExclude.reason}). Keep .codegraph/ out of the change.`;
|
|
166
|
+
}
|
|
167
|
+
|
|
112
168
|
function resolveLimit(limit: number | undefined): number {
|
|
113
169
|
if (limit === undefined) return DEFAULT_LIMIT;
|
|
114
170
|
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
@@ -304,7 +360,7 @@ export function createCodeGraphTool(
|
|
|
304
360
|
signal: AbortSignal | undefined,
|
|
305
361
|
_onUpdate: undefined,
|
|
306
362
|
ctx: ExtensionContext,
|
|
307
|
-
): Promise<{ content: { type: "text"; text: string }[]; details: { operation: CodeGraphOperation; cwd: string; args: string[] } | CodeGraphFallbackDetails }> {
|
|
363
|
+
): Promise<{ content: { type: "text"; text: string }[]; details: { operation: CodeGraphOperation; cwd: string; args: string[]; localExclude?: CodeGraphLocalExclude } | CodeGraphFallbackDetails }> {
|
|
308
364
|
const cwd = resolveWorkspaceCwd(ctx.cwd);
|
|
309
365
|
assertSafeIndexDirectory(cwd);
|
|
310
366
|
const args = commandArguments(parameters, cwd);
|
|
@@ -313,18 +369,24 @@ export function createCodeGraphTool(
|
|
|
313
369
|
try {
|
|
314
370
|
const result = await runner(args, { cwd, signal: runSignal, maxBuffer: PROCESS_MAX_BUFFER });
|
|
315
371
|
const output = truncateOutput([result.stdout, result.stderr].filter(Boolean).join("\n"));
|
|
372
|
+
const localExclude = parameters.operation === CODEGRAPH_OPERATION.INIT || indexExists(cwd)
|
|
373
|
+
? excludeIndexLocally(cwd)
|
|
374
|
+
: undefined;
|
|
316
375
|
return {
|
|
317
|
-
content: [{ type: "text" as const, text: output || "CodeGraph completed without output." }],
|
|
318
|
-
details: { operation: parameters.operation, cwd, args },
|
|
376
|
+
content: [{ type: "text" as const, text: withLocalExcludeNote(output || "CodeGraph completed without output.", localExclude) }],
|
|
377
|
+
details: { operation: parameters.operation, cwd, args, ...(localExclude ? { localExclude } : {}) },
|
|
319
378
|
};
|
|
320
379
|
} catch (error: unknown) {
|
|
321
380
|
const details = codeGraphFailureDetails(error, parameters.operation, cwd);
|
|
381
|
+
// A failed init can still leave a partial index behind.
|
|
382
|
+
const localExclude = indexExists(cwd) ? excludeIndexLocally(cwd) : undefined;
|
|
383
|
+
if (localExclude) details.localExclude = localExclude;
|
|
322
384
|
const timedOut = timeout?.aborted === true && signal?.aborted !== true;
|
|
323
385
|
const text = timedOut
|
|
324
386
|
? `CodeGraph timed out after ${queryTimeoutMs} ms (a slow filesystem such as a WSL /mnt mount, or a very large workspace). ${FALLBACK_INSTRUCTIONS}`
|
|
325
387
|
: codeGraphFailureMessage(details.status);
|
|
326
388
|
return {
|
|
327
|
-
content: [{ type: "text" as const, text }],
|
|
389
|
+
content: [{ type: "text" as const, text: withLocalExcludeNote(text, localExclude) }],
|
|
328
390
|
details,
|
|
329
391
|
};
|
|
330
392
|
}
|