Skip to content

fix(bot): harden automated candidate delivery - #2429

Merged
ascorbic merged 3 commits into
mainfrom
codex/harden-emdash-bot-delivery
Aug 12, 2026
Merged

fix(bot): harden automated candidate delivery#2429
ascorbic merged 3 commits into
mainfrom
codex/harden-emdash-bot-delivery

Conversation

@ascorbic

@ascorbic ascorbic commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Hardens emdashbot's automated implementation and candidate-delivery path after its first real implementation attempt exposed weak verification and publication boundaries.

  • Publishes candidate branches from trusted Worker code through GitHub's Git Data API, with fast-forward and stale-head checks.
  • Restricts direct sandbox pushes to issue-scoped artifact branches and parses the actual receive-pack ref field.
  • Records authoritative verification exit codes and binds every passing check to the exact Git tree published.
  • Gives implement a dedicated result contract while retaining the preview, reporter-confirmation, and draft-PR gates.
  • Adds failure-stage/run diagnostics, configurable staging preview packages, and an opt-in end-to-end implementation smoke script.

Follow-up hardening for #2383.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main. (N/A: no admin UI changes.)
  • I have added a changeset (if this PR changes a published package) — N/A: this changes private bot infrastructure only.
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/... — N/A: this is reliability and security hardening for existing bot behavior.

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: OpenAI Codex (GPT-5)

Screenshots / test output

  • pnpm lint
  • pnpm build
  • pnpm typecheck
  • Bot unit suite: 216 tests passed
  • Cloudflare Worker integration suite: 43 tests passed
  • Bot production Vite build passed
  • git diff --check

The live evals:implementation smoke was not run because it intentionally creates real comments, labels, a candidate branch, and a draft PR in a configured staging repository. The bot package's standalone tsc --noEmit still reports its existing generated Flue/Wrangler RPC binding gaps; the repository package typecheck and production bot build pass.


Try this PR

Open a fresh playground →

A full working EmDash site, deployed from this branch. Each visit gets its own session-scoped sandbox: no login needed and no shared state. Try the admin, edit content, hit the public site.

Tracks codex/harden-emdash-bot-delivery. Updated automatically when the playground redeploys.

Copilot AI lite review requested due to automatic review settings August 11, 2026 20:05
@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 94e6aa6

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 2,289 lines across 35 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-do 94e6aa6 Aug 12 2026, 06:46 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-demo-cache 94e6aa6 Aug 12 2026, 06:46 AM

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground 94e6aa6 Aug 12 2026, 06:48 AM

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2429

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2429

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2429

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2429

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2429

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2429

emdash

npm i https://pkg.pr.new/emdash@2429

create-emdash

npm i https://pkg.pr.new/create-emdash@2429

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2429

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2429

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2429

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2429

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2429

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2429

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2429

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2429

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2429

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2429

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2429

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2429

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2429

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2429

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2429

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2429

commit: 94e6aa6

@emdashbot emdashbot Bot 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.

This PR hardens the bot's candidate-delivery path in the right direction: moving publication from the agent container to the trusted Worker via GitHub's Git Data API, adding fast-forward/idempotency checks, binding verification records to the candidate tree, and giving implement its own result contract. I read the diff, the full changed modules, the new tests, and traced the state-machine and proxy call sites.

The change is mostly clean and well-tested, but two issues need attention before merge.

  • renderAgentComment for the legacy awaiting_feedback path ignores PREVIEW_PACKAGE. The new preview_buildingawaiting_reporter flow uses renderPreviewReadyAsk and renderDraftPrBody, both of which respect PREVIEW_PACKAGE; the older workingawaiting_feedback path still returns a hard-coded emdash-cms/emdash URL. @emdashbot repro (which still lands in working) will therefore advertise the production preview in a staging deployment, defeating the purpose of the new config.

  • Container-side file modifications from verification commands are lost before publication. snapshotCandidate materializes the VFS onto the container before staging, so any edits made by a container command (e.g. pnpm format) are overwritten. run_check records the container tree SHA after the command, so publish_candidate then fails the tree-match check and refuses to publish. The skill still instructs agents to run formatting through run_check, which sets up a legitimate workflow that cannot complete.

Comment thread infra/emdash-bot/.flue/lib/comments.ts
Comment thread infra/emdash-bot/.flue/lib/exec-env.ts

