Skip to content

feat(test): add failure triage for batch root-cause grouping#44

Open
SahilRakhaiya05 wants to merge 8 commits into
TestSprite:mainfrom
SahilRakhaiya05:feat/failure-triage
Open

feat(test): add failure triage for batch root-cause grouping#44
SahilRakhaiya05 wants to merge 8 commits into
TestSprite:mainfrom
SahilRakhaiya05:feat/failure-triage

Conversation

@SahilRakhaiya05

@SahilRakhaiya05 SahilRakhaiya05 commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

This PR adds a new CLI command:

testsprite test failure triage --project <project-id> --output json

The command groups failed tests into root-cause clusters instead of returning a flat list of unrelated failures. This helps agents and developers quickly identify the highest-priority issue, investigate one representative test first, and avoid fixing the same underlying problem multiple times.

Each cluster includes:

  • A human-readable label
  • A representative test to investigate first
  • All affected test IDs
  • A confidence score
  • A fix priority, where lower means higher priority

The command uses existing TestSprite APIs only, so no backend changes are required. It fetches lightweight failure summary data per test and does not download screenshots, videos, or full failure bundles.


Problem

Today, when a batch run fails many tests, the CLI and agents only see individual failures:

testsprite test run --all --project proj_xxx --wait
# → tests failed, each reported separately

This makes agents and developers:

  1. Review many separate failed rows.
  2. Guess which failure to investigate first.
  3. Download multiple failure bundles.
  4. Often fix the same underlying issue more than once.

The CLI already has strong per-test analysis through test failure get, test failure summary, rootCauseHypothesis, recommendedFixTarget, and failureKind.

What was missing is cross-test grouping after a batch failure.


Solution

test failure triage works in three steps:

  1. Lists all failed tests for a project.
  2. Fetches failure summaries for each failed test in parallel.
  3. Groups failures client-side using deterministic heuristics.

The grouping algorithm uses the following signals:

  • Shared recommendedFixTarget.reference
  • Environment-wide failureKind, such as network_timeout or infra
  • Similar rootCauseHypothesis
  • Singleton fallback when no grouping signal exists

Clusters are ordered by fix priority first, then by member count.


Command surface

testsprite test failure triage --project <project-id> [options]

Supported options:

  • --project <id> — required project ID
  • --type frontend|backend — filter failed tests by type
  • --filter <substr> — filter tests by name substring
  • --max-concurrency <n> — parallel summary fetches, default 5
  • --output json|text — machine or human output
  • --endpoint-url <url> — override API host

Also supports global flags such as --dry-run, --profile, --verbose, and --debug.


Recommended agent workflow

# 1. Batch run fails
testsprite test run --all --project <project-id> --wait --output json

# 2. Triage failures into clusters
testsprite test failure triage --project <project-id> --output json

# 3. Download one bundle from the highest-priority representative test
testsprite test failure get <representativeTestId> --out ./.testsprite/failure

# 4. Fix the issue and rerun the representative first
testsprite test rerun <representativeTestId> --wait

# 5. Run full regression after the representative passes
testsprite test rerun --all --project <project-id> --wait

The agent skill was also updated to recommend triage before downloading bundles when multiple tests fail.


Implementation details

Added new grouping logic in:

src/lib/failure-triage.ts

This includes:

  • normalizeHypothesis()
  • computeGroupKey()
  • pickRepresentativeTestId()
  • computeClusterConfidence()
  • computeFixPriority()
  • buildFailureClusters()
  • renderFailureTriageText()

Added command implementation in:

src/commands/test.ts

The command validates inputs, paginates failed tests, applies filters, fetches summaries with bounded concurrency, handles stale failed rows, and emits JSON or text output through the existing output system.


Test coverage

This PR adds 18 automated tests:

  • 11 unit tests for src/lib/failure-triage.test.ts
  • 7 integration tests for src/commands/test.test.ts

Coverage includes:

  • Grouping by fix target
  • Grouping by failure kind
  • Grouping by hypothesis
  • Singleton fallback
  • Representative test selection
  • Cluster confidence and priority
  • Empty projects
  • Stale failed rows
  • Missing project validation
  • JSON and text output
  • Help surface

