Engineering Delivery
The agentic flow
Most work now starts with a spec and is built by an AI agent. A weak spec is then paid for again at every later step. The agentic flow gives that work five gates, so you can see where work passes first time and where it comes back. Your engineers keep working the way they do; Engineering Delivery reads the flow from what your tracker, GitHub and Claude Code already record, finds the gaps, and reports them.
The flow
Each gate is a status change a person makes, so your tracker records when work passed.
| Status | What happens | Gate on leaving |
|---|---|---|
| Backlog, Ready | Logged and ranked | |
| Spec, Spec Review | The engineer and AI write the spec; the engineer approves Claude's spec, and someone else approves a spec a person wrote | 1. Spec approved |
| Building, Validating | Agents build it test first; the engineer checks every acceptance criterion | 2. Build validated |
| In Review | Agent review, then one person, with checks passing | 3. Pull request approved |
| QA, Ready to Release | The test plan runs on staging | 4. QA passed |
| Released | In production for 14 days without a reopen or a bug it caused | 5. No issues after release |
| Canceled | Dropped |
Teams without staging skip QA. When a gate fails, move the ticket back to the stage where the problem started. No label is needed: Engineering Delivery reads every move back as a bounce, and a bounce back to Spec counts against gate 1 wherever it happened, which is how it shows where late rework really starts.
Your teams keep their own status names. In Engineering Delivery's Admin, map each of a project's statuses to a stage; a step a project does not have is simply left out.
Make Claude Code check in
Paste this into your organization's Claude Code managed settings, in claude.ai under Organization settings > Claude Code > Managed settings, or into a managed-settings.json that your device management deploys:
{
"env": {
"CLAUDE_CODE_ENABLE_TELEMETRY": "1",
"OTEL_METRICS_EXPORTER": "otlp",
"OTEL_LOGS_EXPORTER": "otlp",
"OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
"OTEL_EXPORTER_OTLP_ENDPOINT": "https://otel.solidafy.com",
"OTEL_EXPORTER_OTLP_HEADERS": "x-api-key=YOUR_KEY_FROM_SETUP",
"OTEL_LOG_TOOL_DETAILS": "1",
"OTEL_METRICS_INCLUDE_REPOSITORY": "true",
"OTEL_METRICS_INCLUDE_VERSION": "true",
"OTEL_METRICS_INCLUDE_ENTRYPOINT": "true",
"OTEL_LOG_MANAGED_SETTINGS": "1"
},
"permissions": {
"allow": [
"Bash(: solidafy-checkin *)"
]
},
"claudeMd": "## Solidafy check-ins\nWhen you work on a ticket, record each step the moment it happens by running this with the Bash tool, on its own:\n`: solidafy-checkin ticket=<KEY> event=<event> stage=<stage> repo=<owner/name> branch=<branch> commit=<short hash>` plus the extras below.\n\n- ticket: a real key from the branch, the conversation or the tracker. No key, no check-in.\n- stage: where the ticket is after the event: backlog, ready, spec, spec_review, building, validating, in_review, qa, ready_to_release, released, canceled, blocked (the closest to the team's own status).\n- events and their extras:\n - started; finished (you stop work on it this session)\n - stage_entered (moved forward): from_stage\n - bounced (moved back): from_stage, reason, attempt\n - tests_passed / tests_failed: tests\n - pr_opened: pr=<owner/repo#number>\n - sub_task_created: the sub-task as ticket, plus parent_ticket\n- reason: spec_gap, bug, test_failure, review_change, scope_change, env. attempt: 1 the first time. ac: criteria met/total, whenever the ticket leaves validating. tests: passed/run. blocked_on, when blocked: review, decision, dependency, access, env. spec_method=<name> on entering spec_review (e.g. superpowers, grill-me).\n\nExample: `: solidafy-checkin ticket=SOL-1887 event=bounced stage=building from_stage=validating reason=test_failure attempt=1 ac=4/6 repo=acme/web branch=sol-1887-login commit=8d1e004`\n\nShapes: ticket and parent_ticket match [A-Z][A-Z0-9]+-[0-9]+ (sol-1887 becomes SOL-1887); other words come from the lists above; spec_method is letters, digits, dashes, colons; attempt 1 to 99; ac and tests are n/n; repo owner/name and pr owner/repo#number use letters, digits, dots, dashes, underscores; branch those plus /, up to 100; commit is 7 to 40 of 0-9a-f.\nIf repo, branch or commit is missing or does not have its exact shape, leave that field out and still check in. If any other value does not have its exact shape, do not check in. Never put spaces inside a value, quotes, $, `, ;, |, &, <, >, brackets, new lines or anything else in the command."
}
What each part does:
envsends Claude Code's telemetry to Solidafy, as on Connecting Claude Code, withOTEL_LOG_TOOL_DETAILSon so check-ins arrive.permissionslets Claude record a check-in without asking anyone.claudeMdis the rule every session reads: when you work on a ticket, record each step with one short command, such as: solidafy-checkin ticket=SOL-1887 event=stage_entered stage=building from_stage=spec repo=acme/web branch=sol-1887-login commit=3f9c2ab. The command does nothing on the machine; the line reaches Solidafy through the telemetry. Work without a ticket is not checked in.
Each check-in names the repository, branch and commit it happened on, because Claude Code's own telemetry does not send them from a terminal session. When work goes back it says why (reason, such as spec_gap or test_failure) and which try it was (attempt). At build validation it says how many acceptance criteria were met (ac=5/6), after tests how many passed (tests=41/42), and when work is blocked what it waits on (blocked_on). Every value has a fixed shape, so no spec text, code or names leave the machine, and a branch or ticket name cannot run anything.
Before you paste:
- Merge, do not replace. If your managed settings already hold other keys, add these to them. Pasting over them removes your existing policy.
- The key goes right after
x-api-key=. Create it in Engineering Delivery's Setup, in the Claude Code telemetry card, and paste only the key, so the line readsx-api-key=followed by the key and nothing else. - Use Claude Code 2.1.260 or later. Earlier versions ask for approval of the rule, or ignore it.
- Restart once. Each engineer approves the telemetry settings once when Claude Code asks, and restarts Claude Code.
What is kept: tool details send every command Claude runs, its tool arguments and Claude's description of each, and Solidafy stores them as they arrive. Engineering Delivery reads only the check-in lines and your usage numbers. A filter that drops everything but the check-ins on arrival is coming. If you send telemetry to your own Azure instead (option B on Connecting Claude Code), all of it is stored there, so leave OTEL_LOG_TOOL_DETAILS off for that option.
Set up your tracker
Linear: in Team settings > Workflow, add the statuses above in order (Backlog as Backlog, Ready as Unstarted, Spec to Ready to Release as Started, Released as Completed). In the GitHub integration, move issues to In Review when a pull request opens and to QA when it merges.
Jira: create the same statuses (Backlog and Ready in To Do, Spec to Ready to Release in In Progress, Released and Canceled in Done), with transitions one step forward plus the moves back.
Keep the status names as written; capitals do not matter, so an existing In Review status works as it is.
Every pull request names its ticket
Put the ticket key at the start of each pull request's title (SOL-1887 Short summary) and Fixes SOL-1887 as the first line of its body, so the pull request shows on the ticket and Engineering Delivery links them. Use Part of SOL-1887 when more pull requests follow.
To enforce it, add this workflow to each repository as .github/workflows/ticket-link.yml and make Ticket link a required check. It fails a pull request without a ticket key, and needs no permissions or secrets.
# .github/workflows/ticket-link.yml
name: Ticket link
on:
pull_request:
types: [opened, edited, reopened, synchronize]
permissions: {}
jobs:
ticket-key:
runs-on: ubuntu-latest
steps:
- name: The pull request names its ticket
env:
TITLE: ${{ github.event.pull_request.title }}
BRANCH: ${{ github.head_ref }}
run: |
# The title starts with the key (SOL-123 ...), or a branch part starts with it (feature/sol-123-...).
if printf '%s\n' "$TITLE" | grep -Eq '^[A-Z][A-Z0-9]+-[0-9]+([^0-9]|$)' ||
printf '%s\n' "$BRANCH" | grep -Eiq '(^|/)[a-z][a-z0-9]+-[0-9]+([^0-9]|$)'; then
echo "Ticket key found."
else
echo "::error::Put the ticket key, for example SOL-123, in the pull request title or branch."
exit 1
fi
Or download it straight into a repository:
mkdir -p .github/workflows
curl -fsSL https://www.solidafy.com/agentic-flow/ticket-link.yml -o .github/workflows/ticket-link.yml
Where it shows in Engineering Delivery
Engineering Delivery reads the flow from your connected tracker, GitHub and Claude Code: each stage's time, how often each gate passes first time, where work bounced back, and which sessions, pull requests and AI cost belong to each ticket. See Setting it up.