Copilot AI 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.

Pull request overview

Hardens the infra/emdash-bot implementation + candidate-delivery pipeline by moving candidate publication into trusted Worker code (Git Data API), tightening what the sandbox can push, and recording/verifying run outputs against an exact published Git tree.

Changes:

  • Adds a Worker-owned candidate publisher (publish_candidate) with stale-head checks, fast-forward-only ref updates, and post-publication verification.
  • Restricts sandbox git-receive-pack to issue-scoped artifacts branches and parses the actual receive-pack command ref (not ref-like capability text).
  • Introduces an implement lane/result contract (separate from bug reproduction fields), plus richer failure/run diagnostics, configurable preview package targeting, and an opt-in end-to-end implementation smoke script.

Reviewed changes

Copilot reviewed 33 out of 35 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
infra/emdash-bot/wrangler.jsonc Adds PREVIEW_PACKAGE configuration for staging/prod preview targeting.
infra/emdash-bot/worker-configuration.d.ts Regenerates Worker env typings to include PREVIEW_PACKAGE.
infra/emdash-bot/package.json Adds evals:implementation smoke script entrypoint.
infra/emdash-bot/BOT_STATE_MACHINE.md Updates state/event docs for the implement → fixing lane and “candidate change” wording.
infra/emdash-bot/.flue/skills/investigate/SKILL.md Updates fix-loop guidance to route publication through publish_candidate.
infra/emdash-bot/.flue/skills/implement/SKILL.md Adds a new skill defining the directed-implementation lane and its boundaries.
infra/emdash-bot/.flue/skills/fix/SKILL.md Updates fix guidance to use run_check + publish_candidate and removes direct git push instructions.
infra/emdash-bot/.flue/lib/verification.ts Adds verification-command hardening and “latest named checks must pass + match tree” enforcement.
infra/emdash-bot/.flue/lib/router.ts Extends agent result with implemented and maps implement outcomes to agent.fix_ready/agent.failed.
infra/emdash-bot/.flue/lib/preview.ts Adds preview-package parameterization + validation for pkg.pr.new URLs.
infra/emdash-bot/.flue/lib/comments.ts Shifts copy from “fix” → “change”, supports staging preview packages, and adds failure stage/run-id in failed comments.
infra/emdash-bot/.flue/lib/github.ts Adds Git Data API helpers (blob/tree/commit/ref) for Worker-owned publication.
infra/emdash-bot/.flue/lib/candidate-publisher.ts Implements candidate snapshot publication via Git Data API with race/stale-head and tree-sha verification.
infra/emdash-bot/.flue/lib/github-proxy.ts Tightens outbound gating: only artifacts pushes; parses receive-pack ref safely; surfaces structured gate diagnostics.
infra/emdash-bot/.flue/lib/exec-env.ts Wraps container commands with bash -o pipefail, snapshots staged candidate content, and enforces publication bounds.
infra/emdash-bot/.flue/lib/machine.ts Updates state descriptions and transitions so implement enters fixing and uses “candidate change” wording.
infra/emdash-bot/.flue/lib/machine.json Regenerates machine JSON for updated transitions and descriptions.
infra/emdash-bot/.flue/lib/orchestrator.ts Threads PREVIEW_PACKAGE through preview polling + comments/PR body; persists run diagnostics for failure reporting.
infra/emdash-bot/.flue/cloudflare.ts Switches to structured GitHub request inspection and logs denial stage/refs/parse errors for ops visibility.
infra/emdash-bot/.flue/agents/investigate.ts Adds run_check/publish_candidate, persists verification/publication metadata, and introduces report_implementation.
infra/emdash-bot/evals/src/types.ts Extends reported result types with run/publication/verification details and implement-mode fields.
infra/emdash-bot/evals/src/client.ts Updates reported-result detection logic and comments for new payload shape.
infra/emdash-bot/evals/README.md Documents the new live implementation delivery smoke run and staging preview-package isolation.
infra/emdash-bot/evals/bin/implementation-smoke.ts Adds an opt-in end-to-end smoke that exercises real delivery (branch, preview, confirm, draft PR).
infra/emdash-bot/tests/unit/router.test.ts Updates router expectations for fixing state/label and implement-mode outcome semantics.
infra/emdash-bot/tests/unit/preview.test.ts Adds coverage for previewPackage behavior and input validation.
infra/emdash-bot/tests/unit/orchestrator-comments.test.ts Verifies new copy, staging preview package injection, and failed-stage/run diagnostics.
infra/emdash-bot/tests/unit/github-proxy.test.ts Updates push gating expectations and adds tests for capability vs body parsing and command-ref parsing.
infra/emdash-bot/tests/unit/exec-env.test.ts Updates expectations for pipefail-wrapped commands and adds candidate snapshot parsing/guard tests.
infra/emdash-bot/tests/unit/evals-scorer.test.ts Updates eval harness expected reported payload shape.
infra/emdash-bot/tests/unit/evals-client.test.ts Updates eval client fixture to include run/publication/verification fields.
infra/emdash-bot/tests/integration/orchestrator.test.ts Updates integration expectations for implement lane, preview gating, and failed-comment diagnostics.
infra/emdash-bot/tests/unit/verification.test.ts Adds direct unit coverage for verification-command and record selection/tree binding.
infra/emdash-bot/tests/unit/github.test.ts Adds unit coverage for Git Data API request shapes and ref creation/update semantics.
infra/emdash-bot/tests/unit/candidate-publisher.test.ts Adds unit coverage for candidate publication invariants (stale head, idempotency, tree match, fast-forward parent).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread infra/emdash-bot/.flue/lib/orchestrator.ts Outdated
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond cla: signed and removed review/needs-review No maintainer or bot review yet labels Aug 11, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Aug 12, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Aug 12, 2026

