Skip to content

feat(launcher): export the injected package set for delegated children - #1771

Merged
barbatdev merged 9 commits into
Gentleman-Programming:mainfrom
matraket:fix/1690-launcher-injection-signal
Oct 5, 2026
Merged

barbatdev merged 9 commits into
Gentleman-Programming:mainfrom
matraket:fix/1690-launcher-injection-signal

Conversation

@matraket

@matraket matraket commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • The launcher injects gentle-pi into the parent with pi -e <packageRoot> (isolated home, or a takeover), but never says so, so the subagent runner cannot give children the same package.
  • buildPiInvocation now exports GENTLE_SHELL_CHILD_PACKAGE_INJECTION, JSON {version: 1, noExtensions, extensionPaths}:
    • takeover: the exact deduped -e set, with noExtensions: true;
    • no gentle-pi in settings.json: [packageRoot];
    • a declared gentle-pi, or a pi subcommand: no value, and an inherited one is removed.
  • Passthrough -e (the managed herdr extension, or one the user typed) is not part of the signal.
  • Paths are absolute, resolved against an explicit cwd (the bin passes its own, which the spawn uses), and deduped after that.
  • The wire format lives once in lib/child-package-injection.ts (encoder, a parser that never throws, the child argv helper) and is generated into runtime/.

This is PR 2 of 4. Nothing consumes the signal yet; #1772 does.

Issue

Refs #1690

PR type

  • New feature (type:feature)

Changes

Commit Change
ac5ea634 lib/child-package-injection.ts, the signal in buildPiInvocation, the generated runtime, registration in build-runtime-modules and verify-package-files, tests at lib and bin level.
f493b3af Explicit cwd instead of process.cwd(); dedupe the signal after making paths absolute.
1030d68d Passes cwd in the upstream bin fixture.

Test plan

All four branches were verified on top of main (653dad90) with env -u GENTLE_PI_AGENTS_CHILD (delegated shells export it).

  • Focused suites: 584 pass, 0 fail (launcher, injection module, bin, package manifest, and the PR 1 set).
  • check-types, build-runtime-modules --check, verify-package-files: pass.
  • Test-first: the bin-level test fails with the assignment disabled.
  • Native review (RDD): every commit approved, no corrections.

Known follow-ups (non-blocking, from review)

  • No bin-level test pins that the bin passes its own cwd and the spawn sets none.
  • The no-declaration branch is not tested with a relative packageRoot.

Chain Context