Future work

Out of scope for this PR:

  • Native GET /projects/{id}/failures/clusters API
  • Semantic embedding clustering on rootCauseHypothesis
  • BE wave/cascade graph integration
  • --rerun-representatives --wait orchestration flag

Checklist

  • New command with JSON and text output
  • Uses existing APIs only
  • Deterministic grouping, no CLI LLM calls
  • 18 automated tests added
  • Typecheck, lint, and build pass
  • Documentation updated
  • Agent skill updated
  • Help snapshot added
  • Manual production API smoke test completed

The Pr solving issue #116

Summary by CodeRabbit

  • New Features

    • Added a new test failure triage command to group failed tests into root-cause clusters and show a representative test, affected test IDs, confidence, and fix priority.
    • Added support for filtering, concurrency limits, and a dry-run mode for reviewing results without fetching failure bundles.
  • Documentation

    • Updated the command reference, README, and troubleshooting guidance to explain when to use triage and how to handle single vs. multiple test failures.

@zeshi-du

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR adds a client-side "test failure triage" CLI command that clusters a project's failed tests by root cause using existing analysis fields (fix target reference, failure kind, hypothesis) without downloading failure bundles. Includes a new clustering library, CLI wiring, dry-run sample data, tests, and documentation updates.

Changes

Failure Triage Feature

Layer / File(s) Summary
Clustering library
src/lib/failure-triage.ts
New module with data types (FailureTriageMember, FailureTriageCluster, FailureTriageResult, FailureTriageInput, GroupKeyResult) and functions (normalizeHypothesis, computeGroupKey, pickRepresentativeTestId, computeClusterConfidence, computeFixPriority, buildFailureClusters, renderFailureTriageText) implementing deterministic clustering heuristics.
Clustering library tests
src/lib/failure-triage.test.ts
Vitest coverage for all clustering helpers, grouping precedence, representative selection, confidence/priority scoring, cluster ID collision handling, and empty-input behavior.
Dry-run sample data
src/lib/dry-run/samples.ts, src/lib/dry-run/samples.test.ts
Adds sampleFailureTriageResult canned response with two clusters and matching tests.
CLI command implementation
src/commands/test.ts
Adds DEFAULT_TRIAGE_CONCURRENCY, exported runFailureTriage, and wires the test failure triage subcommand with --project, --type, --filter, --max-concurrency flags, dry-run support, pagination, concurrent failure-summary fetches, NOT_FOUND skip handling, and clustering/rendering.
CLI command tests
src/commands/test.test.ts, test/help.snapshot.test.ts
Tests the new subcommand surface, help output, and runFailureTriage behavior across JSON/text/dry-run modes, filtering, validation, and skip handling.
Docs, changelog, and skill guidance
README.md, DOCUMENTATION.md, CHANGELOG.md, skills/testsprite-verify.skill.md
Documents the new command and updates verification skill guidance to trigger triage for multi-failure cases while single failures go straight to artifact download.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI
    participant runFailureTriage
    participant TestSpriteAPI
    participant buildFailureClusters

    User->>CLI: testsprite test failure triage --project <id>
    CLI->>runFailureTriage: parsed options (type, filter, maxConcurrency)
    alt dry-run mode
        runFailureTriage-->>CLI: sampleFailureTriageResult (canned)
    else live mode
        runFailureTriage->>TestSpriteAPI: fetch failed tests (paginated, filtered)
        loop per failed test (bounded concurrency)
            runFailureTriage->>TestSpriteAPI: GET /tests/{testId}/failure/summary
            alt NOT_FOUND
                TestSpriteAPI-->>runFailureTriage: 404 NOT_FOUND
                runFailureTriage->>runFailureTriage: record skipped test
            else success
                TestSpriteAPI-->>runFailureTriage: failure summary fields
            end
        end
        runFailureTriage->>buildFailureClusters: FailureTriageInput[]
        buildFailureClusters-->>runFailureTriage: FailureTriageResult (clusters, summary)
    end
    runFailureTriage-->>CLI: render text or JSON
    CLI-->>User: cluster report
