T06

The repair should normalize one literal leading ./ inside the shared vendor_mirror_canonical helper, using the normalized string for both prefix matching and suffix extraction. Preserve the original Allowed Paths spelling in diagnostics. Do not use lstrip("./"): it removes meaningful leading dots, as the existing creates normalizer explicitly explains.

Current behavior, inferred from source rather than executed:

  • src/gzkit/governance/brief_path_validity.py:94–101 only removes trailing slashes before matching its five mirror prefixes. ./.claude/rules/tests.md therefore misses detection, although .claude/rules/tests.md maps to .gzkit/rules/tests.md.
  • At lines 197–222, mirror refusal precedes existence and creation exemptions. A prefixed existing mirror passes the filesystem check; a missing prefixed mirror ordinarily produces only a missing-file error. If declared for creation, it can pass entirely because _normalize_for_creates already removes ./.
  • The map covers Claude rules/skills, GitHub skills/instructions, and agents skills. Normalize before computing the tail; changing only the prefix comparison would corrupt canonical advice by slicing the original string at the wrong offset. Preserve root-directory, trailing-slash, nested-path, and glob suffix behavior.

Coupled obligations:

  1. Repair the shared helper rather than adding separate fixes in command handlers. commands/plan_audit_cmd.py:394–421 unions brief and plan creation declarations before calling the shared checker. commands/obpi_cmd.py:957–977 uses the brief wrapper; both single and batch validation invoke it only under --authored (lines 1006 and 1056). commands/adr_promote.py:336–367 also uses the wrapper. These consumers should inherit identical refusal and canonical advice without changed signatures or duplicated mapping tables.
  2. Retain refusal before creation exemptions. Cover inline **CREATE**, “Creates these files” sections, and the scaffolded “intentionally created in this OBPI” hint. Canonical net-new paths must remain exempt from existence errors.
  3. Update the helper’s documentation and relevant operator examples in obpi-validate.md and plan-audit.md; both full manpages currently omit this path-validity detail. Explain that a leading ./ changes neither mirror classification nor canonical advice. The primary gz-obpi-specify skill already states mirrors fail regardless of creation; any explanatory addition belongs in the primary skill and its generated distribution/mirrors, not a hand-edited vendor copy.
  4. Preserve existing command boundaries: promotion checks occur after application and --force overrides their structural errors (adr_promote.py:423–437). This task should not silently promise atomic refusal or remove that override.

Proposed verification, not performed:

  • Add failing unittest cases in tests/governance/test_brief_path_validity.py: independently specified canonical results for all five mappings, with/without ./, root with/without trailing slash, nested file, and glob. Assert exact canonical tails.
  • Exercise shared checking with existing mirrors, missing mirrors, and creation declarations. Require a mirror diagnostic containing the original input and correct canonical destination, rather than accepting any error. Pair with canonical and unrelated-path controls, including ./.claude/plans/... and prefix lookalikes such as .claude/rules-extra/....
  • Exercise real brief extraction and plan creation extraction. Extend tests/test_plan_audit_cmd.py to assert exit 1 and a FAIL receipt containing the correct gap for prefixed mirrors, including brief-only and plan-only creates declarations.
  • Verify authored single/batch validation and promotion’s wrapper retain that diagnostic. Keep lenient validation and legitimate creation behavior unchanged. Run focused tests first, then applicable repository quality/docs checks during authorized implementation; no results are claimed here.

Uncertainty: reconciliation is a separate existence/drift engine (governance/brief_reconcile.py) and does not refuse even unprefixed mirrors. Making it enforce a new mirror policy would broaden this spelling-equivalence repair and requires an explicit scope decision. I have not established live work ownership from the intentionally excluded briefs/history, nor exhaustively inspected every validation surface.

Read inventory: /tmp/gzkit-remedy-inputs/T06.md; under /tmp/gzkit-remedy-snapshot/, full reads of src/gzkit/governance/{brief_path_validity,brief_reconcile}.py, tests/governance/test_brief_path_validity.py, and docs/user/manpages/{obpi-validate,plan-audit,adr-promote}.md; excerpts/search matches from src/gzkit/commands/{plan_audit_cmd,obpi_cmd,adr_promote}.py, tests/{test_plan_audit_cmd,test_obpi_template}.py, docs/user/manpages/plan.md, .gzkit/skills/{gz-plan-audit,gz-obpi-specify}/SKILL.md. Also performed snapshot file inventory and symbol searches across supplied source/tests/user-docs/primary-skills/rules; search matches were not whole-file reads. Some attempted filenames did not exist. Actual shell/read call count: 11, including task read and this output write. No checks or source edits executed.