Skip to content

[AI-98] HITL issue-to-PR automation pipeline (Phases 0–7, code) — DRAFT#64

Draft
roberthunterjr wants to merge 29 commits into
mainfrom
ai-98-hitl-bug
Draft

[AI-98] HITL issue-to-PR automation pipeline (Phases 0–7, code) — DRAFT#64
roberthunterjr wants to merge 29 commits into
mainfrom
ai-98-hitl-bug

Conversation

@roberthunterjr

@roberthunterjr roberthunterjr commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

AI-98 — HITL Issue-to-PR Automation Pipeline

Automates TDD bug fixes: when an engineer labels a GitHub issue agent-ready (the human-in-the-loop gate), an Azure Durable Functions app runs a Claude Opus 4.8 agent hosted on Microsoft Foundry that reproduces the bug with a failing test, fixes it, runs validation on GitHub Actions, and opens a draft PR.

Status: DRAFT — code/docs complete, not yet deployed or live-verified. Opening for human review ahead of the operator-gated deploy.

What's included (Phases 0–7, code & docs)

  • Infra (Bicep): infra/issue-to-pr/ — Consumption Functions app (Node 22, system-assigned MI), Foundry resource + claude-opus-4-8 deployment (eastus2), agent-runs Cosmos container, RBAC (KV Secrets User, Cosmos data-plane role, Cognitive Services User).
  • Auth: github-client.js — GitHub App JWT → cached installation token.
  • Tools: nine in-process GitHub handlers + shared github-request helper.
  • Agent (Path B): agent-runner.js self-owned Claude tool-use loop that yields at validation so the orchestrator owns GHA polling; agent-tools.js, foundry-client.js, run-store.js.
  • Orchestration: WorkflowOrchestrator.js + WorkflowActivities.js — deterministic Durable generator with a df.createTimer polling loop, MAX_ROUNDS/MAX_POLLS guards.
  • Ingress: GitHubWebhookReceiver.js — HMAC validation, label filter, deterministic instance id (idempotent re-labels), branchName computation.
  • CI/CD: three workflows (agent-execution.yml validation runner, PR gate, IaC-first deploy), actions SHA-pinned to the allowed list.
  • Docs: design spec, implementation plan, Phases 1–4 feature guide (with test-case mapping), implementation-status/resume guide, operator runbook.

Architecture

agent-ready label → receiver (HMAC + idempotent start) → Durable orchestrator → Slack notice → agent edits + dispatches validation, yields → orchestrator polls GHA on a durable timer → agent reacts (opens draft PR / re-validates / fails) → failure comment on the issue. Tool calls run in-process (Path B — no remote MCP server); Claude auth is managed-identity Entra ID (no API key). Decision record: docs/superpowers/specs/.

Testing

  • 93 unit tests pass, biome lint clean, az bicep build compiles. Each feature is mapped to its tests in docs/AI-98-feature-guide.md.
  • External systems (GitHub API, Foundry/Claude, Cosmos, Slack, GHA) are covered by mocks. Live end-to-end (Phase 5.2 smoke, Phase 7 integration/staging/failure-path) is pending the deploy.

Security review

A security review was run on this branch — no HIGH/MEDIUM findings at the reporting threshold. RS256 signing, timing-safe HMAC, parameterized Cosmos access, and post-validation parsing were verified sound. Two defense-in-depth items surfaced by the review have been applied (commit db4f6b9): (1) the webhook validator now fails closed if the secret is empty/unresolved; (2) the receiver enforces an AGENT_ALLOWED_REPOS allowlist when set (defaults to Ed-Fi-Alliance-OSS/Fiona in Bicep). 98 tests pass.

Remaining — operator-gated (NOT in this PR)

  • Deploy: confirm Azure Marketplace access for the Anthropic Claude offering; az deployment group create (with the GitHub App ID); set the GitHub App webhook URL post-deploy.
  • Live verification: Phase 5.2 local smoke test + Phase 7 staging/failure-path.

⚠️ AI authorship & accountability

This change set was substantially authored by AI (Claude), built via test-driven development with a per-task implement→review→fix loop. Per Foundation policy, a human must review before merge/deploy, and accountability for the final output rests with the reviewer. Assumptions/limitations are documented in docs/AI-98-implementation-status.md.

🤖 Generated with Claude Code