Loading

Possibly related issues

Suggested reviewers: ruili-testsprite, zeshi-du

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 53.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main addition: failure triage for grouping failed tests by root cause.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

Add testsprite test failure triage --project <id> to group failed tests into root-cause clusters using existing M2.1 analysis fields. Returns a representative test per cluster, confidence score, and fix priority without downloading failure bundles.

Includes grouping library, command wiring, unit/integration tests, docs, CHANGELOG entry, agent skill update, and help snapshot.
…iage

- Fix test failure triage help snapshot default value quoting

- Run prettier on changed files

- Add filter and max-concurrency validation tests

- Remove draft issue/PR markdown files from repo

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
src/lib/failure-triage.test.ts (1)

1-212: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider adding coverage for buildClusterLabel, renderFailureTriageText, and representative tie-breaks.

Current tests exercise the grouping/priority/confidence heuristics well but skip buildClusterLabel/renderFailureTriageText entirely and only test the "prefers hypothesis" branch of pickRepresentativeTestId (not the recency/testId tie-breaks). A test asserting cluster.label is derived consistently with cluster.canonicalRootCause/representativeTestId for a multi-member hypothesis cluster would have caught the label/representative mismatch noted in failure-triage.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/failure-triage.test.ts` around lines 1 - 212, Add tests covering the
missing failure-triage paths: verify buildClusterLabel and
renderFailureTriageText produce labels/text that stay aligned with
cluster.canonicalRootCause and representativeTestId, especially for multi-member
hypothesis clusters. Also extend pickRepresentativeTestId coverage to include
the recency and testId tie-break cases, since the current suite only checks the
hypothesis-preference branch. Use the existing failure-triage.test.ts helpers
and symbols buildFailureClusters, buildClusterLabel, renderFailureTriageText,
and pickRepresentativeTestId to locate the relevant assertions.
src/commands/test.test.ts (1)

3454-3482: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a command-level missing---project test for test failure triage.

These cases only call runFailureTriage() directly, so they won't catch the new .requiredOption('--project') parse path returning Commander's generic failure instead of the documented validation exit code. Please exercise the CLI command itself here and assert the missing-project exit behavior.

As per path instructions, "Tests must be deterministic and offline" and "Cover new behavior, including error and exit-code paths."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/test.test.ts` around lines 3454 - 3482, Add a command-level test
for missing --project in test failure triage by exercising the CLI parser path
instead of only calling runFailureTriage(), so the new
.requiredOption('--project') behavior is covered. Update the existing validation
tests in test.test.ts to invoke the command entrypoint for the missing-project
case and assert the exact exit behavior/exit code returned by Commander, while
keeping the test deterministic and offline. Use the existing runFailureTriage
and test failure triage command symbols to locate the right place to extend
coverage.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/commands/test.ts`:
- Around line 4067-4141: The `opts.dryRun` branch in `test.ts` is hard-coding a
triage response that should live in the shared dry-run sample registry instead.
Move this canned `FailureTriageResult` payload into
`src/lib/dry-run/samples.ts`, update the `dry-run` sample lookup/use path so the
command reads the fixture from the registry, and add or adjust the corresponding
test to cover the shared sample shape and keep `renderFailureTriageText` output
aligned.
- Around line 8920-8926: The triage command currently uses Commander’s
requiredOption for --project, which short-circuits before runFailureTriage() and
bypasses the VALIDATION_ERROR envelope and exit-5 mapping. Update the command
definition in failure.command('triage') to make --project a plain option, then
keep validation inside requireProjectId() so the existing createTestCommand()
error-path behavior and documented exit code handling remain intact.

In `@src/lib/failure-triage.ts`:
- Around line 191-212: buildClusterLabel is re-selecting its own representative
member, which can diverge from the representative already chosen in
buildFailureClusters via representativeTestId. Update buildClusterLabel to
accept and use that existing representative (or look it up by ID) instead of
falling back to members[0], so hypothesis/fix-target labels always come from the
same test as cluster.canonicalRootCause. Keep the existing label formatting
logic, but ensure the rep/testName/rootCauseHypothesis source is consistent with
the cluster’s chosen representative.
- Around line 214-221: `slugifyClusterId` is truncating cluster IDs in a way
that can cause collisions between distinct clusters. Update `slugifyClusterId`
in `failure-triage` to keep the readable slug but append a short stable
disambiguator, such as a hash of the full `groupKey`, so different `hyp:`
entries do not end up with the same `clusterId` after truncation. Make sure the
`groups`/cluster creation flow continues to use this new unique `clusterId`
shape consistently wherever `normalizeHypothesis` and downstream consumers rely
on it.

---

Nitpick comments:
In `@src/commands/test.test.ts`:
- Around line 3454-3482: Add a command-level test for missing --project in test
failure triage by exercising the CLI parser path instead of only calling
runFailureTriage(), so the new .requiredOption('--project') behavior is covered.
Update the existing validation tests in test.test.ts to invoke the command
entrypoint for the missing-project case and assert the exact exit behavior/exit
code returned by Commander, while keeping the test deterministic and offline.
Use the existing runFailureTriage and test failure triage command symbols to
locate the right place to extend coverage.

In `@src/lib/failure-triage.test.ts`:
- Around line 1-212: Add tests covering the missing failure-triage paths: verify
buildClusterLabel and renderFailureTriageText produce labels/text that stay
aligned with cluster.canonicalRootCause and representativeTestId, especially for
multi-member hypothesis clusters. Also extend pickRepresentativeTestId coverage
to include the recency and testId tie-break cases, since the current suite only
checks the hypothesis-preference branch. Use the existing failure-triage.test.ts
helpers and symbols buildFailureClusters, buildClusterLabel,
renderFailureTriageText, and pickRepresentativeTestId to locate the relevant
assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c17d6b2c-df3b-47d1-88af-90fadf5de85a

📥 Commits

Reviewing files that changed from the base of the PR and between 3ab8136 and 9f7c9e4.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (11)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • DOCUMENTATION.md
  • README.md
  • skills/testsprite-verify.skill.md
  • src/commands/test.test.ts
  • src/commands/test.ts
  • src/lib/failure-triage.test.ts
  • src/lib/failure-triage.ts
  • test/help.snapshot.test.ts
  • vitest.config.ts

Comment thread src/commands/test.ts
Comment thread src/commands/test.ts
Comment thread src/lib/failure-triage.ts
Comment thread src/lib/failure-triage.ts
- Move --dry-run payload to sampleFailureTriageResult in samples.ts
- Use .option for --project so requireProjectId emits exit 5
- Build cluster labels from the chosen representative test
- Append short hash suffix to clusterId to avoid slug collisions

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/commands/test.ts (1)

4520-4562: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Stop fan-out after the first fatal error.
reject(err) still falls through to inFlight-- and startNext(), so a non-NOT_FOUND failure can keep launching more /failure/summary requests after the operation has already failed. Add an abort guard or return immediately on fatal errors.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/test.ts` around lines 4520 - 4562, Stop launching more triage
requests after a fatal `/failure/summary` error. In the `startNext` flow inside
`test.ts`, a non-`NOT_FOUND` failure currently calls `reject(err)` and then
still decrements `inFlight` and recurses, so add a fatal-error/aborted guard or
return immediately from the `.catch` path after rejecting. Make sure
`startNext`, the `inFlight` bookkeeping, and the `triageInputs` fan-out all stop
once a real error has been hit.
🧹 Nitpick comments (1)
src/lib/failure-triage.test.ts (1)

