Issue readiness and completion audits¶
Editions: OSS, Cloud, Enterprise. Unless stated otherwise, everything on this page ships in OSS.
The lifecycle controller connects triage output, authorized implementation pickup, and an independent audit after a merged PR closes an issue. GitHub is the first provider adapter. Other providers fail explicitly until an authoritative merge linkage adapter is installed.
Configure a project¶
Clone Issue Refinement and Readiness and Merged Issue Completion Audit
from the preset catalog. Scope each flow to its tracker/project. The refinement
flow can follow normal triage; its structured result is a proposal, not permission
to implement. The audit flow subscribes to issue.closed.
Configure project.settings.issue_lifecycle through the existing project settings
API, using the account's actual flow IDs:
{
"ready_enabled": true,
"ready_label": "<existing-project-readiness-label>",
"implementation_flow_id": "<implementation-flow-uuid>",
"audit_flow_id": "<audit-flow-uuid>",
"create_follow_ups": false
}
The implementation flow must select an operator-approved environment profile
with named test_commands. Readiness consumes the environment capability
service; it does not define image setup or replace the pre-push verification
gate. Runtime setup still performs its own preflight before running an agent.
Missing profiles, unsupported runner protocols and missing commands block pickup.
The audit flow requires exactly one repository in git_clone_config.repositories,
with the scoped project_id, cloning enabled and create_pull_request: false.
Its MCP tool/server lists must be empty. It starts a fresh conversation, does not
restore an implementation session, and the verified merge SHA is carried in
payload.sha for the existing runner checkout path. Configure its environment
profile with the relevant checks and dependency services for the application.
Refine and authorize¶
All endpoints below require tenant authentication and existing issue/flow
permissions. Paths are relative to /api/v1/issues/{issue_uuid}/lifecycle.
GET /returns the live issue scope fingerprint and operation history.POST /refinestores aReadinessContract, either submitted directly or produced by the refinement flow. Include the liveissue_revision, problem, user outcome, constraints, non-goals, ordinary assumptions, materially blocking decisions, conflicts, observable criteria with stable IDs, code/test entry points, dependency issue numbers, environment profile and command names.- Review blockers and the proposal.
POST /readywith{"issue_revision":"<fingerprint>"}rechecks the issue, dependencies and environment and adds the explicitly configured existingready_labelonce under project policy. No default label vocabulary is assumed.
Labels are added, not replaced: existing project labels retain their ownership. The normal label webhook triggers implementation. A durable pickup record binds that transition to one execution; repeated delivery reuses the same execution. Follow-up issues never inherit the readiness label or bypass this gate.
When creating a fresh authorized pickup, the controller can attach a stored triage
assessment as lifecycle_pickup.triage_context. It loads the packet through
account-scoped records and checks its issue revision, current project policy and
source flow configuration. An applicable packet has status available; otherwise
the context is explicitly unknown with no packet. Incoming event fields cannot
supply this assessment. A reused execution retains its original context, even if
a newer assessment is later stored. Dispatch retries recheck pickup readiness;
they do not refresh the execution's frozen assessment or claim that its source
flow and catalogue were revalidated. The implementation preset treats the packet
as evidence to recheck against the current issue and checkout, never permission
to dispatch or change scope. The readiness policy and explicit approval above
remain required.
Pickup also checks the issue's current complexity labels against the packet's recorded family. A changed, removed or conflicting classification makes the packet inapplicable; unrelated human labels do not invalidate it. This check uses the fresh issue snapshot without scanning the project's label catalogue. Catalogue completeness in the packet describes the assessment-time catalogue; pickup does not claim to have revalidated subsequent repository label definitions.
Triage records both the revision it assessed and the verified revision after its managed issue update. Pickup uses that resulting full title/body revision. This mapping permits reuse after a managed assessment or factual title improvement without excluding human requirements from the readiness fingerprint.
The scope fingerprint covers title and description, excluding automation's
comments and labels. GitHub does not offer a conditional label-write API. The
provider rereads scope immediately before labeling, then the controller checks
again afterward and before dispatch. A racing scope edit is recorded as
needs_reconciliation; it cannot silently start a new implementation. If no
execution was created, refine the current revision and explicitly submit /ready
again. The controller rechecks scope and blockers and archives the superseded
authorization before replacing it. Stale revisions cannot authorize a pickup.
Once an execution exists, /ready returns pickup_reconciliation_required and
preserves that execution. Finish or cancel active work through the existing
execution controls; scope edits never retarget a running conversation. Refine
the revised scope, review the proposal, then explicitly call
POST /pickup/reconcile with issue_revision, previous_execution_id, and a
nonempty review reason. This requires both issue-edit and flow-execution
permissions. The named previous execution must be terminal, the revision must
be current and changed, and all readiness checks must pass again.
The controller archives the old authorization with its execution link and creates
a distinct authorization for the revised scope. It dispatches one fresh execution
directly because the readiness label may already be present. It never removes
and re-adds labels to manufacture another webhook. Repeating the same request or
receiving duplicate issue events reuses the same authorization/execution, including
a retry after dispatch failure. A missing worker acknowledgment returns durable
dispatch_pending plus the execution ID; retrying the same reconciliation request
retries delivery without authorizing a second execution. Old execution prompts and
contracts remain intact.
Same-scope execution retries use the existing execution retry API. The
/audit/reconcile endpoint separately recovers audits.
Audit the actual merge¶
A completed issue state alone is insufficient. The provider loads the latest closure event, requires a closing commit, and verifies its association with a merged PR in the same repository. Manual closure without that evidence creates no audit. Multiple linked merged PRs remain in the evidence envelope; the final closing merge revision is the checkout and idempotency key.
The independent result maps every stored acceptance criterion to code, test
results and observations. The controller computes complete, gap or unknown.
Missing evidence, unchecked deployment requirements and subsequent issue scope
changes cannot produce a complete verdict. An issue with no stored acceptance
contract remains unknown rather than inventing criteria.
The controller upserts one marked audit comment, including issue and merge
revisions, contributing PRs, evidence, and the execution link. With
create_follow_ups: true, it searches existing issues and creates at most three
bounded confirmed defects/enhancements. Existing marked issues, or human-created
issues referencing the source issue and the same acceptance criterion, are reused.
Unknown findings remain in the audit. The original issue is never reopened.
HTTP handlers and lifecycle hooks run each complete operation in a worker, including synchronous CRUD calls and lazy ORM reads after commit. Provider I/O uses that worker's private loop. Flow dispatch returns to the persistent application loop, so locally dispatched background executions survive the request or hook finishing.
Per-issue PostgreSQL transaction locks serialize decisions, and unique operation
keys survive duplicate webhooks. Remote writes also use deterministic markers:
a retry after provider success and local rollback finds the original issue or
comment. POST /audit/reconcile retries pending dispatch or completed-result
publication after transient failures. Results are persisted before the terminal
hook runs, so reconciliation does not ask an agent to generate a second audit.
Verify deployment separately¶
Closure cannot establish which revision runs in an environment. Deployment criteria therefore produce a pending verification requirement and remain unknown. An authenticated CD/operator caller can subsequently record the deployed revision and trigger a separate, explicitly approved verification flow:
{
"deployment_targets": {
"staging": {
"flow_id": "<deployment-verification-flow-uuid>",
"approved_scope": ["Read preferences for the synthetic staging account"]
}
}
}
Add this to the project lifecycle policy. Configure the dedicated flow with
agent_config.lifecycle_kind: deployment_audit, an environment and exact project
checkout, and a prompt that verifies only the supplied approved scope and emits
the same audit-result contract as the merge preset. Its trigger types should be
empty: the authorized endpoint is the trigger.
Call POST /deployment/verify with merge_sha, configured target, exact
deployed_revision and a deployment_evidence URL identifying the deployment
record. The controller stores the record/scope, creates a separate conversation,
pins checkout to the deployed revision and publishes a separate audit comment.
Repeating the same target/revisions reuses that execution. The deployment record
is an assertion by the authenticated CD/operator caller; it must come from the
actual deployment system. The issue-close flow never probes production.
Bounded audit publication¶
Audit effects remain serialized per issue because marker lookup followed by
provider creation is not atomic. Marker scans stop as soon as a matching comment
or follow-up is found. Complete scans accept up to 10,000 results, with one extra
page used only to prove exhaustion. Larger responses fail closed before creation.
Publication has an eight-second provider-work deadline and a shared 128-request
budget, including lookups and mutations. Exhaustion rolls back the local operation
and leaves its persisted result available through /audit/reconcile; a prior
provider effect is recovered by marker on retry. The controller does not create a
new ticket when it cannot finish the deduplication lookup. Large repositories may
still require a later indexed or independently claimed publication path.