Skip to content

feat: exclude external-analyzer test files from CRAP scoring - #286

Merged
jflowers merged 9 commits into
unbound-force:mainfrom
jflowers:opsx/exclude-external-test-functions
Oct 1, 2026
Merged

jflowers merged 9 commits into
unbound-force:mainfrom
jflowers:opsx/exclude-external-test-functions

Conversation

@jflowers

Copy link
Copy Markdown
Collaborator

Summary

External analyzers instrument only source files (e.g. --cov=src), so test
functions get no coverage entry and are scored at a phantom 0% — inflating
CRAP/CRAPload/quadrant counts and emitting spurious add_tests flags. This
change filters test files out of CRAP scoring using the analyzer's own
discover test_files (consumed at the adapter layer, never reaching
computeScores), and adds a no_contract_expected/reason quality sentinel
for test functions whose target has no production contract to assert.

Closes #284.

How to Test

go test -race -count=1 -short ./...
go test -race -count=1 -short -run 'TestCrapWithExternalAnalyzer|TestQualityWithExternalAnalyzer_HappyPath' ./cmd/gaze/...
go test -race -count=1 -short -run 'TestFilterTestFiles|TestSession_Discover|TestBuildQualityFromMappingsNoContractExpected' ./internal/adapter/...

How to Demo

Run gaze crap --analyzer <fake-analyzer> --language python ./src against the
fake analyzer in internal/protocol/testdata/fake_analyzer/. The test-file
function tests/test_ops.py::test_add is absent from scores and fix-strategy
counts (3 source functions only). Run gaze quality --analyzer <fake> to see
no_contract_expected: true + reason: "test_function_no_target_effects" on
the test_add report. Analyzers without the discover capability (or whose
discover fails) degrade gracefully to unfiltered scoring with a warning.

Key Files Changed

  • internal/adapter/session.go — testFiles field, discover in Initialize, DiscoverTestFiles() accessor
  • internal/adapter/complexity.go — SetTestFiles setter + filterTestFiles
  • internal/adapter/quality.go — BuildQualityFromMappings testFiles param, sentinel, summary exclusion
  • internal/taxonomy/types.go — NoContractExpected/Reason fields on ContractCoverage
  • internal/report/schema.go + report_test.go — schema properties + validation test
  • cmd/gaze/main.go — thread DiscoverTestFiles() into quality path
  • internal/adapter/{session_test.go,complexity_internal_test.go,quality_internal_test.go,adapter_test.go,call_test.go} — tests
  • internal/protocol/testdata/fake_analyzer/main.go + client_test.go — fixture extensions
  • cmd/gaze/external_analyzer_test.go — CRAP/quality integration assertions
  • docs/protocol.md, docs/reference/json-schemas.md — docs
  • openspec/changes/exclude-external-test-functions/ — spec artifacts

This PR was generated by /uf.finale (AI-assisted).

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Review · ⚠️ Cancelled · Started 11:28 PM UTC · Ended 11:30 PM UTC

Commit: 95bcb5a · View workflow run →

@fullsend-ai-review

fullsend-ai-review Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 11:30 PM UTC · Completed 11:48 PM UTC

Commit: 3272205 · View workflow run →

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

@fullsend-ai-review fullsend-ai-review Bot added the risk/moderate PR risk: moderate label Sep 28, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Risk Assessment: moderate (2/5)

Details

Moderate risk. 25-file feature PR with good test ratio (32%), no protected paths, no security-sensitive files, no CI/dependency changes, by an established contributor with a well-scoped linked issue. Primary risk factor is high churn on cmd/gaze/main.go. Changes are well-contained within the adapter/external-analyzer subsystem.

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Medium

  • [stale-doc] docs/porting/taxonomy-reference.md:208 — The JSON Field Name Reference table lists canonical snake_case field names for cross-implementation compatibility but does not include the two new ContractCoverage fields added in this PR: no_contract_expected (bool, omitempty) and reason (string, omitempty). Porters implementing the quality report output format will not know these fields exist.
    Remediation: Add two rows to the JSON Field Name Reference table: no_contract_expected | ContractCoverage | bool (nullable) and reason | ContractCoverage | string (nullable).

