GitHub production smoke testing
Use this after a planned production recreation to prove the iterate project's
GitHub connection, config-repo synchronization, webhook assignment, and PR
routing. It creates one temporary draft PR in iterate/config; do not mention
the production GitHub App login in its title or body, so it cannot wake an LLM
turn.
Run OS commands from apps/os. Resolve the production project ID rather than
hard-coding it:
doppler run --config prd -- pnpm cli itx run -e '
const projects = await itx.projects.list({ scope: "deployment" });
return projects.filter((project) => project.slug === "iterate");
'Prove the connection and config repo#
Use the resolved ID as --context. This makes an authenticated GitHub request,
pulls GitHub's default branch into Artifacts, pushes the equal head back through
the linked repo, and reports all heads:
doppler run --config prd -- pnpm cli itx run --context <iterate-project-id> -e '
const before = await itx.repo.processor.snapshot();
const github = before.state.github;
if (github === null) throw new Error("config repo is not linked to GitHub");
const status = await itx.integrations.getConnection({
provider: "github",
connection: github.connection,
});
const octokit = itx.integrations.github.get(github.connection).octokit;
const repository = await octokit.rest.repos.get({ owner: github.owner, repo: github.repo });
const branch = repository.data.default_branch;
const remoteBefore = await octokit.rest.git.getRef({
owner: github.owner,
repo: github.repo,
ref: `heads/${branch}`,
});
const sync = await itx.repo.syncFromGithub({ force: true });
const push = await itx.repo.pushToGithub({});
const local = await itx.repo.listFiles();
return {
status,
link: github,
fullName: repository.data.full_name,
remoteHead: remoteBefore.data.object.sha,
localHead: local.commitOid,
sync,
push,
};
'Require a connected status with the recorded installation ID, iterate/config,
and equal remote, local, sync, and push commit OIDs. This proves both GitHub App
requests and full-history config synchronization; it does not yet prove webhook
routing.
Prove webhook routing with gh#
Create an empty-commit draft PR from a temporary clone. The user must have authorized this externally visible smoke action (the recreation operator normally asks once before cutover):
TMP=$(mktemp -d)
gh repo clone iterate/config "$TMP/config"
cd "$TMP/config"
BRANCH="smoke/recreate-production-$(date -u +%Y%m%d-%H%M%S)"
git switch -c "$BRANCH"
git commit --allow-empty -m "Smoke test production GitHub routing"
git push -u origin "$BRANCH"
gh pr create --repo iterate/config --base main --head "$BRANCH" --draft \
--title "Smoke: production GitHub routing" \
--body "Temporary post-recreation webhook-routing smoke. No review requested."Record the PR number. Then inspect both the original connection fact and the userspace path derived from the internal repo address:
doppler run --config prd -- pnpm cli itx run \
--context <iterate-project-id> \
--vars '{"pullRequest":123}' \
-e '
const snapshot = await itx.repo.processor.snapshot();
const github = snapshot.state.github;
if (github === null) throw new Error("config repo is not linked to GitHub");
const connectionPath = `/integrations/github/${github.connection}`;
const agentPath = `/agents/repos/config/pr/${vars.pullRequest}`;
const readAll = async (stream, eventTypes) => {
const events = [];
let afterOffset = 0;
for (;;) {
const page = await stream.getEvents({ afterOffset, eventTypes, limit: 500 });
events.push(...page);
if (page.length < 500) return events;
afterOffset = page.at(-1).offset;
}
};
const [connectionEvents, agentEvents] = await Promise.all([
readAll(itx.streams.get(connectionPath), [
"events.iterate.com/github/webhook-received",
]),
readAll(itx.streams.get(agentPath)),
]);
return {
agentPath,
connectionPath,
originalDeliveries: connectionEvents
.filter((event) =>
event.payload?.associations?.pullRequest?.number === vars.pullRequest,
)
.map((event) => ({
offset: event.offset,
installationId: event.payload?.installationId,
delivery: event.payload?.delivery,
associations: event.payload?.associations,
})),
agentCreated: agentEvents.find(
(event) => event.type === "events.iterate.com/agent/created",
),
copiedDeliveries: agentEvents.filter(
(event) => event.type === "events.iterate.com/github/webhook-received",
),
processorSubscriptions: agentEvents
.filter(
(event) =>
event.type ===
"events.iterate.com/stream/subscription-configured" &&
event.payload?.receiver?.action === "processor-wake",
)
// The subscription NAME selects the contract (name == registered slug).
.map((event) => event.payload?.name),
};
'Webhook delivery is asynchronous; retry the read for up to a minute. Require one
original pull_request:opened delivery whose singular pull-request association,
repository ID, and installation match the link; the canonical agent/created
fact at /agents/repos/config/pr/<number>; and the same complete delivery copied
to that stream. The processor subscriptions must contain the ordinary agent
and capability-host processors and must not contain the removed
github-agent. This proves the installation directory selected the right
connection and the current userspace config worker created and routed the PR
agent without a platform GitHub-agent processor.
Close the PR and delete its branch after recording evidence:
gh pr close --repo iterate/config <number> --delete-branchThe post-recreation GitHub smoke is complete only when both sections pass. A PR created before the recreation is baseline evidence, not restore evidence.