roberthunterjr and others added 29 commits June 16, 2026 17:27
Captures approved architecture for HITL bug-fix automation:
Azure Durable Functions orchestration, Azure AI Agent Service (Claude),
MCP tool server via Azure Functions, and GitHub Actions as the
execution environment for lint/test validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- package.json, host.json, biome.json, jest.config.js, local.settings.json.example
- lib/webhook-validator.js: constant-time HMAC-SHA256 signature verification (6 tests)
- src/functions/GitHubWebhookReceiver.js: filters agent-ready label events,
  starts Durable orchestration, returns 202 immediately (6 tests)
- .gitignore: override root lib/ rule (Python artifact) to allow src/lib/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full phase-by-phase implementation plan for the issue-to-pr pipeline,
covering infrastructure (Bicep IaC), GitHub App setup, auth layer,
MCP tool server (9 handlers), agent runner, Durable orchestrator,
GitHub Actions workflows, and end-to-end validation. Incorporates
GitHub App auth model from the Phase 1 implementation pack.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Item 4 (Foundry model): deployment name `claude-opus-4-8`, referenced by
  agent-runner.js via @azure/ai-projects; pin specific version, not alias
- Item 5 (GHA auth): corrected Phase 6.3 from OIDC to existing pattern —
  azure/login@v3.0.0 with AZURE_CREDENTIALS service principal JSON stored
  at environment level, matching deploy-fiona-slack-container.yml pattern
- Item 6 (Slack webhook): added slack-webhook-url to Phase 0.3 Key Vault
  secrets table and updated Bicep app setting to Key Vault reference
- Open Items table updated to show resolved vs pending status

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve agent-layer contradiction, Durable timeout risk, MCP auth gap,
idempotency, and branch naming; correct Azure resource names from az lookups.

- Phase 0.1: confirmed fiona-kv-bronze, fiona-db-dev-cosmos (edfi-fiona-rg);
  new Foundry hub/project created in edfi-fiona-rg (single RG, no cross-RG)
- New Phase 3.0 agent-layer spike gates Foundry vs direct-Anthropic
- Phase 4 orchestrator restructured: owns GHA polling via df.createTimer;
  agent dispatches then yields (stay on Consumption plan)
- MCP tool server: AAD bearer-token auth baseline, function-key fallback
- Deterministic Durable instanceId for idempotency; purge-and-rerun on terminal
- Orchestrator-computed branch name agent/issue-{n}-{slug}
- Minor fixes: secret count 3->4, github-client single-installation lookup,
  local.settings example vars, GitHubWebhookReceiver.test.js in checklists

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Spike (doc-level, no provisioning) confirmed claude-opus-4-8 is GA in the
Foundry catalog (Anthropic/GlobalStandard) in eastus2. Decision: Path B —
Claude hosted on Microsoft Foundry via @anthropic-ai/foundry-sdk with a
self-owned tool-use loop and managed-identity Entra ID auth.

- Drop @azure/ai-projects / Azure AI Agent Service; agent-runner.js owns the loop
- Foundry resource pinned to eastus2 (Claude region constraint); single RG
- No remote MCP server: 9 GitHub handlers dispatched in-process (McpToolServer
  not built; MCP-endpoint AAD auth concern moot)
- No anthropic-api-key secret (3 KV secrets); MI gets Cognitive Services User;
  new settings ANTHROPIC_FOUNDRY_BASE_URL + CLAUDE_DEPLOYMENT_NAME
- Phase 3.0 reframed GATE -> RESOLVED; live tool round-trip folds into 3.1
- ai-foundry.bicep adopted (base on Azure-Samples/claude starter kit)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Provisions: Functions app (Consumption, Node 22, system-assigned MI),
AI Foundry hub (eastus2, claude-opus-4-8 deployment), Cosmos DB
agent-runs container, RBAC role assignments (KV Secrets User, Cosmos
Data Contributor, Cognitive Services User). Also updates Phase 0.5
local.settings.json.example to match production app settings exactly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Review fixes:
1. Cosmos DB Built-in Data Contributor is a data-plane SQL role, not an
   Azure RBAC role. Replace the Microsoft.Authorization/roleAssignments
   grant with a Microsoft.DocumentDB sqlRoleAssignments resource on the
   existing fiona-db-dev-cosmos account, scoped to the account id.
2. Untrack infra/issue-to-pr/main.json (compiled ARM output) and add it
   to .gitignore.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements RS256 JWT generation using node:crypto, installation ID lookup,
installation access token exchange, and a 60-second-before-expiry in-memory
cache. All 5 required test cases pass (happy path, cache hit, cache refresh,
malformed key, 404 installation not found). No new runtime dependencies.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds src/lib/mcp-handlers/github-request.js (authenticated GitHub API helper
returning {status, data}; throws descriptive errors on non-2xx), plus handlers
read-issue, list-directory, read-file, write-file with full tests. Also fixes
pre-existing lint issues in webhook-validator.test.js and GitHubWebhookReceiver.test.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e-file tests