Field Value
Chain Standalone subagents load the gentle-pi package (#1690)
Tracker PR Not needed
Position 2 of 4
Base main (each PR is opened against main; until its predecessors merge, its diff also shows their commits, and I rebase it as they land)
Depends on #1770
Follow-up #1772
Review budget 461 changed lines (+436/-25), of which about 110 are the generated runtime/*.mjs copies and about 230 are tests.
main
 └─ #1770 child guards
   └─ #1771 launcher injection signal   📍 this PR
     └─ #1772 package forwarding + missing-tools warning
       └─ #1773 tests + docs

Review only the commits listed under Changes; earlier commits belong to the PRs below it in the chain.

Summary by CodeRabbit

  • New Features
    • Standalone child agents now receive the required extension configuration when launched, including support for custom extension paths.
  • Behavior Improvements
    • Delegated child sessions skip parent-only setup, such as repository preparation, history capture, registry startup, and startup banners. Development-binary overrides remain available.
  • Tests
    • Added coverage for extension forwarding and child-session behavior.

Adrian Cester Trallero added 9 commits October 4, 2026 11:46
Guard gentle-ai session_start side effects, repository preparation on
tool_result, skill-registry startup, history capture, the pi-pretty
fallback and the startup banner when GENTLE_PI_AGENTS_CHILD=1, so a
child that loads the full package does not rewrite shared state.

Refs Gentleman-Programming#1690
Move the parent-owned session_start steps into startParentSession so a
delegated child skips only that work, and assert that the child-local
resets (elapsed-timing ledger, reminder re-arm) still run.

Refs Gentleman-Programming#1690
Await the tool_execution_start handlers before counting ledger entries,
and assert that a restarted child clears its YOLO indicator and re-arms
the review sidebar for its own session.

Refs Gentleman-Programming#1690
Upstream expects a child session to surface the dev-binary warning
fallback. Extract the notice from the parent-only startup work so
children still run it while skipping the rest.

Refs Gentleman-Programming#1690
buildPiInvocation now sets GENTLE_SHELL_CHILD_PACKAGE_INJECTION (JSON v1)
with the exact -e set it injects: the full takeover set with
noExtensions, or the package root when settings declare no gentle-pi.
Declared and pi-subcommand launches drop any inherited value. The wire
format lives in lib/child-package-injection.ts, built into the runtime.

Refs Gentleman-Programming#1690
… cwd

buildPiInvocation takes the cwd the spawn uses instead of reading
process.cwd(), and dedupes the exported signal after making paths
absolute, so one file spelled two ways appears once. The -e argv is
unchanged.

Refs Gentleman-Programming#1690
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 38c0f374-49f9-400b-a083-7a9d333acbb5
📥 Commits

Reviewing files that changed from the base of the PR and between 653dad9 and 1030d68.

📒 Files selected for processing (21)
  • bin/gentle-shell.mjs
  • extensions/gentle-ai.ts
  • extensions/history/index.ts
  • extensions/pi-pretty.ts
  • extensions/skill-registry.ts
  • extensions/startup-banner.ts
  • lib/child-package-injection.ts
  • lib/gentle-shell-launcher.ts
  • odd/tasks/fix-1690-standalone-child-package.md
  • runtime/child-package-injection.mjs
  • runtime/gentle-shell-launcher.mjs
  • scripts/build-runtime-modules.mjs
  • scripts/verify-package-files.mjs
  • tests/child-package-injection.test.ts
  • tests/gentle-ai-child-guards.test.ts
  • tests/gentle-shell-bin.test.ts
  • tests/gentle-shell-launcher.test.ts
  • tests/history-child-guard.test.ts
  • tests/pi-pretty.test.ts
  • tests/skill-registry.test.ts
  • tests/startup-banner.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 2 remain after this review.


📝 Walkthrough

Walkthrough

The launcher now provides child-package injection metadata for selected invocation cases. Extensions also use the child-session flag to skip selected parent-session initialization, history capture, banner setup, and repository preparation.

Changes

Child Session Behavior

Layer / File(s) Summary
Package-injection signal and launcher wiring
lib/child-package-injection.ts, runtime/child-package-injection.mjs, lib/gentle-shell-launcher.ts, runtime/gentle-shell-launcher.mjs, bin/gentle-shell.mjs, scripts/*, tests/child-package-injection.test.ts, tests/gentle-shell-launcher.test.ts, tests/gentle-shell-bin.test.ts, odd/tasks/fix-1690-standalone-child-package.md
The launcher encodes a versioned signal for selected extension-injection cases. Takeover paths are made absolute and deduplicated; the no-declaration case signals the package root. Other invocation cases remove an inherited signal. Helpers validate and encode the signal, and tests cover payloads, paths, and launcher behavior. The ODD records implementation status, constraints, and follow-ups.
Gentle AI parent-session work
extensions/gentle-ai.ts, tests/gentle-ai-child-guards.test.ts
Child sessions skip parent startup work and bound-repository preparation after writes. Both startup paths check dev-binary overrides. Tests cover child and parent startup behavior, session restart state, write handling, and confirmation relay.
Other child-session extension guards
extensions/history/index.ts, extensions/pi-pretty.ts, extensions/skill-registry.ts, extensions/startup-banner.ts, tests/history-child-guard.test.ts, tests/pi-pretty.test.ts, tests/skill-registry.test.ts, tests/startup-banner.test.ts
The child-session flag now gates history capture, pi-pretty setup, skill-registry startup, and startup-banner initialization. Tests verify the child guards and, where included, parent behavior.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Suggested reviewers: alan-thegentleman

Merge Risk: ⚪ Minimal · up to 1030d

The package-injection signal and child-session guards are ready to merge after normal checks. Runner-side forwarding remains a documented later task, not part of this change.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 1030d

The launcher exports paths it already selected for the parent, and the child-session guards preserve mutation tracking and command restrictions while avoiding parent-owned work. No introduced security defect was established. Risk remains nonminimal because the actual delegated-child consumer and its execution controls could not be verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is the launched Pi process and its inherited environment, with extension paths already selected for parent loading. If a downstream child consumes the payload, it can request execution of those extensions and disable ordinary discovery. The consumer's privileges, sandbox, credentials, and maximum delegated exposure were not evidenced.

Trust Boundaries and Controls

  • observed — The launcher overwrites or removes inherited package metadata rather than trusting it as the source of its own injection set. The parser performs syntactic validation only; an absolute path is not proof of trusted executable origin. No attacker-to-consumer execution path was established.
  • observed — Skipping parent startup does not skip the inspected tool controls. Sensitive-path evaluation remains ahead of tool execution, and child bash commands pass through the child destructive-command guard before the confirmation path.

Resilience and Maintainability Implications

  • observed — Successful-write mutation recording occurs before awaiting preparation and deduplicates receipts by source, task, and tool-call identity. Child writes retain this evidence while avoiding parent repository preparation. Session shutdown invalidates the manager and preparation binding; startup interruption during awaited parent work was not directly exercised by the supplied tests.

Hardening Proposals

  • proposed — When integrating the delegated-child consumer, preserve trusted-parent provenance for the package set and document the child's effective loading authority. Do not treat absolute-path validation or the child-session flag as authorization, and define behavior for absent or unsupported payload versions.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 20 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: exporting the launcher’s injected package set for delegated children.
Full details: Docstring Coverage

Explanation

Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 20 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@carlosmoradev carlosmoradev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The contract in lib/child-package-injection.ts is solid.

Key highlights:

  1. Explicit wire contract: Exporting {version: 1, noExtensions, extensionPaths} via an explicit environment signal avoids guessing or fragile argv inspection downstream in the runner.
  2. Path normalization: Resolving relative paths against an explicit cwd before deduplicating ensures that child processes in subdirectories resolve the correct package root without path drift.
  3. Safe degradation: The parser handles malformed or unknown-version signals fail-closed without throwing.

Solid step 2 of 4.

@barbatdev barbatdev left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved. The signal contract is sound: all four branches set or explicitly delete the env (so an inherited value never leaks into a subcommand or declared home), passthrough -e stays out of the signal while argv keeps it, paths are absolutized against the spawn's actual cwd and deduped after, and the parser never throws while gating on version === 1. The generated runtime copy is mechanically reconciled and verify-package-files holds.

Focused suites pass on my side too (145/145 across the injection, bin and child-guard files, typecheck at the 186-diagnostic baseline).

Non-blocking notes, mostly for #1772 since it becomes the first consumer:

  • The parser rejects extensionPaths: [], so a semantically valid noExtensions: true with zero paths cannot round-trip today. Unreachable from the launcher, but the first consumer will have to respect it.
  • The encoder does not validate, so it can emit values the parser rejects (a relative path, an empty list). No test pins that asymmetry.
  • Version mismatch degrades silently to undefined. Safe, but worth one line of policy somewhere: bump means new key, old keys stay parseable for one cycle, or similar.
  • Bin-level e2e asserts the signal only for the no-declaration branch; the takeover assertion would pin the branch this stack actually exercises in production.

@barbatdev
barbatdev merged commit 7dcb3b4 into Gentleman-Programming:main Oct 5, 2026
6 checks passed
@matraket
matraket deleted the fix/1690-launcher-injection-signal branch October 5, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants