Tasks: Charter directive-resolution hardening

Mission: charter-directive-resolution-hardening-01M29022 Issues: #4239, #4240 (epic #2519) — deferred from #4194/#4185 Branch: fix/charter-directive-resolution-hardening → PR targets main

Two independent, outcome-preserving WPs on disjoint files. Both may run in parallel.

Subtask Index

IDDescriptionWPParallel
T001Memoize _iter_artifact_paths per-layer scan keyed on (path, mtime) (or pass-scoped), preserving high→low orderingWP01[P]
T002Route the memo through resolve_config_id's per-stem resolve_artifact_urn round-trip so repeated stems reuse the scanWP01[P]
T003Add a counted-scan seam + test: ≤1 scan per layer per resolution pass, and mtime-correct invalidation across passesWP01[P]
T004Verify no resolution-outcome change (tests/charter/ subset) + ruff/format/mypy clean, complexity ≤15WP01[P]
T005Emit a per-token logging WARNING (raw token + normalized form) ONLY on the fully-unresolvable fallback in resolver.directivesWP02[P]
T006Add a caplog test: warns on unresolvable token; silent when token resolves by stem / exact id / known catalog idWP02[P]
T007Verify resolution outcome unchanged + ruff/format/mypy clean, complexity ≤15WP02[P]

Work Packages

WP01 — Memoize directive resolver filesystem scans (#4239)

T001 Memoize the per-layer scan (WP01) T002 Route memo through the round-trip (WP01) T003 Scan-count + mtime-invalidation test (WP01) T004 Verify no outcome change + quality gates (WP01)

  • Goal: One filesystem scan per doctrine layer per directive resolution pass, down from up-to layers×stems. No resolution-outcome change; single overlay-precedence authority preserved.
  • Priority: P1
  • Independent test: a scan-count seam asserts ≤1 scan/layer/pass on a multi-layer fixture; existing tests/charter/test_directive_identity_mapping.py stays green.
  • Requirements: FR-001, FR-002, NFR-001, NFR-002, NFR-003, C-001, C-002
  • Included subtasks:
  • Prompt: tasks/WP01-memoize-directive-resolver-scans.md
  • Dependencies: none

WP02 — Diagnostic for a silently-normalized unresolvable token (#4240)

T005 Emit the per-token WARNING on the unresolved fallback (WP02) T006 caplog test: warn-on-unresolved / silent-on-resolved (WP02) T007 Verify no outcome change + quality gates (WP02)

  • Goal: Surface a per-token WARNING when resolver.directives best-effort normalizes a fully-unresolvable activation token, without changing the resolution result.
  • Priority: P1
  • Independent test: caplog asserts a warning on an unresolvable token and none on a resolvable one; delivered directives unchanged.
  • Requirements: FR-003, FR-004, NFR-002, NFR-003, C-001, C-003
  • Included subtasks:
  • Prompt: tasks/WP02-unresolvable-token-warning.md
  • Dependencies: none

MVP

Both WPs are small and independent; WP01 is the higher-value (perf on the hot resolution path). Either can land first.