Skip to content

feat: wire --analyzer to gaze quality for external test_mapping - #242

Merged
jflowers merged 9 commits into
unbound-force:mainfrom
jflowers:opsx/quality-external-analyzer
Sep 21, 2026
Merged

jflowers merged 9 commits into
unbound-force:mainfrom
jflowers:opsx/quality-external-analyzer

Conversation

@jflowers

Copy link
Copy Markdown
Collaborator

Summary

Wires --analyzer and --language flags to gaze quality for external test_mapping support, lifting the D12 deferral from #95.

Changes

New: internal/adapter/quality.go

  • BuildQualityFromMappings — converts []protocol.AssertionMappingData + []taxonomy.AnalysisResult into []taxonomy.QualityReport + *taxonomy.PackageSummary, reusing quality.ComputeContractCoverage for metric computation
  • FetchTestMappings — standalone function for direct test_mapping protocol calls
  • computeOverSpecification — counts assertions targeting incidental side effects
  • buildQualitySummary — aggregates reports into PackageSummary

Modified: internal/adapter/session.go

  • Added SideEffects *ExternalSideEffectAnalyzer field to Providers struct
  • Added Session.Client() accessor for direct protocol calls

Modified: cmd/gaze/main.go

  • Replaced --analyzer rejection block with runQualityWithExternalAnalyzer (follows runCrapWithExternalAnalyzer pattern)
  • Added handleQualityNoTestMapping and handleQualityTestMappingError for graceful degradation
  • Unhid --analyzer/--language flags in newQualityCmd
  • Flag validation: --target and --ai-mapper rejected with --analyzer (Go-specific SSA/AST features)

Tests

  • 11 tests in internal/adapter/quality_internal_test.go (BuildQualityFromMappings + computeOverSpecification)
  • 3 tests in cmd/gaze/external_analyzer_test.go (CLI flag validation)

Documentation

  • README.md: added gaze quality --analyzer usage example
  • AGENTS.md: added Recent Changes entry

Verification

  • go test -race -count=1 -short ./... — all pass
  • golangci-lint run — 0 issues
  • gaze quality --help shows --analyzer and --language flags
  • CRAP scores for new functions: all < 30 (max 12.0)
  • Constitution alignment: all 4 principles PASS

Spec Artifacts

  • openspec/changes/quality-external-analyzer/ — proposal, design, specs, tasks

Closes #229

@yvonnedevlinrh yvonnedevlinrh left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review: PR #242feat: wire --analyzer to gaze quality for external test_mapping

Thanks for this — the core feature is well-structured, spec-backed, and CI-green, reusing existing session/adapter/coverage infrastructure cleanly. Two review passes (general + deep correctness) surfaced findings below. Verdict: REQUEST CHANGES, driven by one HIGH correctness bug and one HIGH scope issue.

Findings summary

