Skip to content

[factory] The babysitter writes to GitHub as the local gh user while Factory's own PRs are authored by the app #221

Description

@khaliqgant

Repo: factory · recipe: agent:single

The defect

Factory has two GitHub write paths with two different identities, and only one of them is deliberate.

PR creation goes through the app. src/mount/relayfile-github-connection-write.ts:92 writes author: 'app', and the comment at :107 says so explicitly:

"The workspace adapter contract explicitly requests app authorship. The concrete bot login is installation-specific and is not included in every acknowledgement receipt, so retain the stable identity label."

The babysitter shells out to gh. src/github/standalone-babysitter.ts:28 and :99. Its own target type admits it: source: 'mount' | 'gh' | 'mount+gh'. Same for src/github/merge-gate.ts:178 and src/intake/notion.ts:784.

gh authenticates as whatever local user is logged in — here a gho_ OAuth token for a human account. So every comment, commit and label the babysitter writes is attributed to a person, while Factory's own PRs are attributed to the app. One product, two audit trails, and the difference is invisible from the outside until you read the source.

This matters more now than it did yesterday: the babysitter is about to run against a large number of PRs that Factory did not create.

Why the shell-out is wrong beyond the identity

Standing rule from the principal: always use the SDK surface; do not shell out to another product's CLI. Shelling out:

  • makes a CLI binary a runtime dependency of every host Factory runs on;
  • couples two release trains across a process boundary with no type checking — relayfile mount recently rejected --workspace and --local-dir, flags its own help text advertised;
  • launders errors through exit codes and stdout parsing rather than typed results;
  • silently inherits whatever identity happens to be logged in, which is exactly the bug above.

The connection Factory needs already exists and is already used for PR creation.

What done looks like

  1. Babysitter GitHub writes go through the Relayfile GitHub connection, with author: 'app', matching PR creation. Same for merge-gate.ts and the gh call in intake/notion.ts.
  2. source: 'gh' stops being a supported provenance for babysitter targets, or its meaning is documented and deliberately retained with a reason.
  3. Identity is asserted, not assumed. A test that proves a babysitter comment is authored by the app and not by whoever ran the process. Do not accept "it worked" as evidence — read back the author on a real write.
  4. If any path genuinely cannot go through the connection today, say so explicitly with the reason rather than leaving a silent gh fallback. A documented limitation beats an invisible one.

Constraints

  • Do not merge. The merge gate belongs to the principal.
  • Do not change PR-creation authorship; it is already correct.
  • Report validation by exit code, not by output — a check that cannot fail is not a check.

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:singleFactory recipe: single agentfactoryFactory automation trigger

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions