Skip to content

Discovery Phase 2: convention-based test traceability #135

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

Activity

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

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