- github-request: attach numeric err.status to non-2xx errors so callers
  can branch on err.status === 404 instead of string-matching error messages
- write-file: replace err.message.includes('status 404') with err.status === 404
- read-file: use Buffer.byteLength for byte-accurate 1 MB secondary guard
- Add test/mcp-handlers/write-file.test.js covering update, new file, and oversize

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…validation-status, create-draft-pr, add-issue-comment

Implements the five remaining GitHub tool handlers with TDD test files.
All 45 tests pass; biome lint clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ry-sdk

Path B uses the Anthropic SDK against the Foundry endpoint; @azure/ai-projects
(Agent Service) is no longer used.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implement the self-owned Claude-on-Foundry tool-use loop for the
issue-to-PR pipeline:
- agent-tools.js: nine Anthropic tool defs + context-injecting dispatchTool
- run-store.js: Cosmos agent-runs create/update via DefaultAzureCredential
- foundry-client.js: mockable single-turn Foundry model call wrapper
- agent-runner.js: startAgentEdits + agentReactToResult; the loop yields on
  run_validation (returns dispatched without the tool_result) and resumes
  with the deferred validation conclusion

Tests mock the Foundry SDK, tool dispatch, and Cosmos; 64 tests pass, lint clean.

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

- agent-runner: pass disable_parallel_tool_use:true on every model call so
  run_validation is always the sole tool_use in its turn; add defensive guard
  that throws (-> failRun) if the invariant is somehow violated
- run-store: replace read-then-replace updateRunRecord with Cosmos patch()
  using set operations to avoid lost-update race and thin-document resurrection
- foundry-client: lazy-init DefaultAzureCredential and AnthropicFoundry client
  on first use (module-scoped cache) to avoid authenticating on import
- tests: assert tool_choice shape on every model call; test defensive guard
  routes to failed; update run-store tests to assert patch operations shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements WorkflowOrchestrator.js (generator-based Durable orchestrator)
and WorkflowActivities.js (5 activities: PostSlackNotification,
StartAgentEdits, GetValidationStatus, AgentReactToResult, PostIssueComment).
Threads agent state (messages/pendingToolUseId/context/dispatchedAt) between
activities; orchestrator is deterministic (no Date.now/fetch in body). Adds
`main` field to package.json for Azure Functions host discovery. 79/79 tests
pass, biome lint clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…OLLS give-up test

Strengthens the determinism scan (Math.random/process.env) with a positive
assertion that the poll timer fires at currentUtcDateTime+30s, and adds the
previously-untested MAX_POLLS give-up path. (Recovered from a stalled fix
agent; corrected its off-by-one in the queued-response fixture.)

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
User/reviewer guide of the auth layer, 9 GitHub tools, agent runner, and
orchestrator — each feature mapped to its unit tests for usability review
and merge confidence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- sanitizeInstanceId: replace non-[A-Za-z0-9_-] with '-' for deterministic instanceId
- slug: lowercase, collapse non-alnum runs to '-', trim edges, truncate to 30 chars
- Handler: getStatus check before startNew; no-op if non-terminal (Running/Pending/ContinuedAsNew/Suspended); purge+restart if terminal (Completed/Failed/Terminated/Canceled)
- Add branchName (agent/issue-{n}-{slug}) to orchestration input
- Pass explicit instanceId to startNew; log uses computed id
- Tests: 13 new cases (idempotency, terminal re-run, branch slug, messy title, truncation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add three workflow files for the issue-to-pr pipeline:
- agent-execution.yml: repository_dispatch handler for agent-triggered validation
- on-pullrequest-issue-to-pr.yml: build/test/CodeQL PR gate
- deploy-issue-to-pr-function.yml: IaC-first deploy to Azure on push to main

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…able

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Monitoring, orchestration inspection, idempotent retry via re-label,
debugging (in-process tools / Foundry agent), and common failure modes —
grounded in the Path B implementation.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Defense-in-depth from the branch security review (neither was an exploitable
finding, both tighten the auth gate before deploy):
- webhook-validator: reject when the secret is empty/undefined (an unresolved
  Key Vault reference can no longer be bypassed with an empty-key HMAC)
- receiver: enforce AGENT_ALLOWED_REPOS (comma-separated owner/repo) when set;
  unset = accept all installed repos (backward compatible). Wired into
  functions.bicep (default Ed-Fi-Alliance-OSS/Fiona) + local.settings example.
- 5 new tests (98 total); lint clean; bicep compiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant