7.6 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 413 | Explicitly allow some external directories relative to the home directory |
Retro: #413 — Explicitly allow some external directories relative to the home directory
Stage: Planning (2026-06-16T15:06:55Z)
Session summary
Investigated a third-party request (filed by michaelmior) to allow outside-CWD directories like ~/.cargo/registry without prompting.
Found the capability already exists via the external_directory surface pattern map; the user's path-surface attempt failed because of most-restrictive-wins composition and a missing trailing *.
After confirming direction with the operator, wrote a docs-only plan to make the external_directory allow-list discoverable across configuration.md, README.md, config.example.json, and the schema.
Observations
- This is fundamentally a discoverability bug, not a missing feature.
"external_directory": { "*": "ask", "~/.cargo/registry/*": "allow" }already does what the user wants. - Rejected the tempting "make a
pathallow suppress theexternal_directorygate" approach. The four layers compose with most-restrictive-wins, so apathallow loosening anexternal_directory: askboundary would be a security regression, not a fix. The operator's instinct (keepexternal_directoryas a separate, intentional layer modeled on OpenCode) confirmed this. - Operator correction on wildcard semantics: do not use
**in examples. A single*compiles to a greedy.*(with thesflag inwildcard-matcher.ts) and already crosses subdirectory boundaries, so~/.cargo/registry/*matches every file beneath the directory. - Scope trimmed by operator:
configuration.md+README.md+config.example.json/schema;troubleshooting.mddeselected. - Next stage is
/build-plan(docs-only), not/tdd-plan. - Alignment constraint applies:
configuration.md,README.md,config.example.json, andschemas/permissions.schema.jsonmust agree on theexternal_directorypattern-map form.
Stage: Implementation — Build (2026-06-16T15:30:00Z)
Session summary
Executed all three docs-only build steps from the plan, one commit each: configuration.md (cache-dir recipe, "which surface?"
clarification, single-* note, most-restrictive-wins reaffirmation), README.md (clear external_directory surface description + pattern-map example), and config/config.example.json + schemas/permissions.schema.json (inline ~/.cargo/registry/* allow example, aligned across both).
No src/ or test/ files were touched, so the test/typecheck suites were not required; the package lint passes and both JSON files parse.
Observations
- The schema
markdownDescriptionis a single physical JSON line with literal\nescape sequences, not real newlines. AnEditoldTextbuilt with actual newlines failed to match; matching the literal\n\n(and escaping the embedded JSON quotes as\\\") was required. - Operator mid-build question confirmed
jsoncis the correct doc fence language: the docs use```jsonc21 times and```jsonzero times, and the loader runsJSON.parse(stripJsonComments(raw))(config-loader.ts:388), so//comments are supported (trailing commas are not). The config file itself stays.jsonand comment-free. - No deviations from the plan.
The optional
piInfrastructureReadPathscross-reference (Open Question) was included as a single sentence inconfiguration.mdonly, as the plan leaned. - Pre-completion reviewer: PASS — ready for
/ship-issue. No WARN findings.
Stage: Final Retrospective (2026-06-16T16:00:00Z)
Session summary
Shipped a docs-only fix for a third-party discoverability issue: the external_directory allow-list (e.g. ~/.cargo/registry/*) already does what the user wanted, so the work documented it across configuration.md, README.md, config.example.json, and the schema rather than changing behavior.
The session ran clean end to end (plan → build → ship → release pi-permission-system v13.1.2), with the only notable inflection at planning: an initial mis-framing of the design that the operator's redirect and a most-restrictive-wins re-analysis corrected before any code was written.
Observations
What went well
- The
ask_usergate on a third-party issue earned its keep. It surfaced the design direction before implementation and prevented building the tempting-but-wrong "make apathallow suppressexternal_directory" change, which most-restrictive-wins makes a silent access-widening regression. - Incremental verification throughout build:
lintran after each of the three doc steps, and both JSON files wereJSON.parse-checked after the schema/example edit — no end-only verification gap. - Operator mid-build spot-check ("
jsoncorjson?") was answered by verifying the convention (21```jsoncvs 0```json) and the loader (JSON.parse(stripJsonComments(raw))), not by assertion — and the answer held.
What caused friction (agent side)
missing-context— the first planningask_userpresented "make apathallow authoritative (suppressexternal_directory)" as a near-recommended fix, framed as correcting an "asymmetry," without first tracing the documented most-restrictive-wins composition whereask>allow. The operator redirected to discuss the interaction; a re-analysis then showed the option was a security regression. Self-corrected after the operator's nudge. Impact: one extra discussion round at planning; no rework (caught before any code), and the outcome improved.other(tool-usage) — two consecutiveEditcalls onconfiguration.md/schemas/permissions.schema.jsonwere rejected: first for a straynewText_unused/newText2_unusedkey in theeditsobject, then because theoldTextfor the schemamarkdownDescriptionused real newlines while the file stores literal\nescapes inside a single-line JSON string. Impact: ~2 retries, no rework.
What caused friction (user side)
- None blocking.
The operator's two interventions (planning redirect,
jsoncspot-check) were timely and strategic, and both improved the result. The only forward-looking opportunity: the most-restrictive-wins composition invariant lives indocs/configuration.mdbut not in the package skill, so it was not front-of-mind when I drafted the firstask_user— see the proposed skill addition.
Diagnostic details
- Model-performance correlation — one subagent dispatch (
pre-completion-reviewer) ran onanthropic/claude-sonnet-4-6for judgment-heavy doc/consistency review; appropriate, no mismatch. - Escalation-delay tracking — no
rabbit-hole; the longest stuck sequence was the two rejectedEditcalls, resolved on the third attempt (under the 5-call threshold). - Unused-tool detection — none;
grep/Readcovered the planning context, and nomissing-contextpoint would have been closed by an unused agent or tool (the gap was an undocumented invariant, not unsearched code). - Feedback-loop gap analysis —
lintran after every build step and JSON parsing after the schema edit; no deferred-verification gap.check/testwere correctly skipped (nosrc//test/changes).
Changes made
.pi/skills/package-pi-permission-system/SKILL.md— added a most-restrictive-wins cross-surface composition invariant after thelast-match-winsbullet (Implementation Priorities), stating that a more-permissive rule on one surface cannot loosen a more-restrictive rule on another and that outside-CWD directories belong onexternal_directory, notpath.- Proposal 2 (JSON-escape
Editnote inAGENTS.md) was considered and declined by the operator — not landed.