Skip to content

Add factory runbook docs, npm scripts, and pear factory passthrough#322

Merged
khaliqgant merged 3 commits into
mainfrom
docs/factory-runbook-and-pear-passthrough
Jun 14, 2026
Merged

Add factory runbook docs, npm scripts, and pear factory passthrough#322
khaliqgant merged 3 commits into
mainfrom
docs/factory-runbook-and-pear-passthrough

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Jun 14, 2026

Copy link
Copy Markdown
Member

Why

The factory-sdk autonomous loop (#321) was built and validated, but had no operator-facing docs and no convenient entrypoint. The only way to run it was the verbose node packages/factory-sdk/bin/fleet.mjs factory … --config <cfg>, and pear factory … didn't work because pear is the Electron app launcher — a different binary from the factory's fleet CLI.

What

  • pear factory <action> … passthrough (bin/pear.mjs): intercepts the factory verb and forwards verbatim to the factory-sdk fleet CLI in a plain Node process. Deliberately does not launch Electron — the daemon/reaper are headless by design. Exit codes and signals propagate.
  • npm scripts (package.json): factory, factory:start, factory:run-once, factory:reap, factory:status.
  • README (packages/factory-sdk/README.md): quick start, full command table, the 2-process production model (daemon + same---config reaper coupling), the gh-auth precondition, a minimal config with annotated fields, and fixture mode.

All three invocations are now equivalent:

```bash
pear factory start --mode live --config ./factory.config.json
npm run factory:start -- --config ./factory.config.json
node packages/factory-sdk/bin/fleet.mjs factory start --mode live --config ./factory.config.json
```

Safety / scope

No behavior change. mergePolicy still defaults to never; the autonomous merge-flip remains pending per #321. This is purely ergonomics + docs.

Verification

  • node --check bin/pear.mjs passes.
  • pear factory run-once with no --config → fails fast with the expected config error (proves the passthrough reaches fleet's main).
  • pear factory run-once --config <fixture> --dry-run → runs a full offline cycle and prints an IterationReport.
  • npm run factory:status -- --config <fixture> → returns heartbeat liveness JSON.

🤖 Generated with Claude Code

Review in cubic

The factory-sdk autonomous loop (issue #321) had no operator-facing docs and
no convenient entrypoint — the only invocation was the verbose
`node packages/factory-sdk/bin/fleet.mjs factory … --config <cfg>`, and the
`pear` binary (an Electron launcher) had no `factory` verb.

- bin/pear.mjs: intercept `pear factory <action> …` and forward verbatim to
  the factory-sdk `fleet` CLI in a plain Node process. Deliberately does NOT
  launch Electron — the daemon/reaper are headless by design. Exit codes and
  signals propagate.
- package.json: add factory / factory:start / factory:run-once / factory:reap /
  factory:status npm scripts.
- packages/factory-sdk/README.md: quick start, command table, the 2-process
  production model (daemon + same-`--config` reaper coupling), gh-auth
  precondition, minimal config + annotated fields, and fixture mode.

No behavior change: mergePolicy still defaults to `never`; the autonomous
merge-flip remains pending per #321.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@khaliqgant, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 46 minutes and 11 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b3dd3cb9-db15-4810-aa9b-c089948e93a5

📥 Commits

Reviewing files that changed from the base of the PR and between a0d2c0a and 2413885.

📒 Files selected for processing (1)
  • packages/factory-sdk/README.md
📝 Walkthrough

Walkthrough

bin/pear.mjs gains a factory verb branch that validates and spawns packages/factory-sdk/bin/fleet.mjs via Node as a headless passthrough, while preserving the existing Electron launch path. Five convenience npm scripts invoking fleet.mjs subcommands are added to package.json. A new packages/factory-sdk/README.md documents the factory CLI. A mount-root invariant incident report is logged.

Changes

factory CLI passthrough and documentation

Layer / File(s) Summary
factory verb branch in pear launcher and npm scripts
bin/pear.mjs, package.json
bin/pear.mjs refactored to slice args once and branch on the factory verb: validates fleet.mjs exists and spawns it via Node, with signal-to-exit-code mapping and error handling shared with the Electron path. Five npm scripts (factory, factory:start, factory:run-once, factory:reap, factory:status) added to package.json.
factory-sdk README
packages/factory-sdk/README.md
New README documenting the factory CLI: invocation via pear factory, mandatory --config flag, quick-start examples, full command and global option listing, 2-process production model, operating preconditions, configuration fields and defaults, fixture/offline mode, headless design note, and esbuild cache behavior.

Mount-root invariant incident logging

Layer / File(s) Summary
Mount-root invariant incident report
memory/INCIDENT-20260614T081954Z.md
New incident report documenting a mount-root directory missing invariant violation, including timestamp, detected condition, reason (local root does not exist), and recovery steps requiring daemon restart acknowledgment via --reset-after-clobber flag.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • AgentWorkforce/pear#266: Changes the underlying fleet CLI logic (fixtureFiles handling and client selection) that this PR wires into the pear launcher.
  • AgentWorkforce/pear#253: Adds the factory reap-orphans action implementation that the new npm scripts invoke.

Suggested labels

no-agent-relay-review

Poem

🐰 A hop through the CLI, no Electron in sight,
pear factory sprints by day and by night.
fleet.mjs awakens with args passed along,
configs and fixtures keep the loop going strong.
No windows to open — just headless and bright! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title concisely summarizes all three main changes: factory runbook docs, npm scripts, and the pear factory passthrough CLI feature.
Description check ✅ Passed The description is well-related to the changeset, explaining why the changes were needed, what was added, and how it affects operators.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/factory-runbook-and-pear-passthrough

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 and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a headless passthrough for pear factory commands in bin/pear.mjs to execute the factory-sdk CLI directly in a plain Node process, bypassing Electron. It also adds corresponding helper scripts to package.json and comprehensive documentation in packages/factory-sdk/README.md. The reviewer suggested consolidating the child process spawning logic in bin/pear.mjs to eliminate duplicated event handlers for the spawned processes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread bin/pear.mjs
Comment on lines +34 to +78
if (args[0] === 'factory') {
const fleetBin = join(appRoot, 'packages', 'factory-sdk', 'bin', 'fleet.mjs')
if (!existsSync(fleetBin)) {
console.error(`factory-sdk CLI not found (missing ${fleetBin}).`)
process.exit(1)
}
const child = spawn(process.execPath, [fleetBin, ...args], { stdio: 'inherit' })
child.on('close', (code, signal) => {
if (signal) {
const signalNumber = typeof os.constants.signals[signal] === 'number' ? os.constants.signals[signal] : 0
process.exit(128 + signalNumber)
}
process.exit(code ?? 0)
})
child.on('error', (error) => {
console.error('Failed to launch factory CLI:', error instanceof Error ? error.message : String(error))
process.exit(1)
})
} else {
if (!existsSync(appMain)) {
console.error(`Pear is not built yet — run \`npm run build\` first (missing ${appMain}).`)
process.exit(1)
}

// In a Node context, requiring electron yields the path to its executable.
const electronPath = require('electron')
// In a Node context, requiring electron yields the path to its executable.
const electronPath = require('electron')

const child = spawn(electronPath, [appMain, ...process.argv.slice(2)], {
stdio: 'inherit'
})
const child = spawn(electronPath, [appMain, ...args], {
stdio: 'inherit'
})

child.on('close', (code, signal) => {
// A signal-terminated child reports code === null; surface that as a failure
// (128 + signal number, per shell convention) so calling scripts can detect it.
if (signal) {
const signalNumber = typeof os.constants.signals[signal] === 'number' ? os.constants.signals[signal] : 0
process.exit(128 + signalNumber)
}
process.exit(code ?? 0)
})
child.on('error', (error) => {
console.error('Failed to launch Pear:', error instanceof Error ? error.message : String(error))
process.exit(1)
})
child.on('close', (code, signal) => {
// A signal-terminated child reports code === null; surface that as a failure
// (128 + signal number, per shell convention) so calling scripts can detect it.
if (signal) {
const signalNumber = typeof os.constants.signals[signal] === 'number' ? os.constants.signals[signal] : 0
process.exit(128 + signalNumber)
}
process.exit(code ?? 0)
})
child.on('error', (error) => {
console.error('Failed to launch Pear:', error instanceof Error ? error.message : String(error))
process.exit(1)
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The event handlers for the spawned child process (close and error) are duplicated between the factory passthrough branch and the standard Electron launch branch. We can consolidate the spawning logic by determining the target binary and arguments first, and then registering the event handlers once on the spawned child process. This significantly reduces code duplication and improves maintainability.

const isFactory = args[0] === 'factory'
let child

if (isFactory) {
  const fleetBin = join(appRoot, 'packages', 'factory-sdk', 'bin', 'fleet.mjs')
  if (!existsSync(fleetBin)) {
    console.error(`factory-sdk CLI not found (missing ${fleetBin}).`)
    process.exit(1)
  }
  child = spawn(process.execPath, [fleetBin, ...args], { stdio: 'inherit' })
} else {
  if (!existsSync(appMain)) {
    console.error(`Pear is not built yet — run \`npm run build\` first (missing ${appMain}).`)
    process.exit(1)
  }

  // In a Node context, requiring electron yields the path to its executable.
  const electronPath = require('electron')
  child = spawn(electronPath, [appMain, ...args], { stdio: 'inherit' })
}

child.on('close', (code, signal) => {
  // A signal-terminated child reports code === null; surface that as a failure
  // (128 + signal number, per shell convention) so calling scripts can detect it.
  if (signal) {
    const signalNumber = typeof os.constants.signals[signal] === 'number' ? os.constants.signals[signal] : 0
    process.exit(128 + signalNumber)
  }
  process.exit(code ?? 0)
})
child.on('error', (error) => {
  console.error(`Failed to launch ${isFactory ? 'factory CLI' : 'Pear'}:`, error instanceof Error ? error.message : String(error))
  process.exit(1)
})

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Open in Devin Review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2620175371

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread bin/pear.mjs
// factory daemon/reaper run in a plain Node process. Everything after `factory`
// is forwarded verbatim, so `pear factory start --mode live --config <cfg>`
// behaves identically to `node packages/factory-sdk/bin/fleet.mjs factory …`.
if (args[0] === 'factory') {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route the installed pear CLI through this passthrough

This branch only runs when bin/pear.mjs is the entrypoint, but the macOS user-installed pear command is generated in src/main/cli-install.ts as a shell shim that execs app.getPath('exe'), so pear factory ... from the packaged app still launches Electron instead of this Node launcher. In that installed-CLI context the main process only scans argv for open in src/main/cli.ts, so the documented production pear factory start/reap-orphans commands won't reach fleet; the installer needs to invoke this passthrough or Electron needs equivalent factory handling before advertising pear factory.

Useful? React with 👍 / 👎.

agent-relay-code Bot added a commit that referenced this pull request Jun 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@package.json`:
- Line 27: The npm script `factory:status` in package.json invokes `factory
loop-status`, but the README documents `status` and `loop-status` as distinct
commands, creating confusion between the script name and its actual behavior.
Either rename the `factory:status` script to `factory:loop-status` to accurately
reflect what it executes, or change the script to invoke `factory status`
instead to align with the documented command semantics. Choose whichever option
matches your intended command architecture.

In `@packages/factory-sdk/README.md`:
- Line 75: The documentation line at packages/factory-sdk/README.md line 75 uses
`node bin/fleet.mjs` without specifying the working directory context, while
earlier examples in the README use the full path `node
packages/factory-sdk/bin/fleet.mjs`. To prevent copy/paste errors and confusion,
either explicitly clarify the working directory requirement (e.g., "from the
factory-sdk package directory") for the relative path example, or normalize all
examples throughout the README to use a consistent path style (either all
relative or all absolute paths) with clear context about where commands should
be executed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: fe103d5a-c5f5-4013-b17d-ab93c446fda1

📥 Commits

Reviewing files that changed from the base of the PR and between cc346eb and 2620175.

📒 Files selected for processing (3)
  • bin/pear.mjs
  • package.json
  • packages/factory-sdk/README.md

Comment thread package.json
"factory:start": "node packages/factory-sdk/bin/fleet.mjs factory start --mode live",
"factory:run-once": "node packages/factory-sdk/bin/fleet.mjs factory run-once",
"factory:reap": "node packages/factory-sdk/bin/fleet.mjs factory reap-orphans",
"factory:status": "node packages/factory-sdk/bin/fleet.mjs factory loop-status",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align factory:status with documented command semantics.

factory:status currently invokes factory loop-status, while the README defines status and loop-status as different commands. Either rename this script to factory:loop-status or point it to factory status to avoid operator confusion.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 27, The npm script `factory:status` in package.json
invokes `factory loop-status`, but the README documents `status` and
`loop-status` as distinct commands, creating confusion between the script name
and its actual behavior. Either rename the `factory:status` script to
`factory:loop-status` to accurately reflect what it executes, or change the
script to invoke `factory status` instead to align with the documented command
semantics. Choose whichever option matches your intended command architecture.

Comment thread packages/factory-sdk/README.md
…lity

Lead with the no-install forms (`npm run factory:*` / `node bin/fleet.mjs`),
then document `npm link` as the standard way operators get the `pear` command —
it is the normal local-Node-CLI install, not a workaround.

Clarify that the packaged .app's "Install 'pear' command in PATH" installs the
GUI launcher for `pear open` only; the factory-sdk is not bundled into the app,
so `pear factory …` is operator-from-repo only. Avoids overstating that
`pear factory` works out of the box.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@khaliqgant khaliqgant force-pushed the docs/factory-runbook-and-pear-passthrough branch from a0d2c0a to 824302a Compare June 14, 2026 08:26
@khaliqgant khaliqgant added the no-agent-relay-review Disable agent-relay automated PR review/fixes label Jun 14, 2026
agent-relay-code Bot added a commit that referenced this pull request Jun 14, 2026
@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented the PR-scope fix in bin/pear.mjs: the factory and Electron branches now choose the child process target first, then share the close and error handlers at bin/pear.mjs. This keeps pear factory ... forwarding factory verbatim to fleet.mjs.

Addressed comments

  • gemini-code-assist[bot] inline on bin/pear.mjs: requested consolidating duplicated child-process event handlers. Fixed in bin/pear.mjs and bin/pear.mjs.
  • gemini-code-assist[bot] review summary: same duplicated handler concern. Fixed in bin/pear.mjs.
  • coderabbitai[bot]: only an auto-generated “review in progress” comment was present when fetched; no actionable finding to change. Refreshed commit statuses later showed CodeRabbit success.

Advisory Notes

  • Lint reports existing warnings outside this PR’s changed files; I left them unchanged as out of scope.
  • I did not run the macOS-only dist:mac/packaged smoke job locally because this sandbox is Linux.

Verification run:
npm ci; npm run verify:mcp-resources-drift; npm run lint; npm run typecheck:web; npm run typecheck:node; npm test; npx vitest run; npm run build; npm run build:web; npx playwright install --with-deps chromium; npx playwright test --config playwright.fidelity.config.ts; npx playwright test --config playwright.redraw.config.ts; plus fixture checks for node bin/pear.mjs factory ... and npm run factory:run-once ....

I’m not printing READY because the local edit still needs the post-harness push/check cycle before GitHub can report CI on the edited commit.

The config section only had a 4-line minimal example + a field list. Add:
- a complete annotated config (subscription, repo routing, safety, models, loop)
- where each value comes from (workspaceId, clonePaths, stateIds + the AR-team
  default UUIDs in src/constants/linear.ts)
- the repo-routing precedence (byLabel → byProject → keywordRules → default)
- the safety gate explained — title `[factory-e2e]` prefix AND team key AR — so
  it's clear why run-once can pull issues yet dispatch none

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@khaliqgant khaliqgant force-pushed the docs/factory-runbook-and-pear-passthrough branch from 4ac8b15 to 2413885 Compare June 14, 2026 08:29
@khaliqgant khaliqgant merged commit 34d1958 into main Jun 14, 2026
4 checks passed
@khaliqgant khaliqgant deleted the docs/factory-runbook-and-pear-passthrough branch June 14, 2026 08:32
@agent-relay-code

Copy link
Copy Markdown
Contributor

Implemented fixes for the PR review findings.

Changed:

  • Installed CLI shim now routes pear factory ... to packaged fleet.mjs headlessly via ELECTRON_RUN_AS_NODE=1, while leaving normal app commands on the Electron executable.
  • Electron packaging now includes and unpacks packages/factory-sdk so the installed shim has a real target.
  • factory:status now calls factory status; added factory:loop-status for heartbeat liveness.
  • README now uses the repository-root node packages/factory-sdk/bin/fleet.mjs path consistently.
  • Added Node tests for the generated CLI shim behavior.

Addressed comments

  • coderabbitai[bot] walkthrough/no actionable recent review: informational only, no code change needed.
  • devin-ai-integration[bot] “No Issues Found”: informational only, no code change needed.
  • chatgpt-codex-connector[bot] raised that packaged installed pear factory ... would still launch Electron instead of fleet: fixed in src/main/cli-shim.ts:14, wired from src/main/cli-install.ts:32, packaged in electron-builder.yml:13.
  • chatgpt-codex-connector[bot] review summary: covered by the inline installed-CLI routing fix above.
  • coderabbitai[bot] raised factory:status invoking loop-status: fixed in package.json:27; explicit factory:loop-status added at package.json:28.
  • coderabbitai[bot] raised ambiguous README node bin/fleet.mjs cwd: fixed in packages/factory-sdk/README.md:75.
  • coderabbitai[bot] review summary listing two actionable comments: both handled by the two CodeRabbit bullets above.

Advisory Notes

None.

Local validation run:

  • npm ci
  • npm run verify:mcp-resources-drift && npm run lint && npm run typecheck:web && npm run typecheck:node && npm test && npx vitest run && npm run build
  • npm run build:web
  • npx playwright install --with-deps chromium
  • npx playwright test --config playwright.fidelity.config.ts
  • npx playwright test --config playwright.redraw.config.ts
  • Generated-shim throwaway execution for normal and factory paths
  • npx electron-builder --dir --linux --publish never
  • Packaged-headless smoke: ELECTRON_RUN_AS_NODE=1 dist/linux-unpacked/pear-by-agent-relay .../fleet.mjs factory status --config packages/factory-sdk/test/fixtures/factory.config.json

I did not run the macOS dist:mac job in this Linux environment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-agent-relay-review Disable agent-relay automated PR review/fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant