Skip to content

Discovery Phase 2: convention-based test traceability #135

@Dimwiddle

Description

@Dimwiddle

Summary

Match test files and functions to existing spec files and scenarios by naming convention. Annotates draft specs with inferred traceability links and enriches specleft status output with convention-matched entries.

Depends on: #124, #133, #134

New file

src/specleft/discovery/traceability.py

from specleft.discovery.models import DiscoveredItem
from specleft.schema import SpecsConfig

@dataclass(frozen=True)
class TraceabilityLink:
    test_file:     Path
    test_function: str
    spec_file:     Path
    scenario_id:   str
    match_kind:    str      # "filename" | "function" | "both"
    confidence:    float

def infer_traceability(
    discovered: list[DiscoveredItem],
    specs: SpecsConfig,
) -> list[TraceabilityLink]:
    """
    Pure function: accepts pre-loaded specs rather than resolving internally.
    Caller is responsible for loading SpecsConfig from the correct directory.
    This keeps the discovery package decoupled from spec resolution.

    Does not scan .specleft/specs/_discovered/ — caller should load specs
    from resolve_specs_dir() which excludes underscore-prefixed directories.
    """

Matching algorithm

Step 1 — Filename match

Normalise both sides (replace -, _ with space, strip test_ prefix from test filename stem):

.specleft/specs/user-authentication.md  →  "user authentication"
tests/test_user_authentication.py       →  "user authentication"

Exact normalised match = filename link.

Step 2 — Function match within a filename-matched pair

Normalise scenario slug and function name (strip test_, replace _ with space):

Scenario: valid-credentials   →  "valid credentials"
def test_valid_credentials()  →  "valid credentials"
  • Exact match → confidence=0.9, match_kind="both"
  • Common prefix >=60% of shorter string → confidence=0.6, match_kind="function"

Step 3 — Annotate draft specs

For matched scenarios, add a frontmatter block to the generated markdown:

---
linked_tests:
  - file: tests/test_user_authentication.py
    function: test_valid_credentials
    confidence: 0.9
---

Integration with specleft status

Update src/specleft/commands/status.py:

  • After the existing _index_specleft_tests() check, if a scenario has no decorator-based match, attempt convention-based lookup via infer_traceability()
  • Pass pre-loaded SpecsConfig — do not re-resolve specs internally
  • If a convention match is found, set match_kind: "convention" in the status entry
  • Table output: show ✓ (convention) instead of for convention-matched scenarios

Acceptance criteria

  • tests/test_user_authentication.py::test_valid_credentials links to .specleft/specs/user-authentication.md scenario valid-credentials
  • tests/test_payment.py does not link to .specleft/specs/user-authentication.md (no false positives)
  • infer_traceability() accepts SpecsConfig directly (dependency injection, not path resolution)
  • infer_traceability() returns [] (not an error) when SpecsConfig has no features
  • .specleft/specs/_discovered/ files are excluded (caller loads specs from resolve_specs_dir() which skips _-prefixed dirs)
  • specleft status table shows (convention) tag for convention-matched scenarios
  • Tests in tests/discovery/test_traceability.py using fixture spec files and test files
  • Update scenarios and tests in features/feature-spec-discovery.md to cover the functionality introduced by this issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    new featureIssues or PRs for a new feature that doesn't currently existphase-2Phase 2 spec generationtraceabilityConvention-based traceability

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions