os-aidd-lint: dead-reference-pointer path-resolution false positives (consolidated: outside-repo, placeholder, relative-CLI, runtime-artifact) #150

Open
opened 2026-07-26 21:54:03 +00:00 by jared · 2 comments
Owner

Problem

DeadReferencePointer/PathReferenceScanner false-positive on real paths that
aren't resolvable relative to the referencing file, the repo root, or the
plugin root — the three bases PathReferenceScanner.candidate_paths tries.
Consolidated 2026-08-01 from #110 and #129 (three of #110's original classes
are folded in here; its ${CLAUDE_PLUGIN_ROOT} class is already fixed, see
Fixed section below).

Confirmed still-reproducing classes (verified against this checkout)

  1. Documented runtime location outside the repo (original #150). Example:
    plugins/os-vault/skills/write/SKILL.md:39 — "read vault-conventions.md
    at the vault root" resolves to ~/Documents/SecondBrain/vault-conventions.md,
    a real file the repo-scoped resolver can never see. Prose states the
    location explicitly; the cop has no notion of it.

  2. Placeholder path segments beyond date-shaped tokens (from #129).
    PLACEHOLDER_TOKEN only recognizes YYYY|MM|DD. Confirmed live: See `tmp/aidd-tier2/NN.prompt.md` for details. still flags dead today even
    though NN is a documented zero-padded-index placeholder, not a filename.
    (The four original #129 repro lines no longer match only because the docs
    were reworded, not because this was fixed — the underlying gap is
    unchanged and will resurface on the next such doc.)

  3. Relative-path invocation breaks repo-root detection (from a #129
    comment, previously unverified — now confirmed reproducing). Running
    bin/aidd-lint from inside a subdirectory with a relative path arg finds
    4 dead-reference-pointer hits on plugins/os-doc-hygiene/skills/clean/SKILL.md;
    the identical file passed as an absolute path finds 1. Root walk appears
    to resolve relative to CWD rather than escaping to the true repo root. This
    looks like a plain bug (not a design-tradeoff decision) — worth fixing
    independent of 1/2/4 below.

  4. Runtime-generated artifact paths (from #110). Paths under
    directories that only exist after a run — .claude/plugin-data/**,
    .cc-os/** — are never on disk at lint time. Confirmed live hits:
    plugins/cc-architect/skills/cc-architect/SKILL.md:36
    .claude/plugin-data/cc-architect/plans/current-plan.md;
    plugins/os-doc-hygiene/skills/check/SKILL.md:315
    .cc-os/dochygiene/report.md.

Classes 1, 2 and 4 plausibly share one general fix direction (per #129's
original list): recognize a placeholder/runtime-path convention, or scope
checkable references to paths whose parent directory resolves on disk
(runtime-generated files fail that test the same way placeholders do). Class
3 is a distinct, separately fixable defect.

Not carried forward

  • #110's third class ("bare sibling filenames resolved from the wrong base
    dir") was checked against its own listed example
    (plugins/os-doc-hygiene/skills/clean/SKILL.md:512, (see `distill.md`),
    real file at workflows/distill.md). DeadReferencePointer's own code
    comment (lib/aidd_lint/cops/dead_reference_pointer.rb:76-86) explicitly
    rejects repo/subdir-wide bare-filename search outside CLAUDE.md-class files,
    because it could silently resolve a genuinely broken reference against an
    unrelated same-named file — masking exactly what this cop exists to catch.
    That's an intentional design tradeoff, not a bug; the actual fix is
    tightening the doc to write the qualified path (as the same SKILL.md
    already does three other times). No action needed here.
  • #110's ${CLAUDE_PLUGIN_ROOT}-prefixed-path class is already fixed
    (PathReferenceScanner's WHOLE_PATH_PREFIX/truncated-match handling,
    landed via #162; regression test at
    plugins/os-aidd-lint/tests/path_reference_scanner_test.rb:156).

Decision needed before implementing 1/2/4

PathReferenceScanner is shared with Tier 2's AuthoritySourceResolver
(ADR-0060) — widening the matcher has blast radius beyond this cop. None of
the fix directions (placeholder convention, opt-out marker, parent-dir-exists
scoping, outside-repo location resolution) are decided. This needs a
maintainer call, not an implementation attempt. Class 3 (relative-path CLI
bug) does not require this decision and can be fixed independently.

Blocks

DeadReferencePointer stays demoted to :advisory (not :offense) until this
is resolved — blocks #108's ambient-hook rollout for the cop.

## Problem DeadReferencePointer/PathReferenceScanner false-positive on real paths that aren't resolvable relative to the referencing file, the repo root, or the plugin root — the three bases `PathReferenceScanner.candidate_paths` tries. Consolidated 2026-08-01 from #110 and #129 (three of #110's original classes are folded in here; its `${CLAUDE_PLUGIN_ROOT}` class is already fixed, see Fixed section below). ## Confirmed still-reproducing classes (verified against this checkout) 1. **Documented runtime location outside the repo** (original #150). Example: `plugins/os-vault/skills/write/SKILL.md:39` — "read `vault-conventions.md` at the vault root" resolves to `~/Documents/SecondBrain/vault-conventions.md`, a real file the repo-scoped resolver can never see. Prose states the location explicitly; the cop has no notion of it. 2. **Placeholder path segments beyond date-shaped tokens** (from #129). `PLACEHOLDER_TOKEN` only recognizes `YYYY|MM|DD`. Confirmed live: `` See `tmp/aidd-tier2/NN.prompt.md` for details. `` still flags dead today even though `NN` is a documented zero-padded-index placeholder, not a filename. (The four original #129 repro lines no longer match only because the docs were reworded, not because this was fixed — the underlying gap is unchanged and will resurface on the next such doc.) 3. **Relative-path invocation breaks repo-root detection** (from a #129 comment, previously unverified — now confirmed reproducing). Running `bin/aidd-lint` from inside a subdirectory with a *relative* path arg finds 4 dead-reference-pointer hits on `plugins/os-doc-hygiene/skills/clean/SKILL.md`; the identical file passed as an *absolute* path finds 1. Root walk appears to resolve relative to CWD rather than escaping to the true repo root. This looks like a plain bug (not a design-tradeoff decision) — worth fixing independent of 1/2/4 below. 4. **Runtime-generated artifact paths** (from #110). Paths under directories that only exist after a run — `.claude/plugin-data/**`, `.cc-os/**` — are never on disk at lint time. Confirmed live hits: `plugins/cc-architect/skills/cc-architect/SKILL.md:36` → `.claude/plugin-data/cc-architect/plans/current-plan.md`; `plugins/os-doc-hygiene/skills/check/SKILL.md:315` → `.cc-os/dochygiene/report.md`. Classes 1, 2 and 4 plausibly share one general fix direction (per #129's original list): recognize a placeholder/runtime-path convention, or scope checkable references to paths whose *parent directory* resolves on disk (runtime-generated files fail that test the same way placeholders do). Class 3 is a distinct, separately fixable defect. ## Not carried forward - #110's third class ("bare sibling filenames resolved from the wrong base dir") was checked against its own listed example (`plugins/os-doc-hygiene/skills/clean/SKILL.md:512`, `` (see `distill.md`) ``, real file at `workflows/distill.md`). `DeadReferencePointer`'s own code comment (lib/aidd_lint/cops/dead_reference_pointer.rb:76-86) explicitly rejects repo/subdir-wide bare-filename search outside CLAUDE.md-class files, because it could silently resolve a genuinely broken reference against an unrelated same-named file — masking exactly what this cop exists to catch. That's an intentional design tradeoff, not a bug; the actual fix is tightening the doc to write the qualified path (as the same SKILL.md already does three other times). No action needed here. - #110's `${CLAUDE_PLUGIN_ROOT}`-prefixed-path class is already fixed (PathReferenceScanner's `WHOLE_PATH_PREFIX`/truncated-match handling, landed via #162; regression test at `plugins/os-aidd-lint/tests/path_reference_scanner_test.rb:156`). ## Decision needed before implementing 1/2/4 `PathReferenceScanner` is shared with Tier 2's `AuthoritySourceResolver` (ADR-0060) — widening the matcher has blast radius beyond this cop. None of the fix directions (placeholder convention, opt-out marker, parent-dir-exists scoping, outside-repo location resolution) are decided. This needs a maintainer call, not an implementation attempt. Class 3 (relative-path CLI bug) does not require this decision and can be fixed independently. ## Blocks DeadReferencePointer stays demoted to `:advisory` (not `:offense`) until this is resolved — blocks #108's ambient-hook rollout for the cop.
jared changed title from os-aidd-lint: dead-reference-pointer false-positive on runtime paths outside the repo to os-aidd-lint: dead-reference-pointer path-resolution false positives (consolidated: outside-repo, placeholder, relative-CLI, runtime-artifact) 2026-08-01 16:47:08 +00:00
Author
Owner

This was generated by AI during triage.

Consolidated with #110 and #129 per the joint-triage request in this ticket's own body. Verified against the current checkout: outside-repo (this ticket), placeholder-segment, runtime-artifact, and a newly-confirmed relative-path CLI bug all still reproduce. ${CLAUDE_PLUGIN_ROOT} (part of #110) and the bare-sibling-filename case (part of #110) do not need further work — see body for why. Full detail moved into the description above; #110 and #129 closed as duplicates.

> *This was generated by AI during triage.* Consolidated with #110 and #129 per the joint-triage request in this ticket's own body. Verified against the current checkout: outside-repo (this ticket), placeholder-segment, runtime-artifact, and a newly-confirmed relative-path CLI bug all still reproduce. ${CLAUDE_PLUGIN_ROOT} (part of #110) and the bare-sibling-filename case (part of #110) do not need further work — see body for why. Full detail moved into the description above; #110 and #129 closed as duplicates.
Author
Owner

Frozen in the 2026-08-16 backlog reset — see #419 for the expiry procedure. Do not work unless a live run rediscovers this issue.

Frozen in the 2026-08-16 backlog reset — see #419 for the expiry procedure. Do not work unless a live run rediscovers this issue.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jared/cc-os#150
No description provided.