213-244: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test doesn't actually distinguish representative-based label from insertion-order label.

Both t_first and t_recent use the identical sharedHyp string, so cluster.label/canonicalRootCause would equal sharedHyp regardless of which member is picked as representative — even under a buggy insertion-order implementation. The test name promises to catch that regression but the assertions can't actually detect it.

♻️ Suggested fix: use distinct hypothesis text per member
   it('labels hypothesis clusters from the representative test, not map-insertion order', () => {
     const sharedHyp = 'Shared login validation failure across checkout flows.';
+    const olderHyp = 'shared login validation failure across checkout flows.'; // normalized-equal groupKey, distinct display text
     const result = buildFailureClusters('proj_hyp', [
       makeInput({
         testId: 't_first',
         updatedAt: '2026-06-20T00:00:00.000Z',
         summary: {
           status: 'failed',
           failureKind: 'assertion',
           snapshotId: 'snap_first',
-          rootCauseHypothesis: sharedHyp,
+          rootCauseHypothesis: 'Shared login validation failure across checkout flows (older wording).',
           recommendedFixTarget: null,
         },
       }),

Note the raw hypothesis text must still normalize to the same groupKey (via normalizeHypothesis) for both members to land in the same cluster, so any replacement text needs to collapse to the same key while differing enough in display casing/whitespace to prove selection-order matters, or simply differ in a way that keeps grouping intact while making label/canonicalRootCause distinguishable per representative.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/failure-triage.test.ts` around lines 213 - 244, The cluster-label
test in failure-triage.test.ts does not prove representative-based labeling
because both inputs share the same hypothesis text, so a buggy insertion-order
implementation would still pass. Update the test data in buildFailureClusters to
give each member a distinct rootCauseHypothesis that still normalizes to the
same groupKey via normalizeHypothesis, then keep the assertions on
representativeTestId, label, and canonicalRootCause so the chosen representative
actually affects the expected value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/dry-run/samples.ts`:
- Around line 355-419: The canned dry-run fixtures are using human-readable
cluster IDs instead of the hashed wire-format produced by
buildFailureClusters(). Update the sample data in samples.ts so the clusterId
values follow the cluster_${slug}_${8hex} pattern, and make the corresponding
dry-run test expect the same hashed shape. Use the existing groupKey/fix-target
entries to keep the fixture semantics unchanged while aligning the IDs with the
real response format.

---

Outside diff comments:
In `@src/commands/test.ts`:
- Around line 4520-4562: Stop launching more triage requests after a fatal
`/failure/summary` error. In the `startNext` flow inside `test.ts`, a
non-`NOT_FOUND` failure currently calls `reject(err)` and then still decrements
`inFlight` and recurses, so add a fatal-error/aborted guard or return
immediately from the `.catch` path after rejecting. Make sure `startNext`, the
`inFlight` bookkeeping, and the `triageInputs` fan-out all stop once a real
error has been hit.

---

Nitpick comments:
In `@src/lib/failure-triage.test.ts`:
- Around line 213-244: The cluster-label test in failure-triage.test.ts does not
prove representative-based labeling because both inputs share the same
hypothesis text, so a buggy insertion-order implementation would still pass.
Update the test data in buildFailureClusters to give each member a distinct
rootCauseHypothesis that still normalizes to the same groupKey via
normalizeHypothesis, then keep the assertions on representativeTestId, label,
and canonicalRootCause so the chosen representative actually affects the
expected value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ca395e38-f7b5-4a21-a4f2-3e16c35cdeb5

📥 Commits

Reviewing files that changed from the base of the PR and between 9f7c9e4 and 4432c56.

⛔ Files ignored due to path filters (1)
  • test/__snapshots__/help.snapshot.test.ts.snap is excluded by !**/*.snap, !**/*.snap
📒 Files selected for processing (10)
  • CHANGELOG.md
  • DOCUMENTATION.md
  • README.md
  • src/commands/test.test.ts
  • src/commands/test.ts
  • src/lib/dry-run/samples.test.ts
  • src/lib/dry-run/samples.ts
  • src/lib/failure-triage.test.ts
  • src/lib/failure-triage.ts
  • test/help.snapshot.test.ts
✅ Files skipped from review due to trivial changes (3)
  • README.md
  • CHANGELOG.md
  • DOCUMENTATION.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/help.snapshot.test.ts
  • src/lib/failure-triage.ts
  • src/commands/test.test.ts

Comment on lines +355 to +419
clusterId: 'cluster_kind_network_timeout',
label: 'Environment issue (network_timeout)',
groupKey: 'kind:network_timeout',
groupReason: 'failure_kind',
failureKind: 'network_timeout',
representativeTestId: 'test_dryrun_a',
memberTestIds: ['test_dryrun_a', 'test_dryrun_b'],
members: [
{
testId: 'test_dryrun_a',
testName: 'Dry-run checkout flow',
testType: 'frontend',
updatedAt: '2026-06-26T12:00:00.000Z',
status: 'failed',
failureKind: 'network_timeout',
snapshotId: 'snap_dryrun_a',
rootCauseHypothesis: null,
recommendedFixTarget: null,
},
{
testId: 'test_dryrun_b',
testName: 'Dry-run profile update',
testType: 'frontend',
updatedAt: '2026-06-26T12:01:00.000Z',
status: 'failed',
failureKind: 'network_timeout',
snapshotId: 'snap_dryrun_b',
rootCauseHypothesis: null,
recommendedFixTarget: null,
},
],
canonicalRootCause: null,
confidence: 0.88,
fixPriority: 1,
},
{
clusterId: 'cluster_ref_src_components_checkoutform_tsx_412',
label: 'Shared fix target: src/components/CheckoutForm.tsx:412',
groupKey: 'ref:src/components/CheckoutForm.tsx:412',
groupReason: 'fix_target',
failureKind: 'assertion',
representativeTestId: 'test_dryrun_c',
memberTestIds: ['test_dryrun_c'],
members: [
{
testId: 'test_dryrun_c',
testName: 'Dry-run submit checkout',
testType: 'frontend',
updatedAt: '2026-06-26T12:02:00.000Z',
status: 'failed',
failureKind: 'assertion',
snapshotId: 'snap_dryrun_c',
rootCauseHypothesis:
'Submit button is disabled because the credit-card field is empty.',
recommendedFixTarget: {
kind: 'code',
reference: 'src/components/CheckoutForm.tsx:412',
rationale: 'Disabled state originates from `isFormValid()`.',
},
},
],
canonicalRootCause: 'Submit button is disabled because the credit-card field is empty.',
confidence: 0.7,
fixPriority: 3,
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect the real clusterId construction to see if the hash suffix is unconditional.
ast-grep run --pattern 'clusterId: $_' --lang typescript src/lib/failure-triage.ts
rg -n -A5 -B5 'slugifyClusterId' src/lib/failure-triage.ts

Repository: TestSprite/testsprite-cli

Length of output: 958


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant implementation and sample lines.
sed -n '215,280p' src/lib/failure-triage.ts
printf '\n--- sample ---\n'
sed -n '355,419p' src/lib/dry-run/samples.ts

printf '\n--- expected cluster ids from group keys ---\n'
python3 - <<'PY'
import hashlib
keys = [
    'kind:network_timeout',
    'ref:src/components/CheckoutForm.tsx:412',
]
for key in keys:
    slug = ''.join(ch if ch.isalnum() else '_' for ch in key.lower())
    while '__' in slug:
        slug = slug.replace('__', '_')
    slug = slug.strip('_')
    h = hashlib.sha256(key.encode()).hexdigest()[:8]
    print(key, '=>', f'cluster_{slug}_{h}')
PY

Repository: TestSprite/testsprite-cli

Length of output: 5111


Align dry-run cluster IDs with the hashed wire format
buildFailureClusters() always emits cluster_${slug}_${8hex}, but these canned clusters still use unhashed IDs. Update the sample (and the matching dry-run test) so --dry-run matches the real response shape.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/dry-run/samples.ts` around lines 355 - 419, The canned dry-run
fixtures are using human-readable cluster IDs instead of the hashed wire-format
produced by buildFailureClusters(). Update the sample data in samples.ts so the
clusterId values follow the cluster_${slug}_${8hex} pattern, and make the
corresponding dry-run test expect the same hashed shape. Use the existing
groupKey/fix-target entries to keep the fixture semantics unchanged while
aligning the IDs with the real response format.

Source: Path instructions

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.

2 participants