# Severity Category Location Finding Suggested resolution
1 HIGH Correctness (Constitution I: Accuracy) internal/adapter/quality.go:60-119 BuildQualityFromMappings keys the effect set off only the first mapping's target (testMappings[0]). When one test asserts on 2+ distinct target functions, contract-coverage/over-specification metrics are silently wrong and the second target is dropped from the report. Untested. Sub-group each test's mappings by target (one report per test,target), or union effects across all referenced targets; add a multi-target test.
2 MEDIUM Spec drift (Constitution III: Actionable Output) cmd/gaze/main.go:1244, :1264 Degraded handlers emit bare &taxonomy.PackageSummary{}. Spec (specs/quality-external-analyzer.md) mandates a reason of "test_mapping_unavailable" / "test_mapping_error", but PackageSummary has no reason field, so neither string appears in text or JSON. Observable degradation (stderr warning + zero coverage + exit code) is correct and tested. Add a reason field to PackageSummary (and schema), or amend the spec scenarios to drop it.
3 MEDIUM Test coverage gap internal/adapter/quality_internal_test.go:14-448 Every TestBuildQualityFromMappings subtest uses a single target per test, so the multi-target path (finding #1) is never exercised — which is why the bug shipped CI-green. Add a subtest with one test function mapping to two distinct targets; assert both targets' effects count.
4 LOW Scope noise .uf/dewey/learnings/*.md (×4) Four draft learning notes (author jay-flowers) bundled into a feature PR — harmless/additive but unrelated; different author suggests a separate workflow. Optional: strip from this PR.
5 LOW Process (AGENTS.md Website Documentation Gate) New user-facing CLI capability (gaze quality --analyzer) requires a tracking issue in unbound-force/website; #229 calls out website#227/#165 as inaccurate. No website issue referenced. Create the website tracking issue before merge.

Positives

  • Security: no findings — no new exec/shell construction; --analyzer/--language reach subprocess via pre-existing initExternalSession/Discover; FetchTestMappings uses JSON-RPC over stdin with a bounded context.WithTimeout(protocol.AnalysisTimeout); moduleDir from os.Getwd(); errors wrapped without secret exposure.
  • Caller impact: none — runQuality signature unchanged; external path is a pure early-return branch gated on analyzerFlag != "". Providers/Session additions are additive.
  • Branch reachability: all new branches (flag rejections, both degradation handlers under both threshold conditions, happy path) are reachable and tested.
  • Constitution: I Accuracy — now questioned by #1; II Minimal Assumptions PASS (Go-native path untouched, opt-in); III Actionable Output PARTIAL (#3); IV Testability PASS (pure functions, table-driven tests, GoDoc present).

Blocking items before merge

  1. Fix multi-target handling (#1) + add multi-target test (#4).
  2. Add reason field or amend spec (#3).

Comment thread internal/adapter/quality.go Outdated
Comment thread internal/adapter/quality_internal_test.go
Comment thread cmd/gaze/main.go Outdated
@em-redhat em-redhat moved this from Ready for Review 👀 to In Review 🏁 in Unbound Force Planning Sep 1, 2026
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 1, 2026
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 1, 2026
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
@jflowers

jflowers commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator Author

Addressing the remaining review body items:

Item 4 (learning files): The .uf/dewey/learnings/*.md files were acquired during PR development as part of the iterative design process. These are tracked for cleanup at unbound-force/unbound-force#570. They don't affect the PR's functionality and are part of the agent's working memory — removing them from this PR would not change the commit scope of the actual code changes.

Item 5 (website documentation gate): Created tracking issue: unbound-force/website#280

All three code findings have been addressed:

  • 9aec1ae — multi-target effect union with seen[e.ID] dedup
  • 74dac61Reason field on PackageSummary with schema update
  • 580534e — dedup regression test + Reason JSON assertion coverage

jflowers added a commit to jflowers/gaze that referenced this pull request Sep 1, 2026
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 1, 2026
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
@jflowers
jflowers force-pushed the opsx/quality-external-analyzer branch from 580534e to 88e0a00 Compare September 1, 2026 21:45
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 2, 2026
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 2, 2026
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
@jflowers
jflowers force-pushed the opsx/quality-external-analyzer branch from 88e0a00 to 2e9a630 Compare September 2, 2026 13:56
@jflowers

jflowers commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator Author

PR Cost Report: #242

Session: Change proposal workflow

ID: ses_fa6a225d8ffeobROvFDniIAAyY

Metric Value
Cost (parent only) $11.62
Input tokens 156
Output tokens 53,113
Cache read tokens 11,595,276
Cache write tokens 718,669

Timeline: 2026-08-31 15:48:38 — 2026-08-31 17:20:19

Child sessions: 3 — additional cost: $1.81
Session tree total: $13.43

Child session breakdown
Session Cost Output Tokens
Explore quality+adapter code (@explore subagent) $0.28 9,402
Spec Review Council (@cobalt-crush-dev subagent) $1.19 8,256
Code Review Council (@cobalt-crush-dev subagent) $0.35 864

Session: uf.unleash autonomous pipeline

ID: ses_fa64b83baffeiH9zngDbTjxikM

Metric Value
Cost (parent only) $11.49
Input tokens 121
Output tokens 39,893
Cache read tokens 9,338,485
Cache write tokens 1,050,913

Timeline: 2026-08-31 17:23:15 — 2026-08-31 18:41:54

Child sessions: 6 — additional cost: $14.46
Session tree total: $25.96

Child session breakdown
Session Cost Output Tokens
Adversary code review (@divisor-adversary subagent) $1.87 7,334
Architect code review (@divisor-architect subagent) $2.08 7,584
Guard intent review (@divisor-guard subagent) $3.25 8,689
Testing review (@divisor-testing subagent) $2.77 9,181
SRE operational review (@divisor-sre subagent) $3.62 8,176
Curator documentation review (@divisor-curator subagent) $0.89 5,047

Session: Address GitHub PR feedback

ID: ses_fa266ed13ffeowQuAlgs8XeSUT

Metric Value
Cost (parent only) $7.99
Input tokens 110
Output tokens 39,985
Cache read tokens 8,499,788
Cache write tokens 438,127

Timeline: 2026-09-01 11:31:48 — 2026-09-01 14:20:44

Child sessions: 5 — additional cost: $7.61
Session tree total: $15.60

Child session breakdown
Session Cost Output Tokens
Adversary review of PR fix (@divisor-adversary subagent) $1.58 5,511
Architect review of PR fix (@divisor-architect subagent) $1.45 5,541
Guard review of PR fix (@divisor-guard subagent) $1.87 6,994
Testing review of PR fix (@divisor-testing subagent) $2.33 7,613
Review council iter 2 - testing (@reviewer-testing subagent) $0.38 1,805

Session: Sync fork with upstream, rebase main

ID: ses_f9dafed3dffeoHKN7HT8F9O1EK

Metric Value
Cost (parent only) $8.40
Input tokens 109
Output tokens 35,452
Cache read tokens 7,179,662
Cache write tokens 628,049

Timeline: 2026-09-02 09:30:09 — 2026-09-02 13:22:51

Session total: $8.40


Session: Propose changes with artifacts

ID: ses_f9cda3f39ffews3YSoQAJUxjCm

Metric Value
Cost (parent only) $16.68
Input tokens 193
Output tokens 70,897
Cache read tokens 17,582,695
Cache write tokens 977,700

Timeline: 2026-09-02 13:23:33 — 2026-09-02 15:19:31

Child sessions: 15 — additional cost: $19.46
Session tree total: $36.13

Child session breakdown
Session Cost Output Tokens
Adversary spec review (@divisor-adversary subagent) $1.08 5,070
Architect spec review (@divisor-architect subagent) $1.44 7,201
Guard spec review (@divisor-guard subagent) $1.47 6,212
Testing spec review (@divisor-testing subagent) $0.94 5,979
SRE spec review (@divisor-sre subagent) $0.84 3,119
Adversary spec re-review (@divisor-adversary subagent) $1.08 4,972
Architect spec re-review (@divisor-architect subagent) $1.31 6,548
Guard spec re-review (@divisor-guard subagent) $1.31 4,397
Testing spec re-review (@divisor-testing subagent) $1.46 5,081
SRE spec re-review (@divisor-sre subagent) $1.34 5,839
Adversary code review (@divisor-adversary subagent) $1.03 3,566
Architect code review (@divisor-architect subagent) $1.65 5,706
Guard code review (@divisor-guard subagent) $1.59 4,878
Testing code review (@divisor-testing subagent) $1.28 4,448
SRE code review (@divisor-sre subagent) $1.64 5,332

Grand Total: $99.52 across 5 session tree(s) (34 sessions)

em-redhat
em-redhat previously approved these changes Sep 9, 2026

@em-redhat em-redhat left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR #242 Review: feat: wire --analyzer to gaze quality for external test_mapping

Branch: opsx/quality-external-analyzermain | Closes: #229 | Diff: +1901 / -196, 20 files


Pre-flight

Mode Result
ci-aware PASS — all 5 CI checks pass (MegaLinter, Unit+Integration Go 1.24/1.25, E2E Go 1.24/1.25). All local tools covered by CI. No local execution needed.

Prior Review Status

yvonnedevlinrh filed CHANGES_REQUESTED with 5 findings. All have been addressed by 3 fix commits:

  • HIGH (multi-target data loss) → Fixed in 048242d with seen map dedup
  • MEDIUM (spec drift — missing Reason field) → Fixed in 686c0ac
  • MEDIUM (test coverage gap) → Fixed in 2e96309 with multi-target dedup + Reason tests
  • LOW (dewey files, website issue) → Scope noise / process items

Constitution Alignment

Principle Verdict Notes
I. Accuracy PASS BuildQualityFromMappings reuses quality.ComputeContractCoverage — the same metric engine as native Go. Multi-target effect union with dedup-by-ID prevents double-counting.
II. Minimal Assumptions PASS External analyzer provides all data; no Go-specific assumptions. Flag validation correctly rejects --target and --ai-mapper (Go SSA/AST features).
III. Actionable Output PASS Reports include contract coverage %, over-specification ratio, worst coverage tests, ambiguous effects, unmapped assertions. Reason field explains zero-coverage causes.
IV. Testability PASS 14+ new tests. All graceful degradation handlers directly testable via synthetic params. No subprocess needed for handler tests.

Findings

MEDIUM: Dual fetchTestMappings implementations — maintenance divergence risk

Files: internal/adapter/contract.go:93 (private) and internal/adapter/quality.go (exported FetchTestMappings)

Two functions that do essentially the same thing — call test_mapping and return mappings — but with different patterns:

  • Private: manual Call → Error check → Unmarshal, uses p.warn() for stderr logging
  • Exported: uses callAndUnmarshal generic helper, no logging (caller handles)

Both are intentional — private serves the crap provider pipeline (needs p.warn() for graceful degradation within the provider), exported serves the quality CLI path (returns error to caller). However, any future change to the test_mapping protocol (new fields, error handling changes) must be applied to both implementations or they will silently diverge.

Recommendation: Add a comment cross-referencing the two implementations, or consider refactoring the private method to delegate to the exported function with a p.warn() wrapper. Not blocking since both are tested independently.

Convention: DR-003 (Document Why Not What)


LOW: AssertionDetectionConfidence hardcoded to 0

File: internal/adapter/quality.go (within BuildQualityFromMappings)

Every QualityReport produced by the external path sets AssertionDetectionConfidence: 0. This is documented in the design (D5 in design.md) as a conscious trade-off — external analyzers provide assertion mappings directly rather than detecting them mechanically. However, downstream consumers (like the gaze-reporter agent) interpret this field as a signal of mapping quality. A value of 0 may mislead consumers into thinking assertions were not detected at all.

Recommendation: Consider using a sentinel value like -1 for "not applicable" vs 0 for "detected but none found", or add a comment in the agent prompt explaining that external analyzer reports always have confidence 0. This is informational — the current approach is functional and documented.


LOW: 4 dewey learning files in PR scope

Files: .uf/dewey/learnings/quality-external-analyzer-*.md

These are draft learnings about testing patterns and GoDoc gotchas discovered during development. While they contain useful context, they add 4 files of noise to a feature PR. The yvonnedevlinrh review flagged this as scope noise.

Recommendation: Consider moving dewey learnings to separate commits or a housekeeping PR. Not blocking.


INFO: Website documentation gate

Per AGENTS.md "Website Documentation Gate", changes affecting user-facing CLI commands require a GitHub issue in unbound-force/website. The --analyzer flag on gaze quality is now visible and documented in README. yvonnedevlinrh flagged this as a LOW finding. Confirm whether a website issue has been filed.


Walkthrough

Architecture: The PR separates concerns cleanly between the adapter layer (internal/adapter/quality.go — protocol-to-taxonomy conversion) and the CLI layer (cmd/gaze/main.go — flag validation, graceful degradation orchestration, output formatting). FetchTestMappings is a standalone function rather than a method, enabling direct testing without constructing a full Session.

Data flow: runQualityWithExternalAnalyzer → validates flags (D6/D7) → providers.SideEffects.AllResults()adapter.FetchTestMappings()adapter.BuildQualityFromMappings()quality.WriteText/WriteJSON. Two graceful degradation branches handle no-capability and fetch-error cases, producing zero-coverage reports with explanatory Reason fields.

Key design decisions: D1 (reuse quality.ComputeContractCoverage), D4 (composition via SideEffects field), D5 (AssertionDetectionConfidence: 0), D6/D7 (reject Go-specific flags), D8 (threshold evaluation on zero-coverage reports).


Verdict

APPROVE — The implementation is well-structured, thoroughly tested, and addresses all prior review findings. The single MEDIUM finding (dual implementation divergence risk) is a maintenance observation, not a correctness issue. All CI checks pass. Constitution principles satisfied.

jflowers added a commit to jflowers/gaze that referenced this pull request Sep 15, 2026
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 15, 2026
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
@jflowers
jflowers force-pushed the opsx/quality-external-analyzer branch from 2e9a630 to dc03527 Compare September 15, 2026 22:12
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 21, 2026

Copy link
Copy Markdown

🤖 Review · ❌ Terminated · Started 4:20 PM UTC · Ended 4:40 PM UTC

Commit: cadc735 · View workflow run →

@jflowers

Copy link
Copy Markdown
Collaborator Author

Addressed the remaining round-2 review body items:

Reason tokens (HIGH)cmd/gaze/main.go now emits stable machine-readable tokens matching the spec scenarios: reason: "test_mapping_unavailable" and reason: "test_mapping_error". Operational detail (fetchErr) remains in the stderr warning, not the JSON reason field. Tests assert the exact tokens. (db76ac7, plus cadc735 aligning the embedded JSON Schema reason description.)

Documentation (MEDIUM)

  • docs/reference/cli/quality.md — added --analyzer/--language flags, the note that Go-specific flags (--target, --ai-mapper, --include-unexported) are rejected with --analyzer, and graceful-degradation behavior.
  • docs/reference/json-schemas.md — added the reason field to the PackageSummary table.
  • docs/protocol.md — lifecycle diagram now covers crap/quality/report; the test_mapping degradation bullet now notes gaze quality degrades to zero contract coverage with reason: test_mapping_error. (de1cbe5)

@jflowers

Copy link
Copy Markdown
Collaborator Author

Two findings reviewed and intentionally not actioned:

AGENTS.md modification (protected-path)AGENTS.md is a protected governance file. The only change is a "Recent Changes" entry documenting already-implemented work; it is informational and is already visible to reviewers as part of the human review process. No code action required.

Report structure per-test-function (scope-alignment) — the per-test-function structure of QualityReport is intentional (design decision D1 in the proposal) and required by the downstream report pipeline's aggregation. No change needed.

fullsend-ai-review[bot]

This comment was marked as outdated.

@fullsend-ai-review fullsend-ai-review Bot added the requires-manual-review Review requires human judgment label Sep 21, 2026
@fullsend-ai-review

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:20 PM UTC · Completed 4:40 PM UTC

Commit: cadc735 · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $11.36

…CRAP + contract coverage)

Add BuildQualityFromMappings in internal/adapter/quality.go that
converts []protocol.AssertionMappingData + []taxonomy.AnalysisResult
into []taxonomy.QualityReport + *taxonomy.PackageSummary, reusing
quality.ComputeContractCoverage for metric computation.

Add FetchTestMappings standalone function for direct test_mapping
protocol calls. Add computeOverSpecification for incidental-effect
assertion counting. Add Session.Client() accessor. Expose SideEffects
on Providers struct.

In cmd/gaze/main.go: replace --analyzer rejection block with
runQualityWithExternalAnalyzer following runCrapWithExternalAnalyzer
pattern. Add handleQualityNoTestMapping and handleQualityTestMappingError
for graceful degradation. Unhide --analyzer/--language flags.

Flag validation: --target and --ai-mapper rejected with --analyzer
(Go-specific SSA/AST features).

14 new tests across internal/adapter/ and cmd/gaze/.

Closes unbound-force#229
- Fix FetchTestMappings GoDoc to accurately describe error propagation
- Remove double-logging by eliminating stderr param from FetchTestMappings
- Add happy-path integration test for quality with external analyzer
- Add unit tests for FetchTestMappings (success + protocol error)
- Add tests for graceful degradation handlers (no test_mapping, error)
- Add buildQualitySummary truncation test with 6+ test functions
- Add findSideEffectID direct test with 4 table-driven cases
- Add computeOverSpecification edge case for empty SideEffectID
- Update README with --language example and flag incompatibility note

Closes unbound-force#229

Assisted-by: claude-opus
Generated with AI assistance (claude-opus)
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
Add multi-target dedup test case with shared effect IDs (se-shared)
across targets to exercise the seen[e.ID] deduplication path. Add
Reason field JSON assertions to NoTestMapping and TestMappingError
handler tests verifying the quality_summary.reason value.

Addresses review-council iteration 1 blocking findings.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
Change the JSON quality_summary.reason from human-readable
"test_mapping unavailable"/"test_mapping error: %v" to the
spec-mandated stable machine-readable tokens
"test_mapping_unavailable"/"test_mapping_error". Operational
error detail remains in the stderr warning.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
…ch behavior

Add cross-reference comments between the standalone FetchTestMappings
(quality CLI path) and the private ExternalContractCoverageProvider
.fetchTestMappings (crap path), and document the deterministic
first-match semantics of findSideEffectID.

Addresses PR unbound-force#242 review feedback from @fullsend-ai-review[bot] and @em-redhat.

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
…quality

Add --analyzer/--language flags and Go-specific flag incompatibility
note to the quality CLI reference, document the PackageSummary.reason
field in the JSON schema reference, and update the protocol lifecycle
and degradation notes to reflect gaze quality's test_mapping behavior.

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh and @fullsend-ai-review[bot].

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
…able tokens

Addresses PR unbound-force#242 review-council finding: the embedded JSON Schema
still described PackageSummary.reason as "human-readable" after the
reason field was changed to carry stable machine-readable degradation
tokens (test_mapping_unavailable / test_mapping_error).

Addresses PR unbound-force#242 review feedback from @yvonnedevlinrh.

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
@jflowers
jflowers force-pushed the opsx/quality-external-analyzer branch from cadc735 to 7cb85fc Compare September 21, 2026 16:46
@fullsend-ai-review

fullsend-ai-review Bot commented Sep 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 4:47 PM UTC · Completed 5:24 PM UTC

Commit: 7cb85fc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $9.71

@fullsend-ai-review fullsend-ai-review 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.

See the review comment for full details.

Comment thread cmd/gaze/main.go
// Design decisions D6/D7: --target and --ai-mapper are rejected because
// they depend on Go-specific SSA target inference and AST assertion
// detection that external analyzers cannot provide.
func runQualityWithExternalAnalyzer(p qualityParams) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] doc-code-mismatch

README.md and docs/reference/cli/quality.md document that --include-unexported is rejected when --analyzer is set, but runQualityWithExternalAnalyzer only validates --target and --ai-mapper. The flag is silently ignored instead of rejected, contradicting user-facing documentation.

Suggested fix: Add a validation check for p.includeUnexported in runQualityWithExternalAnalyzer, or remove --include-unexported from the documented rejection list in README.md and docs/reference/cli/quality.md.

Comment thread cmd/gaze/main.go
return reports, summary
// writeQualityEmptyOutput writes an empty quality report in the
// requested format. Used by degraded/error paths.
func writeQualityEmptyOutput(p qualityParams, summary *taxonomy.PackageSummary) error {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] naming-consistency

writeQualityEmptyOutput is a thin wrapper around writeQualityEmptyResults that adapts qualityParams fields. A brief comment documenting its purpose would clarify the indirection.

Suggested fix: Add a comment explaining the wrapper serves as parameter adaptation from qualityParams to the lower-level function signature, or inline the call.

@fullsend-ai-review fullsend-ai-review Bot removed the requires-manual-review Review requires human judgment label Sep 21, 2026
@jflowers
jflowers merged commit 76f8ca9 into unbound-force:main Sep 21, 2026
35 checks passed
jflowers added a commit that referenced this pull request Sep 21, 2026
…pings

BuildQualityFromMappings previously keyed the effect set off only the
first mapping's target, causing data loss when a test exercises multiple
target functions (e.g., integration tests). Contract coverage was
computed against a subset of effects, producing false metrics.

The fix collects all distinct target funcKeys from the test's mappings,
unions their side effects with deduplication by effect ID, and passes the
full effect surface to ComputeContractCoverage. The first target is
retained as the primary for display metadata only.

Adds a multi-target subtest exercising the union path with two targets
(db.Save + cache.Invalidate) and three contractual effects.

Addresses PR #242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit that referenced this pull request Sep 21, 2026
Design decision D5 specifies that degraded quality results should carry
a clear reason explaining why metrics are unavailable. The Reason field
was missing from PackageSummary, causing bare empty summaries to be
emitted by handleQualityNoTestMapping and handleQualityTestMappingError.

Adds Reason string field (json:"reason,omitempty") to
taxonomy.PackageSummary. Populates it with "test_mapping unavailable"
or "test_mapping error: <detail>" in the two degraded handlers.
Updates QualitySchema with the new field.

Addresses PR #242 review feedback from @yvonnedevlinrh.

Signed-off-by: Jason Flowers <jason@unboundforce.com>
Assisted-by: claude-opus-4-6
jflowers added a commit that referenced this pull request Sep 21, 2026
Change the JSON quality_summary.reason from human-readable
"test_mapping unavailable"/"test_mapping error: %v" to the
spec-mandated stable machine-readable tokens
"test_mapping_unavailable"/"test_mapping_error". Operational
error detail remains in the stderr warning.

Addresses PR #242 review feedback from @yvonnedevlinrh.

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
jflowers added a commit that referenced this pull request Sep 21, 2026
…ch behavior

Add cross-reference comments between the standalone FetchTestMappings
(quality CLI path) and the private ExternalContractCoverageProvider
.fetchTestMappings (crap path), and document the deterministic
first-match semantics of findSideEffectID.

Addresses PR #242 review feedback from @fullsend-ai-review[bot] and @em-redhat.

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
jflowers added a commit that referenced this pull request Sep 21, 2026
…quality

Add --analyzer/--language flags and Go-specific flag incompatibility
note to the quality CLI reference, document the PackageSummary.reason
field in the JSON schema reference, and update the protocol lifecycle
and degradation notes to reflect gaze quality's test_mapping behavior.

Addresses PR #242 review feedback from @yvonnedevlinrh and @fullsend-ai-review[bot].

Signed-off-by: jflowers
Assisted-by: deepseek-v4-pro
@fullsend-ai-retro

fullsend-ai-retro Bot commented Sep 21, 2026

Copy link
Copy Markdown

🤖 Finished Retro · ✅ Success · Started 5:43 PM UTC · Completed 5:54 PM UTC

Commit: 7cb85fc · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $7.03

@fullsend-ai-retro

Copy link
Copy Markdown

Retro: PR #242 — Wire --analyzer to gaze quality

PR: #242 by jflowers (human-authored), merged 2026-09-21 after 21 days and 3 review rounds. +1926/-199 across 24 files. Closes #229.

Timeline

  1. Aug 31: PR opened with core feature (external analyzer support for gaze quality).
  2. Sep 1: Human reviewer (yvonnedevlinrh) found a HIGH correctness bug — multi-target data loss in BuildQualityFromMappings where effects were keyed off only the first mapping's target. Also found a missing reason field (spec drift) and the test gap that allowed the bug to ship CI-green. Author fixed all findings same day.
  3. Sep 9: Second human reviewer (em-redhat) approved, noting maintenance risk from dual fetchTestMappings implementations.
  4. Sep 15: First review agent run (14 days after PR creation — fullsend was not triggered on this branch until Sep 9). Agent found documentation consistency gaps across 4 reference files and a FetchTestMappings API shape concern. All findings were doc-level; the agent did not identify the correctness bug (already fixed by this point).
  5. Sep 16: Human re-review found another HIGH issue — reason strings were human-readable instead of spec-mandated machine-readable tokens. Author fixed.
  6. Sep 21: Author pushed final fixes. Three review agent runs fired within 90 minutes (dual-trigger pattern: pull_request_target + pull_request_review). Agent found a legitimate MEDIUM doc-code mismatch (--include-unexported silently ignored despite docs saying it's rejected with --analyzer). PR merged with this finding unresolved.
  7. Sep 21: CI test timeout on Go 1.24 — TestOllamaAdapter_ContextCancellation hung for 15 minutes due to a race condition (unrelated to PR changes). Passed on re-run.

Review Quality Assessment

Human reviewers caught both HIGH-severity correctness issues (multi-target data loss, unstable reason tokens) that required domain-specific reasoning and spec cross-referencing. Zero false positives.

Review agent excelled at systematic doc-code consistency sweeps, finding 4+ stale documentation references across CLI reference, JSON schema, protocol docs, and concepts pages. Its strongest unique finding (MEDIUM: --include-unexported doc-code mismatch) was genuinely missed by both human reviewers. However, signal was diluted by noise: findSideEffectID first-match behavior was filed 3 times across runs, FetchTestMappings API shape 3 times, and AGENTS.md protected-path flagged as needing human approval despite being an informational metadata entry.

Cost: $99.52 total across 34 sessions. Review agent: $31.59 across 3 runs.

Existing Issues Referenced

  • Repeated findings across re-reviews: The agent re-filed already-addressed findings without acknowledging prior reviews. This is covered by fullsend-ai/fullsend#956 (resolve inline comments from previous reviews on re-review) and related issues #6950, #7364. This retro provides fresh evidence: 6 duplicate filings across 3 runs on this single PR.
  • Redundant dispatch triggering: 9 fullsend runs in 90 minutes on Sep 21, with pairs triggered within seconds by dual pull_request_target + pull_request_review events. Related to fullsend-ai/fullsend#6968 and #7384.

Proposals filed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk/moderate PR risk: moderate

Projects

Status: Ready for Review 👀

Development

Successfully merging this pull request may close these issues.

feat: wire --analyzer to gaze quality for external test_mapping (GazeCRAP + contract coverage)

4 participants