Skip to content

feat(relationships): add decision code-scope Applies To vocabulary [roadmap:decision-to-code-proximity]#284

Merged
tcballard merged 6 commits into
mainfrom
claude/batch-1-residual-followups-cj4dbx
Jul 3, 2026
Merged

feat(relationships): add decision code-scope Applies To vocabulary [roadmap:decision-to-code-proximity]#284
tcballard merged 6 commits into
mainfrom
claude/batch-1-residual-followups-cj4dbx

Conversation

@tcballard

@tcballard tcballard commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements Initiative 1 of the decision-to-code-proximity roadmap (epic #273, sub-issue #274), adopting the rac-decision-applies-to-scope requirement. A decision can now declare the code paths or components it governs, and rac relationships --validate existence-checks those declarations against the repository — closing the "which recorded decisions govern the file I'm editing?" gap as corpus data rather than prose.

Adds:

  • An optional ## Applies To section on decisions, modelled as a first-class applies_to relationship edge.
  • Existence-checking of literal path/directory entries by rac relationships --validate, with a new applies-to-target-not-found issue code.
  • Dogfood on the four evidence ADRs, boundary + adjacent-type tests, and user-facing docs.

Roadmap / ADR Trace

Roadmap:

  • rac/roadmaps/decision-to-code-proximity.md (Initiative 1)

Requirement:

Relevant ADRs:

  • adr-007 — additive JSON contract (new optional section, new issue code; existing corpora byte-identical)
  • adr-018 — RAC directory as canonical root (the .rac/ anchor for path resolution)
  • adr-019 — asset references
  • adr-065 / adr-066 — declared, never inferred; a pure function of declared references and the file tree
  • adr-002 — deterministic, offline (POSIX-normalised, no database)
  • adr-034 — reports which decisions bind, never a compliance judgement

Scope

Included

  • ## Applies To recognised and extracted on decisions only; additive and append-only; classification-neutral.
  • Deterministic slash-or-glob entry classification: a literal path/directory (contains /) is existence-checked; a glob (*/?/[) is recorded as a declared pattern; a component name (no /, no glob) is a recorded label.
  • Path resolution anchored to the nearest .rac/ (ADR-018), POSIX-normalised so results are byte-identical across platforms; absolute or repo-escaping entries cannot name an in-repo scope and are reported not-found.
  • Surfacing: the scope flows through the existing single-schema consumers (get_related, inspect, rac export --graph) as an external, unresolved, filesystem-scoped edge.
  • The four evidence decisions declare checkable scope (adr-018 → rac/, adr-023 → src/rac/, adr-027 → .github/workflows/, adr-033 → src/rac/mcp/).

Excluded

Product / Architecture Decisions

  • First-class edge over a standalone section. ## Applies To is registered in RELATIONSHIP_SECTIONS with an applies_to EdgeSpec, so it auto-surfaces through get_related/inspect/graph — one vocabulary read by every consumer, per the roadmap's "authored once, read everywhere". This keeps the freshness drift gate and Explorer joining on the same reference rather than a parallel one.
  • New declarative filesystem_scoped flag on EdgeSpec distinguishes this edge from the format-linted external edges (related_tickets, verified_by). Like them it skips id resolution, range, and status checks; unlike them its literal entries are existence-checked. No existing edge did filesystem checking (even verified_by, whose targets are file paths, is format-linted, never checked), so this is a new resolution mode rather than an extension of one.
  • Issue code applies-to-target-not-found, severity error. It fails --validate like the other referential-integrity breakages and is additive to the JSON contract.
  • Filesystem-scoped entries do not count toward relationships_checked, consistent with how the external edges are excluded — the metric stays the id-resolution count and is unchanged for existing corpora.
  • docs/artifacts.md left unchanged: its optional-section lists deliberately abridge the external-reference family (Related Tickets / Verified By are already omitted and documented in relationships.md), so Applies To is documented alongside them there, not added to the per-type list.

User-Facing Contract

CLI

No new command in this change. rac relationships DIR --validate gains the code-scope check:

## Applies To
- src/rac/
- .github/workflows/
- src/**/*.py      # recorded pattern, matched by the lookup (#275)
- Explorer         # recorded label

A missing literal path is reported; a glob or component name is not.

Human Output

A flagged entry renders under Broken Relationships as ✗ PATH path not found.

JSON Output

rac relationships --validate --json emits the existing issue shape {source_path, relationship, target, code} with code: "applies-to-target-not-found" and relationship: "applies_to". SARIF carries the reason "declared path does not exist in the repository". No existing field changes; find_decisions output is byte-identical.

Exit Codes

  • 0: every reference (and declared literal path) resolves.
  • 1: one or more validation issues, including a missing ## Applies To path.
  • 2: path not found (usage).

Verification

Ran

  • rac validate rac/PASS — 362 valid, 0 invalid.
  • rac relationships rac/ --validate → exit 0, 2049 relationships checked, 0 issues (the four dogfood ADRs' paths all exist).
  • rac review rac/95/100, no priority 1–2 findings.
  • pytest tests/test_relationship_validation.py tests/test_relationship_graph.py tests/test_schema.py tests/test_schema_agreement.py → 78 passed. Full non-TUI suite: 1616 passed (the two failures — test_explorer_app, test_hook — reproduce on the pristine tree and are environment gaps: an incompatible textual and rac not on PATH).

Covered

  • Existing path and directory resolve clean; missing literal path → applies-to-target-not-found (target + source preserved).
  • Glob (src/**/*.py) and component name (RAC Core, Explorer) recorded, never flagged.
  • Absolute (/etc/passwd) and escaping (../outside/) entries reported not-found rather than followed out of tree.
  • ./-prefixed entry normalises and resolves.
  • Adjacent-type guard: a requirement declaring ## Applies To is relationship-edge-unsupported, not a scope check; the section never shifts decision classification.
  • CLI/JSON/human faces exercised end to end.
  • Schema-, edge-registry-, and cross-consumer-agreement pins updated to include the additive member.

Review Path

  1. src/rac/core/artifacts.py, src/rac/core/relationship_types.py — the schema section and the applies_to edge (filesystem_scoped).
  2. src/rac/services/relationships.py — the classifier, repo-root/POSIX-normalise helpers, and the existence-check pass wired into _validate.
  3. src/rac/output/human.py, src/rac/output/sarif.py — rendering for the new code.
  4. tests/test_relationship_validation.py (+ the three pin updates) — behaviour coverage.
  5. the four evidence ADRs, rac/requirements/rac-decision-applies-to-scope.md, docs/relationships.md — dogfood, status, and docs.

Notes For Reviewer

Implementation Process

Implemented with AI assistance under the roadmap contract; the implementation contract (edge modelling and the slash-or-glob classifier) was ratified, and final scope, review, and acceptance decisions were made by the maintainer.

tcballard added 6 commits July 3, 2026 06:53
…ision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (Initiative 1, #274),
adopting rac-decision-applies-to-scope.

Adds the optional `## Applies To` section to the Decision schema and a
first-class `applies_to` relationship edge. Unlike the format-linted external
edges (related_tickets/verified_by), the edge carries a new declarative
`filesystem_scoped` flag: its literal path entries are existence-checked against
the working tree rather than resolved by identifier. Additive and append-only
(ADR-007); classification-neutral.
…ecision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (Initiative 1, #274).

`rac relationships --validate` now checks each decision's `## Applies To`
entries against the repository file tree, anchored to the nearest `.rac/`
(ADR-018). Deterministic slash-or-glob classification: literal path/directory
entries are existence-checked (missing ones raise the new
`applies-to-target-not-found` error); glob patterns are recorded for the
lookup (#275); component-name labels are recorded without resolution. Paths
normalise to POSIX repo-relative form so results are byte-identical across
platforms (ADR-002); absolute or escaping entries cannot name an in-repo scope
and are reported not-found. Declared, never inferred (ADR-065/066); the human
and SARIF renderers gain a suffix/reason for the new code.
…ion-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (Initiative 1, #274).

Adds boundary coverage for the code-scope edge: existing paths/directories
resolve; missing literal paths raise `applies-to-target-not-found`; glob and
component-name entries are recorded without existence-checking; absolute and
repo-escaping entries are reported not-found; `./`-prefixed entries normalise.
Adjacent-type guard: a non-decision declaring `## Applies To` is an unsupported
edge, not a scope check, and the section never shifts decision classification.
CLI/JSON/human faces are exercised. Updates the schema-, edge-registry-, and
cross-consumer-agreement pins to include the additive member.
…ecision-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (Initiative 1, #274).

Dogfoods the new code-scope vocabulary: the four evidence decisions declare
checkable `## Applies To` scope existence-checked by
`rac relationships --validate` — adr-018 → `rac/`, adr-023 → `src/rac/`,
adr-027 → `.github/workflows/`, adr-033 → `src/rac/mcp/` (a stray unbalanced
code fence at the end of adr-018 is closed in passing). Marks
rac-decision-applies-to-scope Accepted now its vocabulary, validation, and
forward `get_related` surfacing ship; the querying lookup remains
rac-path-decisions-lookup (#275). Closes the decision-scope box on #236.
…-to-code-proximity]

Implements rac/roadmaps/decision-to-code-proximity.md (Initiative 1, #274).

Adds a "Code scope" section covering the decision-only `## Applies To`
vocabulary — the slash-or-glob entry classification, existence-checking of
literal paths, POSIX normalisation, and the graph-export representation — and
lists the new `applies-to-target-not-found` issue code.
…ecision-to-code-proximity]

Collapse two hand-wrapped single-argument calls the formatter keeps on one
line; behaviour unchanged.
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