Low

  • [api-shape] internal/adapter/complexity.go:35 — SetTestFiles is a post-construction setter on ExternalComplexityProvider, breaking the constructor-injection pattern. The test-file set is available at construction time in Session.Initialize() and could be passed through NewExternalComplexityProvider directly.
    Remediation: Change NewExternalComplexityProvider to accept testFiles map[string]bool as a parameter.

  • [naming-convention] internal/adapter/session_test.go:83 — TestSession_DiscoverUncapable uses non-standard English ("uncapable"). The codebase convention is Disabled or NoXxx (e.g., TestDocCoverage_CapabilityDisabled).
    Remediation: Rename to TestSession_DiscoverCapabilityDisabled.

  • [code-organization] internal/adapter/session_test.go:164 — var _ taxonomy.QualityReport is an orphaned compile guard. The taxonomy import is already justified by its use in TestSession_QualitySentinelEndToEnd. No other test file in internal/adapter/ uses this pattern.
    Remediation: Remove var _ taxonomy.QualityReport.

  • [doc-style] internal/adapter/session.go:170 — DiscoverTestFiles GoDoc over-specifies preconditions relative to the implementation. The comment says "returns nil when the session was not initialized" but the method does not check s.initDone — it relies on zero-value semantics. Existing accessors (Client(), Language()) use more concise GoDoc.
    Remediation: Simplify to match the accessor pattern.

  • [pattern-inconsistency] internal/adapter/session.go:197 — The unexported discover() method checks both s.initDone and s.caps.Discover, but its only caller discoverTestFiles() already validates s.caps.Discover, and is only called from Initialize() after s.initDone = true. The guards follow the exported DocCoverage() pattern but are redundant for an unexported helper.

  • [code-organization] internal/protocol/testdata/fake_analyzer/main.go:32 — New package-level mutable vars (noDiscover, discoverError, reportCounts, methodCounts) break the pre-existing pattern where flags were local to main() and passed as parameters to handleRequest.
    Remediation: Pass the new flags to handleRequest as parameters or bundle into an options struct.

  • [edge-case] internal/adapter/quality.go:115 — The no-contract-expected sentinel's correctness is bounded by the analyzer's discover output accuracy. The existing 4-line comment explains what the sentinel does; a trust-boundary annotation documenting why it depends on discover accuracy would complement it.


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

@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 internal/adapter/complexity.go Outdated
Comment thread internal/adapter/session_test.go
Comment thread internal/adapter/session_test.go Outdated
Comment thread internal/adapter/session.go
Comment thread internal/adapter/session.go Outdated
Comment thread internal/protocol/testdata/fake_analyzer/main.go Outdated
Comment thread internal/adapter/quality.go Outdated
@jflowers
jflowers force-pushed the opsx/exclude-external-test-functions branch from 3272205 to 6dd29ae Compare September 29, 2026 17:19
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 29, 2026
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 29, 2026
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 29, 2026
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 29, 2026
A discover timeout kills the shared analyzer subprocess (the transport
kills on context deadline), so continuing with the same client poisons
every subsequent provider call. Split discover failure handling: protocol
errors and unmarshal errors still degrade gracefully (warn + no filtering),
but a timeout now returns an error so Session.Initialize fails fast.

Also fixes review-council LOW findings:
- share an isTestFile helper for the filepath.Clean lookup (D3)
- replace the bare "test_function_no_target_effects" magic string with a
  typed ContractCoverageReasonType constant
- return a defensive copy from DiscoverTestFiles()
- correct SetTestFiles -> constructor-injection doc drift in design/tasks
- align json-schemas.md wording ("empty unioned target effects")
- soften the proposal's Go-mode regression-test claim
- add fake-analyzer --hang-discover and --empty-discover modes with tests

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

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
jflowers added a commit to jflowers/gaze that referenced this pull request Sep 29, 2026
Amend tasks.md 1.2/6.4 and design.md Goals/D2 to reflect the
constructor-injection filter and the timeout fail-fast split, removing
the final stale SetTestFiles references and "no hard errors" language.

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

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
@jflowers

Copy link
Copy Markdown
Collaborator Author

Addressed in c9a4c01: documented no_contract_expected and reason in the JSON Field Name Reference table.

- Filter test files out of CRAP scoring using the analyzer's own
  discover test_files, at the adapter layer (no scoring-engine change)
- Add no_contract_expected/reason quality sentinel for test functions
  with no production target to assert on
- Graceful degradation: discover unsupported or failing leaves
  filtering off and warns to stderr

Assisted-by: deepseek-v4-pro
Generated with AI assistance (deepseek-v4-pro)
- external-analyzer-filtering: filter test files at the adapter layer via discover test_files, not computeScores
- schema-sentinel-pattern: additive no_contract_expected/reason bool+string, never an out-of-band -1 sentinel
- review-hallucination-gotcha: verify reviewer-cited file paths against disk before fixing findings

