T09

The repair is to select project_root / config.paths.source_root / "gzkit" in _collect_source_path_literal_issues. The configured directory replaces src; do not scan both or introduce fallback discovery.

Current behavior, established by reading:

  • src/gzkit/config.py defines PathConfig.source_root, defaulting to src, and loads configured values from .gzkit.json.
  • commands/common.py::ensure_initialized loads that file. check_config_paths_cmd already passes the resulting config into the scanner and checks the configured source directory for existence/type.
  • Nevertheless, commands/config_paths.py scans only project_root / "src" / "gzkit". If absent, it returns no literal issues. Thus a relocated package escapes inspection, and a leftover default tree can be inspected incorrectly.
  • The scanner recursively reads sorted Python files, parses AST string constants, and reports repository-relative file paths. It skips missing scan roots and unreadable or syntactically invalid files. Preserve these behaviors for this bounded repair.

Necessary coupled obligations:

  1. Update the scanner’s src/gzkit/ docstring to describe <paths.source_root>/gzkit. Keep its existing config parameter and CLI wiring; no new configuration field, parser flag, or schema is needed.
  2. Preserve classification: known-prefix regex, whitespace handling, URL/length exclusions, global explicit exemptions, manifest coverage and config coverage. Manifest extraction derives parents; config extraction uses configured values without deriving parents. Both feed the existing segment-aware equality/ancestor/descendant matcher. Calling config coverage simply “exact matching” would misdescribe that matcher.
  3. Preserve _AUDIT_SUBJECT_LITERALS: exact declaration name, module-level assignments only, normalized exact literal exemption, scoped to the declaring file. Do not widen the regex to include arbitrary configured source names: relocating the inspected files does not change which literals the audit recognizes.
  4. Update tests/test_config_paths.py and command-level coverage in tests/commands/test_audit.py. Its real-repository acceptance test currently supplies GzkitConfig() instead of loading project configuration. Make that acceptance witness inspect the actual configured package and establish that it exists/contains Python inputs; otherwise a relocation could leave a vacuous green assertion. Preserve the helper’s legitimate missing-package behavior for adopter projects.
  5. Extend docs/user/manpages/check-config-paths.md: its “What It Checks” currently omits source literal scanning entirely. Explain configured package scope, default, replacement behavior, and governed/exempt meaning. Update hardcoded exemption-census examples in scanner/test commentary to describe the configured source tree while identifying src as the default example.
  6. The canonical skill and user skill page describe a generic command workflow; they need no forced behavioral rewrite. If clarifying scope there, edit .gzkit/skills/gz-check-config-paths/SKILL.md, bump skill version/review date, and regenerate package/vendor copies per .gzkit/rules/AGENTS.md; do not manually edit mirrors.

Proposed verification, not executed:

  • First add a failing regression with source_root="application", a nested application/gzkit/.../bad.py, and an unmapped recognized literal such as artifacts/unknown/reports. Assert the exact issue and relocated repository-relative filename.
  • Plant distinguishable violations in both relocated and default package trees. Expect only the relocated violation. Repeat with the configured package absent and a violating default tree present: expect no source issues, proving no fallback.
  • Retain default-root detection and add a nested configured source directory case. Keep fixtures’ manifest/config coverage separate from scan selection so accidental broad exemptions cannot make tests green.
  • Exercise mapped manifest paths, config-only discovery-index coverage, undeclared siblings, explicit exemptions, and all module-declaration scope boundaries under the relocated root. Include a positive violation alongside exemption cases to prove scanning occurred.
  • Use an initialized command fixture with persisted custom configuration and otherwise valid required paths. Verify JSON valid, issue fields, exit 1 for a source violation, and success after its removal; verify human output too. Retain existing missing-path and legacy-OBPI tests.
  • Subsequently run relevant unittest modules, repository quality checks, and strict documentation validation in the complete checkout. No pass result is claimed here.

Uncertainty: absolute/outside-project paths and symlinks are not specified; existing relative reporting may constrain them. Do not silently add a containment policy. Historical ownership, release routing, and complete-checkout execution remain unassessed.

Read inventory (snapshot-relative): full content of src/gzkit/commands/config_paths.py, tests/test_config_paths.py, src/gzkit/config.py, .gzkit.json, .gzkit/skills/gz-check-config-paths/SKILL.md, docs/user/manpages/check-config-paths.md, docs/user/skills/gz-check-config-paths.md; excerpts of src/gzkit/commands/common.py, src/gzkit/cli/parser_maintenance.py, tests/commands/test_audit.py, docs/user/runbook.md, .gzkit/rules/AGENTS.md (output truncated). Searches inspected matching lines across snapshot Python/JSON/config/skill files and .gzkit/rules, docs/user; broad output was truncated. Attempted .gzkit/rules/config-paths.md was missing. Also read /tmp/gzkit-remedy-inputs/T09.md.

Actual shell/read calls: 7, including task read and this response write. No checks or source edits executed.