Skip to content

feat(relationships): add decision applies-to scope and path lookup [roadmap:decision-to-code-proximity]#251

Closed
tcballard wants to merge 11 commits into
mainfrom
claude/rac-core-review-oi3v4f
Closed

feat(relationships): add decision applies-to scope and path lookup [roadmap:decision-to-code-proximity]#251
tcballard wants to merge 11 commits into
mainfrom
claude/rac-core-review-oi3v4f

Conversation

@tcballard

@tcballard tcballard commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements rac/roadmaps/decision-to-code-proximity.md — Tranche A, rank 1 of the deterministic-substrate programme (epic #248) and gap 7 of the relationship-vocabulary programme (epic #236) in one change, under the new ADR-098.

Adds:

  • ## Applies To on decisions: declared path globs or component labels naming the code a decision governs, registered as the applies_to external edge.
  • rac decisions PATH [DIRECTORY] — the deterministic "which decisions govern this path" lookup.
  • decisions_for_path — the sixth pinned MCP tool, same service underneath.
  • A blocking format lint (malformed-applies-to) in rac validate, and an advisory — never blocking — unmatched-scope warning in rac relationships --validate and rac doctor.
  • ADR-098, the graduated roadmap, the accepted gap-7 requirement, docs, 60 new tests, three new goldens, and dogfood scopes on five real ADRs.

Roadmap / ADR Trace

Roadmap:

  • rac/roadmaps/decision-to-code-proximity.md (graduated from future/ in this PR, per the deterministic-substrate success measures)

Decisions:

  • rac/decisions/adr-098-decision-applies-to-scope-and-path-lookup.md (new) — the entry grammar, the fnmatchcase dialect pin, the lint-vs-advisory split, the lookup contract, and the five-to-six MCP tool change (ADR-030)
  • Follows ADR-087/ADR-096 (external-reference family), ADR-019 (declared references), ADR-067 (bindings, never verdicts), ADR-033 (response budget), ADR-007 (additive)

Requirement:

  • rac/requirements/rac-decision-applies-to-scope.md — Status Proposed to Accepted; all four REQs delivered

Scope

Included

  • applies_to EdgeSpec (external, directional, no provider) — one EXTERNAL_SECTIONS entry routes it through every existing resolution/range/status exemption and the graph export.
  • core/applies_to.py: normalisation, path-vs-label discrimination (whitespace = label; separator or glob char = path; ./ opts a root file into path-hood), malformed reasons, and the governs matcher (fnmatchcase, case-sensitive, directory scope governs its subtree).
  • Advisory infrastructure: a new advisories list on the relationship validation result — excluded from ok, the exit code, and SARIF; JSON key and human block appear only when non-empty; the tree pass discovers the repo root via the nearest .rac config and skips silently on bare corpora.
  • decisions_for_path service in services/resolve.py: one corpus walk, liveness and status from the agent-rules seam (never forked), results sorted by artifact path with matched scopes as evidence.
  • Dogfood: adr-018 governs rac/, adr-023 src/rac/, adr-027 .github/workflows/, adr-033 src/rac/mcp/, adr-035 carries its two component labels.

Excluded

  • Other artifact types than decisions (additive follow-up, per ADR-098).
  • Any code-content inference or component-label resolution (labels are recorded, never resolved).
  • The drift gate itself — this PR is its declared-data precondition; drift lands with freshness-and-drift-detection (Tranche A, rank 2).
  • Custom glob dialects: ** behaves as * in this dialect, pinned by tests.

Product / Architecture Decisions

  • Advisory can never mean blocking, structurally. RelationshipValidation.ok fails on ANY recorded issue regardless of severity, so unmatched scopes live in a separate advisories list rather than a severity level — the reviewed reconciliation of the gap-7 "existence-checked by --validate" requirement with the ADR-096 resolution-exemption precedent.
  • The dialect is a pinned contract. fnmatchcase at the Python 3.11 floor; * crosses /; the test battery fails loudly if the standard library ever shifts semantics.
  • One vocabulary for both programmes. ## Applies To is gap 7's proposed section, so the substrate feature and the relationship-vocabulary gap cannot diverge.
  • adr-018 carried a stray trailing code fence that silently swallowed the document's tail; removed here since the dogfood section exposed it.

User-Facing Contract

CLI

New command: rac decisions PATH [DIRECTORY] [--json] [--top-level]. Exit 0 on any completed lookup including empty; exit 2 on a bad directory or empty path. No existing command changes.

JSON Output

  • rac decisions --json: schema_version, path, type, match_count, matches[{id,type,title,status,path,scopes}].
  • rac relationships --validate --json: additive advisories key, present only when non-empty — existing output byte-identical otherwise.
  • rac schema decision --json: applies_to appended to optional (append-only).

MCP

Sixth pinned tool decisions_for_path (description recorded in ADR-098); payload mirrors find_decisions and inherits ADR-033 whole-item truncation.

Exit Codes

Unchanged meanings; the advisory never alters any exit code.

Verification

Ran

  • python -m pytest -q — 1789 passed, 1 skipped (60 tests added)
  • ruff check src/ tests/, mypy (strict) — clean
  • rac validate rac/, rac relationships rac/ --validate (1705 checked, 0 issues), rac review rac/ (no priority 1-2, health 94), rac export rac/ --agent-rules --check, rac gate rac/ — all exit 0
  • Live demo on this corpus: rac decisions src/rac/mcp/server.py rac/ returns ADR-023 (via src/rac/) and ADR-033 (via src/rac/mcp/) — the requirement's success metric

Covered

  • tests/test_applies_to.py (31): grammar, normalisation, malformed reasons, and the full dialect battery — subtree rule, star-crosses-separators, double-star equivalence, case sensitivity, character classes, no false prefix (rac never governs rac-localview/), empty-query refusal.
  • tests/test_decisions_for_path.py (15): liveness filter (Superseded twin never returned), glob scopes, labels never match, query normalisation (./, backslashes, trailing slash), empty result exit 0, JSON contract, byte-determinism.
  • Advisory: unmatched scope warns while relationships --validate stays exit 0 and gate unaffected; advisories key absent when empty; bare corpora (no .rac config) skip the tree pass; component labels never advise; doctor warns and exits 0.
  • MCP: exact six-tool set and verbatim descriptions, payload equals the service output plus the filter key (one source of truth), retired exclusion, determinism parametrisation; isolation battery green.
  • Goldens: three new files over a config-free fixture corpus; every pre-existing golden byte-identical.

Review Path

  1. rac/decisions/adr-098-decision-applies-to-scope-and-path-lookup.md — the contract everything else implements.
  2. src/rac/core/applies_to.py — grammar + matcher (the normative core).
  3. src/rac/services/relationships.py — the advisories list and the tree pass (the never-blocking guarantee).
  4. src/rac/services/resolve.py — the lookup service.
  5. src/rac/cli.py, src/rac/mcp/server.py, src/rac/output/* — the two faces.
  6. Tests, docs, and the five dogfooded ADRs.

Notes For Reviewer

Implementation Process

Implemented with AI assistance under the roadmap contract and the maintainer-ratified ADR-098 choices (advisory match check; ## Applies To, decisions-only); final scope, review, and acceptance decisions are the maintainer's, via this review.

tcballard added 11 commits July 2, 2026 05:12
…[roadmap:decision-to-code-proximity]

Implements the graduation step required by
rac/roadmaps/deterministic-substrate.md (Tranche A, rank 1): the item
moves out of future/, gains its execution ticket (ADR-093) and its
requirement link (rac-decision-applies-to-scope, gap 7 of the
relationship-vocabulary programme), and joins the substrate programme's
Related Roadmaps.
…map:decision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (contract step).

Settles: the ## Applies To section on decisions (path globs or component
labels), the entry grammar and fnmatchcase dialect, the blocking format
lint vs advisory tree-match split (reconciling the gap-7 requirement
with the ADR-087/096 exemption precedent), and the lookup contract —
rac decisions PATH plus the sixth pinned MCP tool decisions_for_path
(ADR-030 change, ADR-033 budget). Accepts rac-decision-applies-to-scope.
…obs [roadmap:decision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md, Initiative 1
(ADR-098). Adds:
- core/applies_to.py — the entry grammar (path glob vs component label),
  normalisation, format-lint reasons, and the fnmatchcase governs()
  matcher
- the applies_to external edge in the relationship registry, mirroring
  verified_by (external, directional, no provider)
- the section on the decision spec (append-only) and its schema
  description
- the malformed-applies-to validate lint, syntactic and config-free

Adding 'applies to' to EXTERNAL_SECTIONS routes the edge through every
existing external-exempt branch: resolution, range, and status checks
skip it; the graph export carries it unresolved.
…d --validate [roadmap:decision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (ADR-098 advisory
lane). A declared ## Applies To path scope matching nothing in the
working tree is surfaced two ways: an additive advisories list on the
relationship validation result (JSON key and human block present only
when non-empty, excluded from ok and the exit code) and a warning
finding in rac doctor. The pass discovers the repository root via the
nearest .rac config and skips silently on bare corpora, so fixtures and
gates are untouched; advisories stay out of SARIF by construction.
…roximity]

Implements rac/roadmaps/decision-to-code-proximity.md, Initiative 2
(service layer, ADR-098). One corpus walk; liveness and status reuse
the agent-rules seam; matching uses the pinned governs dialect;
component labels never match; results sorted by artifact path with the
matching scopes carried as evidence. Bindings, never verdicts (ADR-067).
…e-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md, Initiative 2
(CLI surface, ADR-098). rac decisions PATH [DIRECTORY] [--json]
[--top-level]: exit 2 on a bad directory or empty path, exit 0 on any
completed query including an empty result. Dedicated human and JSON
renderers; the JSON shape is the ADR-098 contract the MCP tool mirrors.
Implements rac/roadmaps/decision-to-code-proximity.md, Initiative 2
(MCP surface, ADR-098). The sixth pinned tool: scoped grounding through
the same decisions_for_path service the CLI uses, with the pinned
description recorded in ADR-098 (ADR-030 contract change) and the
matches list inheriting ADR-033 whole-item budget truncation.
…and MCP tool [roadmap:decision-to-code-proximity]

Implements the ADR-098 test battery:
- test_applies_to.py pins the entry grammar and the exact fnmatchcase
  dialect (subtree rule, star-crosses-separators, double-star, case
  sensitivity, character classes, no false prefix) so stdlib drift
  fails loudly
- test_decisions_for_path.py covers the service and CLI faces (liveness
  filter, label never matches, empty-result exit 0, JSON contract,
  byte-determinism)
- advisory tests prove unmatched scopes warn without blocking:
  relationships --validate stays exit 0, the advisories JSON key is
  absent when empty, bare corpora skip the tree pass
- doctor warns on stale scopes and still exits zero
- MCP: sixth-tool shape, one-source-of-truth vs the service, retired
  exclusion, determinism parametrisation
- new goldens (decisions_human/json, empty) over a config-free fixture
  corpus; existing goldens byte-identical; both new files join the CI
  battery matrix
…:decision-to-code-proximity]

Adds the rac decisions command reference (inputs, options, exit codes,
JSON shape), the Applies-to scope section in the relationship docs
(grammar, the format-lint vs advisory split), and brings the MCP tool
table up to the actual six pinned tools — it had lagged at four,
missing find_decisions as well as the new decisions_for_path.
…map:decision-to-code-proximity]

The five decisions the gap-7 requirement names as evidence now declare
their scope as data: adr-018 governs rac/, adr-023 src/rac/, adr-027
.github/workflows/, adr-033 src/rac/mcp/, and adr-035 carries the two
component labels its prose stated. Also removes a stray trailing code
fence in adr-018 that had been silently swallowing the document's tail.

rac decisions src/rac/mcp/server.py rac/ now answers with ADR-023 and
ADR-033 — the requirement's success metric, demonstrated on this
repository's own corpus.

Copy link
Copy Markdown
Collaborator Author

Closing as superseded: the decision-to-code-proximity programme landed on main via epic #273 (PRs #284/#285/#286) with the ratified contract — ## Applies To on decisions, rac decisions-for <path>, the find_decisions path argument, and the applies-to-target-not-found validation finding. The ADR number this branch introduced as "ADR-098" has since been assigned to Shared HTTP MCP Serving. The delivered scope is in the CHANGELOG Unreleased section, shipping in the next CalVer release.


Generated by Claude Code

@tcballard tcballard closed this Jul 5, 2026
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.

1 participant