Assisted-by: deepseek-v4-pro
Generated with AI assistance (deepseek-v4-pro)
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
Addresses PR unbound-force#286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
A discover timeout kills the shared analyzer subprocess (the transport
kills on context deadline), so continuing with the same client poisons
every subsequent provider call. Split discover failure handling: protocol
errors and unmarshal errors still degrade gracefully (warn + no filtering),
but a timeout now returns an error so Session.Initialize fails fast.

Also fixes review-council LOW findings:
- share an isTestFile helper for the filepath.Clean lookup (D3)
- replace the bare "test_function_no_target_effects" magic string with a
  typed ContractCoverageReasonType constant
- return a defensive copy from DiscoverTestFiles()
- correct SetTestFiles -> constructor-injection doc drift in design/tasks
- align json-schemas.md wording ("empty unioned target effects")
- soften the proposal's Go-mode regression-test claim
- add fake-analyzer --hang-discover and --empty-discover modes with tests

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

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
Amend tasks.md 1.2/6.4 and design.md Goals/D2 to reflect the
constructor-injection filter and the timeout fail-fast split, removing
the final stale SetTestFiles references and "no hard errors" language.

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

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
@jflowers
jflowers force-pushed the opsx/exclude-external-test-functions branch from 7cee3f6 to 1a56035 Compare October 1, 2026 14:48
@jflowers jflowers self-assigned this Oct 1, 2026

@jflowers jflowers left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Verdict: APPROVE

Note: Could not post as APPROVE due to insufficient permissions (self-review prohibition). Posted as COMMENT instead. Original verdict: APPROVE.

The implementation thoroughly filters external-analyzer test files from CRAP scoring at the adapter layer and adds a no_contract_expected/reason quality sentinel for test functions with no production contract. All spec scenarios are covered by tests, all 5 CI checks pass, and all 7 prior review findings were addressed.

CI Status

All 5 checks PASS (Unit+Integration Go 1.24/1.25, E2E Go 1.24/1.25, MegaLinter).

Walkthrough

  • internal/adapter/session.go — testFiles field, discover in Initialize, DiscoverTestFiles() accessor
  • internal/adapter/complexity.go — constructor-injected filter via filterTestFiles/isTestFile
  • internal/adapter/quality.go — testFiles param, no-contract-expected sentinel, summary exclusion
  • internal/taxonomy/types.go — NoContractExpected/Reason + ContractCoverageReasonType
  • internal/report/schema.go — no_contract_expected/reason schema properties
  • Extensive unit + integration tests; graceful degradation on discover failure/timeout

Alignment / Security / Constitution

No issues found. Issue #284 acceptance criteria both satisfied; Go-mode behavior unchanged; no new untrusted inputs or secrets.

This review was generated by /uf.review-pr (AI-assisted).

@jflowers
jflowers merged commit 9026c30 into unbound-force:main Oct 1, 2026
5 checks passed
jflowers added a commit that referenced this pull request Oct 1, 2026
Addresses PR #286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit that referenced this pull request Oct 1, 2026
Addresses PR #286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit that referenced this pull request Oct 1, 2026
Addresses PR #286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jay.flowers@gmail.com>

Assisted-by: gpt-5.6-luna
jflowers added a commit that referenced this pull request Oct 1, 2026
A discover timeout kills the shared analyzer subprocess (the transport
kills on context deadline), so continuing with the same client poisons
every subsequent provider call. Split discover failure handling: protocol
errors and unmarshal errors still degrade gracefully (warn + no filtering),
but a timeout now returns an error so Session.Initialize fails fast.

Also fixes review-council LOW findings:
- share an isTestFile helper for the filepath.Clean lookup (D3)
- replace the bare "test_function_no_target_effects" magic string with a
  typed ContractCoverageReasonType constant
- return a defensive copy from DiscoverTestFiles()
- correct SetTestFiles -> constructor-injection doc drift in design/tasks
- align json-schemas.md wording ("empty unioned target effects")
- soften the proposal's Go-mode regression-test claim
- add fake-analyzer --hang-discover and --empty-discover modes with tests

Addresses PR #286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
jflowers added a commit that referenced this pull request Oct 1, 2026
Amend tasks.md 1.2/6.4 and design.md Goals/D2 to reflect the
constructor-injection filter and the timeout fail-fast split, removing
the final stale SetTestFiles references and "no hard errors" language.

Addresses PR #286 review feedback from @fullsend-ai-review[bot].

Signed-off-by: Jay Flowers <jflowers@unbound-force.com>
Assisted-by: deepseek-v4-pro
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

None yet

Development

Successfully merging this pull request may close these issues.

crap/quality score external-analyzer test functions at 0% coverage instead of excluding them

2 participants