feat: add blast radius documentation (CM-1371) - #4462
Conversation
PR SummaryLow Risk Overview Also indexes the new ADR in Reviewed by Cursor Bugbot for commit 4dac3c6. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds an ADR describing the blast-radius pipeline’s workflow, ecosystem abstraction, local authentication, and deployment strategy.
Changes:
- Documents pipeline stages, scaling, and operational trade-offs.
- Adds ADR-0017 to the documentation index.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 8 comments.
| File | Description |
|---|---|
docs/adr/README.md |
Adds ADR-0017 to the index. |
docs/adr/0017-blast-radius-pipeline-architecture.md |
Documents the blast-radius architecture and operations. |
Suppressed comments (1)
docs/adr/0017-blast-radius-pipeline-architecture.md:93
- This parenthetical again makes the source download sound retryable, but only the agent loop has
MAX_ATTEMPTS = 3; each source is downloaded once. Please state the retry scope explicitly.
3. **Reachability** (`stages/reachabilityStage.ts`, 1 hour timeout) — downloads each
candidate's source (4 concurrent, up to 3 attempts each) and runs a **Claude Sonnet**
agent (`model: 'claude-sonnet-5'`) to judge whether the vulnerable symbol is actually
reachable, under the same tool/turn/timeout constraints as intel above. Persists a
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (7)
docs/adr/0017-blast-radius-pipeline-architecture.md:19
- This is not exclusive to the Public API:
backend/src/bin/scripts/blastRadiusLoadTest.tsis a developer CLI that startsanalyzeBlastRadiusworkflows directly. Scope the statement to production consumers and acknowledge the harness so the ADR does not contradict the documented local execution path.
(CM-1358). It is consumed exclusively through a public, asynchronous Public API
(`submitBlastRadiusJob(Batch)` / `getBlastRadiusJob(Batch)` in
`backend/src/api/public/v1/packages/`) — there is no CLI or batch-report consumer.
docs/adr/0017-blast-radius-pipeline-architecture.md:34
- Stage 3 is not the pipeline's only concurrent stage. The npm dependents scan calls
forEachWithConcurrencywithSCAN_CONCURRENCY, which defaults to 32 in production (dependentsScan.ts:19-20,287-290). Distinguish sequential stage execution from concurrency within stages.
parallelism between stages; the only concurrency in the whole pipeline is *within* stage 3
(up to 4 dependents downloaded/analyzed at once).
docs/adr/0017-blast-radius-pipeline-architecture.md:64
- The three attempts apply only to the Claude agent call. Source preparation/download happens before the retry loop, and a download failure immediately persists an error verdict (
reachabilityStage.ts:97-138). The diagram currently implies that downloads are retried.
R1["Download each dependent's source<br/>(4 concurrent, up to 3 attempts each)"]
R2["Claude Sonnet judges reachability<br/>of the vulnerable symbol"]
docs/adr/0017-blast-radius-pipeline-architecture.md:92
- This wording again applies the three attempts to source download, but only the agent query is retried; download failures return an error verdict without retry. It also presents the configurable concurrency as a fixed value.
3. **Reachability** (`stages/reachabilityStage.ts`, 1 hour timeout) — downloads each
candidate's source (4 concurrent, up to 3 attempts each) and runs a **Claude Sonnet**
agent (`model: 'claude-sonnet-5'`) to judge whether the vulnerable symbol is actually
docs/adr/0017-blast-radius-pipeline-architecture.md:110
- Public API submissions do not reuse an
analysisIdwhenforceis set: both single and batch handlers always callgenerateUUIDv4()before persisting the request. Therefore a forced resubmission cannot resume prior stage runs as documented here.
Each stage is independently resumable: it checks its own `stage_run` status first and
no-ops if already `succeeded`, so a retried workflow (Temporal-level retry, or a re-submitted
job reusing the same `analysisId` via `force`) skips whatever already completed. Stages run
docs/adr/0017-blast-radius-pipeline-architecture.md:287
- This binary distinction is inaccurate. PyPI and RubyGems consume persisted dependent versions; Go and Maven prefer persisted versions when present; Cargo resolves canonical metadata; and NuGet resolves its version. Documenting these variants matters for the new-ecosystem guidance.
- The `prepareSource` contract's asymmetry (npm has a pre-resolved tarball URL, every other
ecosystem resolves a version at reachability time) means a new ecosystem's author must
understand this distinction rather than copy any single existing ecosystem verbatim —
mitigated by the existing 7 implementations covering both shapes as reference examples.
docs/adr/0017-blast-radius-pipeline-architecture.md:162
- The linked PR moved one shared blast-radius
agent/runner.tsinto the shared library; the ecosystem intel files already imported that runner, and PR #4452's merged code did not yet contain the personal OAuth-token branch. Avoid attributing the complete current chain and removal of per-ecosystem auth duplication to that PR.
This resolution chain was centralized in `services/libs/anthropic-aws` in
`feat: centralize anthropic usage (CM-1357)` (#4452), replacing per-ecosystem duplicated
auth logic that previously lived alongside each `intel{Ecosystem}.ts`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (4)
docs/adr/0017-blast-radius-pipeline-architecture.md:111
- This repeats the wrong analysis cost column and overstates what the polling API reads. The API does not select or expose
total_cost_usd; it usesstatus='done'to expose results and returnscompleted_at. Please document the stored field separately from the API completion signal.
every stage's recorded `cost_usd` (`getStageRunsCost`) and finalizes the
`blast_radius_analyses` row (`completed_at`, `cost_usd`, `status='done'`). The public API's
`getBlastRadiusJob` polls exactly these fields, so this stage is required — it is the
docs/adr/0017-blast-radius-pipeline-architecture.md:292
- Adding an ecosystem requires more than this registry entry: the Public API has its own Zod ecosystem list and OpenAPI enums, and the worker's
SUPPORTED_ECOSYSTEMSmust also be extended. Documenting this as “only” three functions risks shipping worker support that the public API rejects or omits from its contract.
- Adding an 8th ecosystem requires only a new `EcosystemConfig` entry plus its 3 functions —
no changes to `workflows.ts`, `ecosystems.ts`'s dispatch logic, or the report/finalize stage.
docs/adr/0017-blast-radius-pipeline-architecture.md:83
- The diagram names a column that does not exist on
blast_radius_analyses.finalizeAnalysiswrites the aggregate tototal_cost_usd; leavingcost_usdhere makes the documented persistence contract incorrect.
This issue also appears on line 109 of the same file.
F2["Set completed_at, cost_usd, status = 'done'<br/>on blast_radius_analyses"]
docs/adr/0017-blast-radius-pipeline-architecture.md:167
- The PR description still presents
prepareSourceas an npm-versus-all-others split, while this section correctly documents that RubyGems and PyPI also require pre-resolved versions. Update the PR description so the stated architecture matches this ADR.
not a clean npm-vs-everyone-else split. npm and RubyGems require an already-resolved
`version`/`tarball_url` from the dependents stage and return `null` (→ `unclear` verdict)
if it's missing; PyPI is the same shape (`version` required, no resolution attempted). Go,
Maven, Cargo, and NuGet instead resolve a version when the dependents stage only recorded a
constraint (`dep.version ?? resolveXVersion(...)`), each against a different registry API.
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
9112bd5 to
03cf587
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
docs/adr/0017-blast-radius-pipeline-architecture.md:180
- The local worker does not load a generic
.envfile. Both the Compose service (scripts/services/blast-radius-worker.yaml:19-23,37-41) anddev:blast-radius-worker:localloadbackend/.env.override.local, so following this instruction can leave the OAuth token unavailable to the worker. Name the actual override file here.
set it in the local `.env`), forwarded to the agent SDK as `CLAUDE_CODE_OAUTH_TOKEN` —
docs/adr/0017-blast-radius-pipeline-architecture.md:165
- This section correctly documents that the strategy is not npm-versus-everyone-else, but the PR description still says every non-npm ecosystem resolves a version during reachability. RubyGems and PyPI instead require the recorded version, as shown here and in their
reachabilityConfig.tsimplementations. Please update the PR description to match the ADR.
not a clean npm-vs-everyone-else split. npm and RubyGems require an already-resolved
`version`/`tarball_url` from the dependents stage and return `null` (→ `unclear` verdict)
if it's missing; PyPI is the same shape (`version` required, no resolution attempted). Go,
Signed-off-by: Umberto Sgueglia <usgueglia@contractor.linuxfoundation.org>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (5)
docs/adr/0017-blast-radius-pipeline-architecture.md:117
- This repeats the wrong analysis column name and overstates the polling contract.
getAnalysisDetaildoes not select the total cost, andgetBlastRadiusJobusesstatusto determine completion; it only exposescompleted_atfrom this list.
`blast_radius_analyses` row (`completed_at`, `cost_usd`, `status='done'`). The public API's
`getBlastRadiusJob` polls exactly these fields, so this stage is required — it is the
step that makes a run visible as complete to the API. The name is a holdover from an
docs/adr/0017-blast-radius-pipeline-architecture.md:300
- The “only” claim omits a required change:
Ecosystemis derived fromSUPPORTED_ECOSYSTEMS, so an eighth ecosystem must be added there before its registry entry type-checks. Document that contract so the stated extension path is complete.
- Adding an 8th ecosystem requires only a new `EcosystemConfig` entry plus its 3 functions —
no changes to `workflows.ts`, `ecosystems.ts`'s dispatch logic, or the report/finalize stage.
docs/adr/0017-blast-radius-pipeline-architecture.md:83
- The diagram names a nonexistent analysis column.
finalizeAnalysiswrites the aggregate toblast_radius_analyses.total_cost_usd, so usingcost_usdhere gives readers the wrong schema.
This issue also appears on line 115 of the same file.
F2["Set completed_at, cost_usd, status = 'done'<br/>on blast_radius_analyses"]
docs/adr/0017-blast-radius-pipeline-architecture.md:183
- These variables configure Claude Platform on AWS, not Amazon Bedrock (
credentials.ts:23-32setsCLAUDE_CODE_USE_ANTHROPIC_AWSplus an Anthropic workspace/API key). Calling this Bedrock is operationally misleading because Bedrock uses a different authentication path.
1. Claude on AWS Bedrock via `CROWD_AKRITES_ANTHROPIC_AWS_{REGION,WORKSPACE_ID,API_KEY}` —
production path. This key is purchased through the AWS Marketplace (Bedrock), not a
docs/adr/0017-blast-radius-pipeline-architecture.md:172
- The PR description says every non-npm ecosystem resolves a version during reachability, but this ADR and the implementation show that RubyGems and PyPI require a version already recorded by the dependents stage. Update the PR description to reflect this three-way split rather than the npm-vs-everyone-else claim.
not a clean npm-vs-everyone-else split. npm and RubyGems require an already-resolved
`version`/`tarball_url` from the dependents stage and return `null` (→ `unclear` verdict)
if it's missing; PyPI is the same shape (`version` required, no resolution attempted). Go,
Maven, Cargo, and NuGet instead resolve a version when the dependents stage only recorded a
constraint (`dep.version ?? resolveXVersion(...)`), each against a different registry API.
Summary
Adds ADR-0017 documenting the final architecture of the blast-radius analysis pipeline
(
services/apps/packages_worker/src/blast-radius): the 4-stage Temporal workflow, how itwas generalized across 7 package ecosystems, the local-testing setup using a personal
Claude Code token, and the dedicated Kubernetes node pool / scaling strategy.
Changes
docs/adr/0017-blast-radius-pipeline-architecture.md, covering:Mermaid diagram showing the strictly sequential flow (no cross-stage parallelism).
aggregates cost and finalizes the
blast_radius_analysesrow for the polling API toread. Kept the existing
stage = 'report'naming for continuity with the DB enum,but documented it as a naming mismatch worth a low-risk follow-up rename.
EcosystemConfigregistry pattern (stages/ecosystems.ts) that generalizes all 7ecosystems (npm, go, maven, cargo, nuget, rubygems, pypi) behind one interface, and the
prepareSourceasymmetry (npm has a pre-resolved tarball URL vs. every other ecosystemresolving a version at reachability time) as the one genuinely ecosystem-specific axis.
claude setup-tokenandset
CROWD_AKRITES_CLAUDE_CODE_DEV_OAUTH_TOKENto run the full agent pipeline locallywithout AWS credentials.
pg-blast-radiusnode pool (vs. the sharedpg-packagespoolused by every other packages worker), sized for the CPU/memory-bursty agent workload,
and the horizontal (replicas) / vertical (resources +
NODE_OPTIONS) scaling headroomit enables independently of the rest of the packages pipeline.
docs/adr/README.mdindex with the new ADR-0017 entry.Type of change
JIRA ticket
CM-1371