feat(bot): PAT override + artifact sandbox + secret-exfil hardening - #104
Conversation
- Add GITHUB_PERSONAL_ACCESS_TOKEN config + assertPatRequiresAllowlist
guard; resolveGithubToken() short-circuits to PAT when set, falls back
to App installation token. Single-tenant only.
- Move agent summary files (IMPLEMENT.md/REVIEW.md/RESOLVE.md) to a
sibling \${workDir}-artifacts directory exposed via BOT_ARTIFACT_DIR;
prompts updated; readCapturedFiles + cleanup follow.
- Tighten bot:implement Mermaid rule: required for behaviour/flow
changes, only omit for pure refactors/typo/test-only PRs.
- Remove tracked drift IMPLEMENT.md/REVIEW.md/RESOLVE.md from repo root.
- Tests for resolveGithubToken, buildProviderEnv, assertPatRequiresAllowlist.
- Docs: CLAUDE.md, docs/build/architecture.md, docs/operate/configuration.md.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the 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 have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (30)
📝 WalkthroughWalkthroughThis PR introduces two independent architectural improvements: (1) externalize workflow-generated reports (IMPLEMENT.md, RESOLVE.md, REVIEW.md) into a sibling ChangesArtifacts Directory & Workflow Report Isolation
GitHub PAT Authentication & Token Resolution
Sequence Diagram(s)sequenceDiagram
participant Pipeline as Pipeline<br/>(runPipeline)
participant Ctx as Context<br/>(octokit, config)
participant Resolver as resolveGithubToken<br/>(core/github-token.ts)
participant SDK as Claude SDK<br/>(executor)
participant Subprocess as Agent Subprocess<br/>(Claude Code CLI)
participant Artifacts as Artifacts Dir<br/>(${workDir}-artifacts)
Pipeline->>Resolver: resolveGithubToken(octokit)
alt PAT configured
Resolver-->>Pipeline: config.githubPersonalAccessToken
else No PAT
Resolver->>Ctx: octokit.auth({ type: "installation" })
Ctx-->>Resolver: installation token
Resolver-->>Pipeline: token
end
Pipeline->>Pipeline: mkdir(${workDir}-artifacts)
Pipeline->>SDK: executeAgent({ artifactsDir })
SDK->>SDK: buildProviderEnv(token, artifactsDir)
SDK->>Subprocess: spawn with env { GH_TOKEN, GITHUB_TOKEN, BOT_ARTIFACT_DIR }
Subprocess->>Artifacts: write IMPLEMENT.md, RESOLVE.md, REVIEW.md
Subprocess-->>SDK: completion
SDK->>Artifacts: readCapturedFiles(artifactsDir)
Artifacts-->>SDK: report contents
SDK-->>Pipeline: captured reports
Pipeline->>Pipeline: rm(${workDir}-artifacts, recursive)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes The changes span multiple files across config, core execution, handlers, and tests with mixed complexity: configuration validation logic, async token resolution with conditional minting, filesystem operations in pipeline cleanup, and straightforward subprocess environment variable forwarding. The two cohorts are logically independent despite touching common files; artifact isolation is largely documentation and env-var threading, while PAT support requires understanding credential resolution flow and allowlist constraints. Test coverage is comprehensive for both features. No single file contains dense logic, but the distributed nature and credential sensitivity demand careful review of each token path and env-var forwarding. Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Comment |
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR bundles three bot-focused improvements: (1) a GitHub credential resolver that can optionally override the App installation token with a PAT (guarded by a single-owner allowlist), (2) tighter bot:implement PR-body guidance requiring Mermaid diagrams when behavior/flow changes, and (3) moving agent-authored summary artifacts (IMPLEMENT/REVIEW/RESOLVE) outside the cloned checkout so they don’t end up committed into target repos.
Changes:
- Add
GITHUB_PERSONAL_ACCESS_TOKENsupport viaresolveGithubToken()plus a startup guard (assertPatRequiresAllowlist) enforcing single-tenant operation. - Create a sibling
${workDir}-artifactsdirectory, plumb it through execution asBOT_ARTIFACT_DIR, and read captured summary files from there. - Update implement/review/resolve prompts + PR template to reference
$BOT_ARTIFACT_DIRand require Mermaid diagrams when behavior/flow changes; add unit tests covering token/env wiring.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/core/github-token.ts |
New centralized resolver for PAT-vs-installation token selection. |
src/config.ts |
Adds PAT config field + single-tenant startup assertion + warning when override active. |
src/orchestrator/connection-handler.ts |
Routes token acquisition through resolveGithubToken() for dispatched jobs. |
src/core/pipeline.ts |
Creates/uses sibling artifacts dir; captured-file reads now come from artifacts dir; cleans up both dirs. |
src/core/executor.ts |
Exports buildProviderEnv, adds optional artifacts dir -> BOT_ARTIFACT_DIR export. |
src/workflows/handlers/implement.ts |
Tightens Mermaid requirements; writes IMPLEMENT summary to $BOT_ARTIFACT_DIR. |
src/workflows/handlers/review.ts |
Writes REVIEW summary to $BOT_ARTIFACT_DIR. |
src/workflows/handlers/resolve.ts |
Writes RESOLVE summary to $BOT_ARTIFACT_DIR. |
.github/PULL_REQUEST_TEMPLATE/bot-implement.md |
Updates Diagram section instructions to require Mermaid on behavior/flow changes. |
docs/build/architecture.md |
Documents artifacts dir and credential resolution behavior. |
docs/operate/configuration.md |
Documents GITHUB_PERSONAL_ACCESS_TOKEN and updates ALLOWED_OWNERS guidance. |
CLAUDE.md |
Updates pipeline/auth documentation, including PAT override description. |
test/core/github-token.test.ts |
Adds unit tests for PAT short-circuit vs installation mint fallback. |
test/core/build-provider-env.test.ts |
Adds tests for GH token env wiring, BOT_ARTIFACT_DIR, and blank-credential scrubbing. |
test/config.test.ts |
Adds tests for assertPatRequiresAllowlist. |
IMPLEMENT.md |
Removed drift file from repo root. |
REVIEW.md |
Removed drift file from repo root. |
RESOLVE.md |
Removed drift file from repo root. |
Update implement.md / review.md / resolve.md Artifact rows to match the runtime change — IMPLEMENT.md / REVIEW.md / RESOLVE.md now live in the sibling temp dir exposed via $BOT_ARTIFACT_DIR, not the cloned repo root. Required by the docs-sync guard. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- pipeline: move mkdirSync(artifactsDir) inside the try block so cleanup() still fires for workDir if mkdir throws - orchestrator: skip getRepoInstallation+getInstallationOctokit when GITHUB_PERSONAL_ACCESS_TOKEN is set (saves two GitHub API calls per job offer in PAT mode) - pipeline: clarify readCapturedFiles header to mention the artifacts dir, not "the workspace" - CLAUDE.md + src/config.ts: clarify that the PAT changes API and push identity but commit author/committer metadata is pinned by src/core/checkout.ts to chrisleekr-bot[bot] regardless Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds safePostToGitHub chokepoint (src/utils/github-output-guard.ts) for GitHub-bound writes — regex pass strips matched bytes, optional LLM scanner (src/utils/llm-output-scanner.ts) catches encoded/obfuscated secrets with per-call nonce tags + spotlighting anti-injection defense. Wired through tracking-comment, scoped fix/explain-thread executors, and ship/scoped reply paths. Hardens executor subprocess via env allowlist + denylist (GITHUB_APP_PRIVATE_KEY, GITHUB_WEBHOOK_SECRET, DAEMON_AUTH_TOKEN, DATABASE_URL, VALKEY_URL, REDIS_URL, CONTEXT7_API_KEY, GITHUB_PERSONAL_ACCESS_TOKEN). sanitize.ts becomes dual-mode (input spotlighting + output redaction). Prompt builder rejects whitespace in triggerUsername (commit-trailer forging vector). Default scanner + triage models upgraded to sonnet-4-6. test/preload.ts clears GITHUB_PERSONAL_ACCESS_TOKEN to prevent local .env leak into test runs. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 `@docs/build/architecture.md`:
- Line 49: Update the sentence in the architecture doc that currently reads “bot
identity becomes the PAT owner” to avoid implying commit metadata changes:
explicitly state that src/core/github-token.ts:resolveGithubToken() will return
a PAT so the API/git authentication actor will act as the PAT owner, but commit
author/committer metadata is still controlled by the checkout configuration
(bot-pinned) and does not change. Mention both consumers (git credential helper
/ GH_TOKEN env / MCP server) use the resolved token for authentication and
clarify that only auth actor identity differs, not commit metadata.
In `@src/core/executor.ts`:
- Around line 55-63: Add two unit tests in test/core/build-provider-env.test.ts:
one that calls the env-building code with artifactsDir undefined (and another
with artifactsDir === "") and asserts that BOT_ARTIFACT_DIR is not present in
the returned object (verify absence), and a second test that sets
config.provider = "bedrock" with a non-empty artifactsDir and asserts both
BOT_ARTIFACT_DIR is present with the provided value and CLAUDE_CODE_USE_BEDROCK
=== "1" are present; locate the logic behind artifactsDir and BOT_ARTIFACT_DIR
in src/core/executor.ts (the artifactEnv variable and the branch checking
config.provider === "bedrock") and use those symbols to construct assertions.
In `@src/orchestrator/connection-handler.ts`:
- Around line 818-833: Collapse the duplicate PAT check by changing the call
site to pass a lazy octokit factory into resolveGithubToken instead of
performing the config.githubPersonalAccessToken check inline: remove the local
if/else branch in connection-handler.ts and call resolveGithubToken with an
async getOctokit function that calls getOrCreateApp(), fetches the repo
installation via app.octokit.rest.apps.getRepoInstallation({ owner, repo }) and
returns app.getInstallationOctokit(installation.id); then update
resolveGithubToken to accept getOctokit: () => Promise<Pick<Octokit, "auth">>
and internally prefer the passed pat or config.githubPersonalAccessToken before
invoking getOctokit() to request an installation token.
- Around line 818-833: Two code paths miss the PAT short-circuit and must be
updated to avoid minting installation tokens: in postOrphanNotification, and in
the tickle scheduler/octokitFactory used by resumeShipIntent (session-runner),
check if config.githubPersonalAccessToken !== undefined and if so use that PAT
for all GitHub API calls (skip calling app.octokit.rest.apps.getRepoInstallation
and app.getInstallationOctokit) so comments, reactions and scheduler actions are
performed with the PAT user; otherwise retain the existing installation-token
logic (mirror the pattern used in handleJobOfferAccept).
In `@test/core/build-provider-env.test.ts`:
- Around line 5-9: Add explicit regression assertions to the existing test for
buildProviderEnv to verify that high-risk secret keys are never forwarded: call
buildProviderEnv(...) in the same test and assert the returned env object does
NOT contain DAEMON_AUTH_TOKEN, GITHUB_APP_PRIVATE_KEY, DATABASE_URL, and
VALKEY_URL. Place these negative assertions alongside the current positive
assertions that check keys the function sets so the test continues to validate
intended keys while preventing silent reintroduction of exfil paths.
In `@test/core/github-token.test.ts`:
- Around line 23-28: The test relies on the module-level config singleton
(config.githubPersonalAccessToken) which may be set by the environment; update
the suite to pin or mock that singleton so the case is deterministic: in the
"falls back..." test ensure config.githubPersonalAccessToken is undefined by
mocking the config module (e.g., use mock.module("../../src/config", () => ({
config: { githubPersonalAccessToken: undefined } })) or equivalent) or
save/restore process.env around the suite so resolveGithubToken reads the
intended value; target the resolveGithubToken call and the
config.githubPersonalAccessToken symbol when applying the change.
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 2e20a24f-17c5-4189-bbe7-760e44c119bb
📒 Files selected for processing (21)
.github/PULL_REQUEST_TEMPLATE/bot-implement.mdCLAUDE.mdIMPLEMENT.mdRESOLVE.mdREVIEW.mddocs/build/architecture.mddocs/operate/configuration.mddocs/use/workflows/implement.mddocs/use/workflows/resolve.mddocs/use/workflows/review.mdsrc/config.tssrc/core/executor.tssrc/core/github-token.tssrc/core/pipeline.tssrc/orchestrator/connection-handler.tssrc/workflows/handlers/implement.tssrc/workflows/handlers/resolve.tssrc/workflows/handlers/review.tstest/config.test.tstest/core/build-provider-env.test.tstest/core/github-token.test.ts
💤 Files with no reviewable changes (3)
- RESOLVE.md
- REVIEW.md
- IMPLEMENT.md
- postOrphanNotification (connection-handler.ts): when GITHUB_PERSONAL_ACCESS_TOKEN is set, build the Octokit from the PAT instead of minting an installation token. Otherwise orphan-notification comments and reactions appear under the App identity while every other bot reply is from the PAT user — operator-visible cross-identity inconsistency that breaks the "PAT replaces the installation token for ALL GitHub API calls" contract. - Ship tickle scheduler octokitFactory (app.ts): same short-circuit so resumeShipIntent's push/PR/comment actions run as the PAT user. - Tests: add absent/empty BOT_ARTIFACT_DIR regression guards in build-provider-env.test.ts; pin the config singleton in github-token.test.ts via mock.module so a developer who exports GITHUB_PERSONAL_ACCESS_TOKEN locally does not silently flip the installation-token fallback assertion. - Docs: clarify in architecture.md that PAT mode changes only the API/git authentication actor — commit author/committer metadata is pinned by src/core/checkout.ts and stays bot-attributed regardless. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
🎉 This PR is included in version 1.10.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Two production bugs surfaced during local E2E testing of the chat-thread executor: 1. PR #104 (e0d5894) silently regressed the OAuth fix from 82f8332. createLLMClient was once again passing CLAUDE_CODE_OAUTH_TOKEN (sk-ant-oat-...) to the Anthropic SDK as `apiKey`, sending it via x-api-key — a header the API rejects with 401 invalid-x-api-key for OAuth tokens. OAuth must go via Authorization: Bearer (= SDK `authToken`). Restore the branch from 82f8332: API keys → `apiKey`, OAuth → `authToken`. This affects every orchestrator-side classifier call (intent-classifier, nl-classifier, LLM output scanner, and now chat-thread) — every one of them was silently degrading to clarify fallback for OAuth-only deployments. 2. chat-thread.ts caught LLM failures with `log.error({ err }, ...)` but pino's safe-stable-stringify cannot walk Anthropic SDK error objects (they carry circular fetch Response refs) and dropped the payload as "[unable to serialize, circular reference is too complex to analyze]" — hiding the actual 401 / 429 / 500 status. Mirror the nl-classifier / dispatch-scoped pattern (also from 82f8332) and log `err.message` (or String(err)) so operators can see the real cause. Validated locally: - bun run typecheck — clean - E2E against ngrok'd dev server: 401 disappeared; the chat-thread catch now surfaces the underlying 429 rate_limit_error from the OAuth subscription quota verbatim, confirming the auth header is now accepted.
Summary
Four independent bot fixes bundled into one branch:
Optional GitHub PAT override. New
GITHUB_PERSONAL_ACCESS_TOKENconfig bypasses the App installation token for every GitHub API/git operation — useful for solo/dev installs and to side-step App-token rate limits. Single-tenant only:ALLOWED_OWNERSmust contain exactly one owner (mirrors the existingCLAUDE_CODE_OAUTH_TOKENconstraint), enforced at startup viaassertPatRequiresAllowlist. Resolution centralised insrc/core/github-token.ts:resolveGithubToken(). The PAT changes API + push identity, but commit author/committer metadata stays pinned tochrisleekr-bot[bot]bysrc/core/checkout.ts. Orchestrator skipsgetRepoInstallation+getInstallationOctokitwhen the PAT is set, saving two GitHub API calls per job offer.Mermaid diagram now required for behaviour/flow changes in
bot:implementPR descriptions. Previous prompt told the agent to skip the Diagram section "unless behaviour or flow actually changes" — too lax. Tightened to require a Mermaid block whenever code paths, state, external calls, error handling, or sequencing shift. Pure refactors / typo fixes / test-only changes still skip.IMPLEMENT.md/REVIEW.md/RESOLVE.mdno longer get committed to target repos. Pre-fix the agent wrote these summary files at the repo root inside the cloned checkout, so they ended up in PR commits. Pipeline now creates a sibling${workDir}-artifactsdirectory outside the checkout, exposed to the agent asBOT_ARTIFACT_DIR.readCapturedFilesreads from there; both directories are removed in the samefinallyblock.mkdirSync(artifactsDir)lives inside thetryblock socleanup()still fires forworkDirif mkdir throws.Secret-exfiltration hardening (closes security(pipeline): Claude Code subprocess inherits all daemon secrets via process.env spread in buildProviderEnv #102). Four-layer defence against agent-side prompt-injection that tries to make the bot post secrets back to GitHub:
buildProviderEnvinsrc/core/executor.ts): explicit allowlist + prefix patterns + deny-set replaces wildcard...process.env. Banned daemon secrets (GITHUB_APP_PRIVATE_KEY,GITHUB_WEBHOOK_SECRET,DAEMON_AUTH_TOKEN[_PREVIOUS],DATABASE_URL,VALKEY_URL,REDIS_URL,CONTEXT7_API_KEY,GITHUB_PERSONAL_ACCESS_TOKEN) cannot reach the agent CLI. SetsCLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1so grandchildren inherit no creds.src/utils/sanitize.ts+src/core/prompt-builder.ts): user-controlled fields wrapped in nonce-bracketed delimiters;triggerUsernamerejected (not stripped) when it contains whitespace to block git commit-trailer forging.src/utils/github-output-guard.ts:safePostToGitHub): every GitHub-bound write goes throughredactSecrets(); matched bytes silently stripped (no[REDACTED_X]markers — markers leak probing signal). Skip-and-error when redaction empties the body.src/utils/llm-output-scanner.ts): catches encoded/obfuscated secrets the regex misses, with per-call nonce tag names to block tag-close injection. Default ON forsource: "agent", fail-open on Bedrock outage. NewH7daemon-secret-leak heuristic insrc/config.ts.Phase-1 wiring:
tracking-comment.ts(create + update), both scoped daemon executors, both ship/scoped reply paths. Phase-2 callsites tracked inCLAUDE.md§ Coverage status. Default scanner + triage models bumped tosonnet-4-6.Diagram
flowchart TB classDef before fill:#7a1f1f,stroke:#3d0f0f,color:#ffffff classDef after fill:#1f5a3a,stroke:#0f3b25,color:#ffffff classDef shared fill:#3d3d3d,stroke:#1f1f1f,color:#ffffff Webhook["webhook delivery"]:::shared --> Pipeline["pipeline.runPipeline"]:::shared Pipeline --> BeforeAuth["BEFORE<br/>octokit.auth type=installation<br/>at every callsite"]:::before Pipeline --> AfterAuth["AFTER<br/>resolveGithubToken octokit<br/>PAT short-circuit when set<br/>orchestrator skips installation lookup"]:::after Pipeline --> BeforeFs["BEFORE<br/>workDir=clone-tempdir<br/>agent writes IMPLEMENT.md<br/>at repo root inside checkout<br/>file lands in PR commit"]:::before Pipeline --> AfterFs["AFTER<br/>workDir + sibling artifacts dir<br/>BOT_ARTIFACT_DIR env exported<br/>agent writes summaries OUTSIDE<br/>checkout cannot pick up via git add"]:::after Pipeline --> BeforeMermaid["BEFORE<br/>bot:implement prompt<br/>skip Diagram unless behaviour changes<br/>agent often skipped anyway"]:::before Pipeline --> AfterMermaid["AFTER<br/>bot:implement prompt<br/>Mermaid required when behaviour/flow shifts<br/>only omit for pure refactor/typo/test-only"]:::after Pipeline --> BeforeSecrets["BEFORE<br/>subprocess inherits process.env<br/>agent sees GITHUB_APP_PRIVATE_KEY<br/>DB_URL VALKEY_URL etc.<br/>writes posted directly to GitHub"]:::before Pipeline --> AfterSecrets["AFTER<br/>buildProviderEnv allowlist+denyset<br/>safePostToGitHub regex+LLM strip<br/>nonce-tagged spotlight on user input<br/>4 layers gate every reply"]:::afterChanges
src/config.ts):githubPersonalAccessToken,assertPatRequiresAllowlist()guard, LLM scanner settings (llmOutputScanner.*),H7daemon-secret-leak detector heuristic, startup warn when PAT override is active.src/core/github-token.ts, new): single source for GitHub credential — PAT short-circuit or installation-token mint.pipeline.tsand orchestratorconnection-handler.tsmint sites go throughresolveGithubToken(). Orchestrator avoidsgetRepoInstallation+getInstallationOctokitwhen PAT is set.src/core/pipeline.ts): sibling${workDir}-artifactsdir created insidetryblock, passed to executor, cleaned up alongsideworkDirinfinally.readCapturedFilesreads fromBOT_ARTIFACT_DIR.src/core/executor.ts):buildProviderEnvtakesartifactsDir, exportsBOT_ARTIFACT_DIR, replaces wildcard env passthrough with allowlist + prefix patterns + deny-set; setsCLAUDE_CODE_SUBPROCESS_ENV_SCRUB=1.src/utils/github-output-guard.ts, new):safePostToGitHub({ body, source, callsite, log, post })chokepoint — regex pass + optional LLM scanner, fails open on Bedrock outage, skips post + logs error when redaction empties the body, helperapplyLlmRedactionkeeps cyclomatic depth bounded.src/utils/llm-output-scanner.ts, new): Bedrock/Anthropic single-turn classifier with per-call nonce tag names, JSON-only response shape, single retry on malformed JSON, stub injector for tests.src/utils/sanitize.ts): dual-mode — input spotlighting nonce delimiters + outputredactSecrets()(GitHub tokens, AWS access keys, DB URLs stopping at markdown delimiters, JWT-shaped strings ≥20-char segments, RSA private-key blocks). Stateful regex reuse bug fixed.src/core/prompt-builder.ts): nonce-spotlighted user fields; rejects whitespace intriggerUsername(commit-trailer forging vector).tracking-comment.ts(create + update),scoped-explain-thread-executor.ts,scoped-fix-thread-executor.ts,workflows/ship/scoped/explain-thread.ts,workflows/ship/scoped/fix-thread.tsroute writes throughsafePostToGitHub.updateTrackingCommentnow throws on skip instead of silently no-oping.comment.ts,inline-comment.ts): inlineredactSecrets()+console.errorlog (subprocess can't import the daemon-config-bound chokepoint).implement.ts/review.ts/resolve.tssummary writes point at$BOT_ARTIFACT_DIR/<file>.mdwith explicit "do NOT git add" warning;implement.tsMermaid rule tightened.src/k8s/ephemeral-daemon-spawner.ts): env propagation aligned with the new allowlist semantics.claude-3-5-haikutosonnet-4-6alias added toMODEL_MAP; snapshot test updated.test/config.test.ts(4 new PAT-allowlist cases),test/core/github-token.test.ts(3 cases),test/core/build-provider-env.test.ts(12 cases — banned-key denial, prefix-allow, BOT_ARTIFACT_DIR, blank-credential scrub),test/utils/github-output-guard.test.ts(6 cases — clean post, regex strip, empty-after-redact skip, LLM fail-open, LLM strip, system-source no-LLM),test/utils/sanitize.test.tsextended with output-redaction cases,test/preload.tsclearsGITHUB_PERSONAL_ACCESS_TOKENto prevent local.envleak.CLAUDE.mdadds "Security invariants" section + Authentication options GitHub-credential subsection;docs/operate/configuration.mdaddsGITHUB_PERSONAL_ACCESS_TOKEN, LLM scanner env vars, "Subprocess env allowlist" subsection;docs/build/architecture.mdupdated;docs/use/workflows/{implement,review,resolve}.mdArtifact rows reflect$BOT_ARTIFACT_DIR.IMPLEMENT.md,REVIEW.md,RESOLVE.mdfrom the repo root..env.example: addsGITHUB_PERSONAL_ACCESS_TOKEN+ LLM scanner vars.Related Issues
Test plan
bun run typecheckcleanbun run lint0 errors in modified filesbun test test/core/build-provider-env.test.ts test/utils/github-output-guard.test.ts— 21/21 passbun run docs:build+check-docs-versions.ts+check-docs-citations.tsall greenGITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx+ single-ownerALLOWED_OWNERS, fire a webhook, verify resulting commits show the PAT user as author and bot identity stays on commit metadata.@chrisleekr-bot-dev bot:implementon a sandbox repo; verifyIMPLEMENT.mdis NOT in the PR file list and orchestrator log showscapturedFilesread from${workDir}-artifacts. Repeat forbot:review(REVIEW.md) and a resolve flow (RESOLVE.md).ghp_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaatoken via tracking-comment path; verify the resulting GitHub comment has the bytes stripped and ascanner: "regex"warn line appears in the daemon log without the original token bytes.Summary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests