T03

The necessary behavior change is in _collect_source_path_literal_issues: resolve the scan directory as project_root / config.paths.source_root / "gzkit". The existing config argument already reaches this function. Replace the default location; do not scan both locations or fall back to src/gzkit.

Evidence and coupled obligations:

  1. Configuration → scan population. config.py declares PathConfig.source_root, defaulting to src; GzkitConfig.load preserves supplied paths. commands/common.py::ensure_initialized loads the project’s .gzkit.json. check_config_paths_cmd already uses that configuration for required-directory validation and passes it into the collector. The collector nevertheless hardcodes src/gzkit and returns no issues when that package directory is absent. Thus a relocated tree can satisfy directory validation while its literals escape inspection. This is static code evidence, not an executed reproduction. Config-first policy in the hardcoded-root-eradication chore supports removing that resource-location assumption. The task settles that the package suffix remains gzkit.

  2. Population selection must preserve classification. Keep the AST walk, recognized-prefix regex, URL/length exclusions, central exemptions, and per-module declaration handling unchanged. _flatten_manifest_paths adds ancestor mappings; _flatten_config_paths contributes normalized effective values without deriving ancestors. Their union uses exact or segment-boundary ancestor/descendant coverage. Module _AUDIT_SUBJECT_LITERALS declarations instead grant only normalized exact, same-module exemptions, and only from module-level assignments. Existing tests explicitly defend siblings, declaration names, scope and parent boundaries. Relocation does not authorize expanding recognized literal prefixes or adding exemptions.

  3. Consumer output and proof must still witness scanning. The handler aggregates issues into {valid, issues}, renders human output or JSON, and exits 1 on issues. Parser registration already forwards --json; no new flag or schema is needed. Both chore acceptance files consume this exit status, so a false clean scan weakens their evidence. Preserve project-relative POSIX issue paths under the new directory, consistent with cross-platform rules. The real-repository acceptance test currently passes GzkitConfig() rather than loading the repository’s config: update that consumer to use the actual project configuration, or relocation would leave its apparent acceptance witness pointed at the old tree.

  4. Explanatory surfaces. Update the collector docstring and manpage to explain <paths.source_root>/gzkit, its default, replacement semantics and the existing governed/exempt classification. The manpage’s current checklist omits source scanning. The hardcoded-root-eradication chore and scanner/test comments call a census restricted to src/ “COMPLETE”; make that census guidance follow the configured source population, retaining the exact declaration-name requirement. The primary skill and runbook already invoke the correct command and promise no incompatible output form; no routing change is indicated. Tool/skill/runbook alignment requires checking these relationships, not manufacturing new CLI surfaces.

Proposed verification, not performed:

  • Add a failing tempfile/unittest case with configuration loaded from .gzkit.json, a nested relocated root, and a genuinely unmapped recognized literal. Assert the relocated relative issue path.
  • Put distinguishable violations in both old and configured packages: only the configured package must contribute. Also test a missing configured package with an existing old package to rule out fallback; preserve existing missing-package behavior separately from required-directory errors.
  • Exercise governed manifest/config literals and all exemption boundaries under the relocated root, alongside an unexempt negative control so an empty scan cannot pass.
  • Add command-level human/JSON checks with otherwise valid fixtures: failure for relocated unmapped content, success after its legitimate disposition, unchanged schema and exit semantics. Existing default-layout tests remain regression coverage.
  • Subsequently run focused tests, required repository checks and documentation validation, retaining real outputs. No execution evidence exists here.

Reconciliation and limits: scanner prose says config “defaults,” but implementation uses effective configured values; preserve that behavior and clarify wording. Manifest generation can prefer detected structure over config, so manifest and config may disagree; the requested scan authority is configuration, while manifest coverage remains unchanged. Broad chore language about eliminating all literals is qualified by its explicit audit-subject carve-out. Absolute/outside-root configurations, skipped unreadable or invalid Python, and general manifest-resolution hardcoding are independent leads, not required expansion. Their disposition and omitted historical authorities remain unverified.

Read inventory (relative to /tmp/gzkit-remedy-snapshot; excerpts where noted): src/gzkit/{config.py,commands/config_paths.py,commands/common.py (excerpt),sync.py (excerpt),sync_surfaces.py (excerpts),cli/parser_maintenance.py (excerpt)}; tests/{test_config_paths.py,commands/test_audit.py (excerpts)}; .gzkit.json; .gzkit/skills/gz-check-config-paths/SKILL.md; .gzkit/rules/{tool-skill-runbook-alignment,tests,chores,cli,cross-platform}.md; src/gzkit/chores/{hardcoded-root-eradication,config-paths-remediation}/{CHORE.md,acceptance.json}; docs/user/{manpages/check-config-paths.md,runbook.md (excerpt)}. Rules batch output was partly truncated. Also read /tmp/gzkit-remedy-inputs/T03.md; searches/listings stayed inside the corpus. Actual shell/read calls: 13, including this output write. This bounded trace is not proof of completeness.