@emdashbot emdashbot Bot 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.

This PR hardens emdashbot's candidate-delivery path in the right direction: moving publication from the sandbox to the trusted Worker, adding fast-forward/idempotency checks, binding verification records to the candidate tree, and giving implement its own result contract.

On re-review, the two issues from the previous pass are resolved:

  • renderAgentComment now uses previewInstallCommand(anchorNumber, previewPackage) for every agent.fix_ready branch, so the legacy awaiting_feedback lane no longer advertises the production package in staging deployments.
  • Verification commands are now read-only: ExecEnv.runCheck detects any working-tree mutation, restores the container to the VFS state, and rejects the command. The skill instructions have been updated to apply formatting via edit_file/write_file and run only check-only formatter commands.

I read the full changed modules, the new tests, and traced the state-machine and proxy call sites. The code is well-tested and the approach is sound, but one logic issue remains in the publication idempotency path.

Comment on lines +65 to +68
if (liveBefore) {
const liveCommit = await github.getCommit(liveBefore);
if (liveCommit.message.includes(runMarker)) {
return { branch: input.branch, commitSha: liveBefore, files };

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.

[needs fixing] The idempotency branch returns the live commit as soon as its message contains the run marker, without checking whether that commit's tree matches the current snapshot. If a single run calls publish_candidate more than once after making additional edits, the second snapshot will have a different treeSha than the commit already on the branch, but this branch silently returns the stale commit. The agent then reports implemented: true / fixed: true against a publication that does not contain the verified candidate tree.

Suggested change
if (liveBefore) {
const liveCommit = await github.getCommit(liveBefore);
if (liveCommit.message.includes(runMarker)) {
return { branch: input.branch, commitSha: liveBefore, files };
if (liveBefore) {
const liveCommit = await github.getCommit(liveBefore);
if (liveCommit.message.includes(runMarker)) {
if (liveCommit.treeSha !== input.snapshot.treeSha) {
throw new Error(
"candidate branch already has a commit from this run with a different tree",
);
}
return { branch: input.branch, commitSha: liveBefore, files };
}
}

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Aug 12, 2026
@github-actions github-actions Bot added review/awaiting-author Reviewed; waiting on the author to respond and removed review/needs-rereview Author pushed changes since the last review labels Aug 12, 2026
@ascorbic
ascorbic enabled auto-merge (squash) August 12, 2026 09:06
@ascorbic
ascorbic merged commit 4a6cd0b into main Aug 12, 2026
75 of 76 checks passed
@ascorbic
ascorbic deleted the codex/harden-emdash-bot-delivery branch August 12, 2026 09:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed review/awaiting-author Reviewed; waiting on the author to respond size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants