T12

The fix belongs in the shared vendor_mirror_canonical helper, with regression coverage through its consumers. This is a static assessment; no checks were executed.

Current behavior and implementation

In src/gzkit/governance/brief_path_validity.py:94, mirror matching only removes trailing slashes. Consequently ./.claude/rules/tests.md misses the .claude/rules/ prefix. Filesystem resolution accepts that spelling when the file exists; when absent, _normalize_for_creates removes one literal ./, so a matching creation declaration suppresses the existence gap. Both routes evade mirror refusal. Without existence or a declaration, the diagnostic incorrectly reports a missing path rather than the canonical edit surface.

Normalize one literal leading ./ inside vendor_mirror_canonical, before both prefix comparison and suffix extraction. Slicing the original string after matching a normalized string would corrupt canonical advice. Preserve the original Allowed Paths token in the diagnostic. Keep the mapping and its component boundary behavior unchanged: Claude rules and GitHub instructions map to .gzkit/rules; Claude/GitHub/Agents skills map to .gzkit/skills.

Use removeprefix("./"), not lstrip("./"): the latter destroys dot-directory names, explicitly documented by _normalize_for_creates and its regression tests. No filesystem resolution or dependency is needed. Keep the mirror check ahead of existence and creation exemptions (:211–221). No creation declaration may authorize a generated mirror.

Coupled obligations

  • commands/plan_audit_cmd.py:398 gathers brief and plan creation declarations and invokes the shared checker. Preserve this union. Mirror gaps must yield FAIL, exit 1, and canonical advice in both output and the existing receipt; do not change its schema.
  • commands/obpi_cmd.py:958 delegates to the brief wrapper. Verify both single-file and ADR-batch --authored validation. Preserve lenient non-authored behavior; this repair does not newly enable checks there.
  • commands/adr_promote.py:333 checks promoted briefs using the same wrapper. Verify propagation into its structural errors. Its current command checks after applying promotion, and --force bypasses blocking: do not describe this as an atomic pre-write refusal or silently change those semantics.
  • Keep the compatibility alias imported by plan_audit_cmd working. Consumers need tests, not duplicate normalization implementations.
  • Update the shared helper documentation and user guidance in the plan-audit and obpi-validate manpages with a concrete ./ mirror-to-canonical example and the creation-exemption boundary. Explain promotion’s applicable check accurately. Clarify the same invariant in canonical gz-obpi-specify guidance. If skills change, bump their skill version and use the documented synchronization process to deliver packaged/vendor copies; do not edit generated mirrors manually.

Proposed verification

First add failing unittest cases with explicit expected canonical paths for all five mapping entries. Cover prefixed/unprefixed files, directory roots with/without trailing slash, nested suffixes, and globs. Include negative controls for ./.gzkit/..., ordinary source paths, .claude/plans/..., and similarly named non-mirror directories.

Exercise the shared checker against existing mirrors, missing mirrors, and missing mirrors declared created. Assert exactly the mirror diagnostic with intact original token and correct canonical suffix. Test extraction through a real brief and plan, including inline **CREATE** and Creates these files, with mixed prefixed/unprefixed declarations. Preserve canonical net-new creation exemptions, missing ordinary-path failures, glob-root behavior, and empty/scaffold handling.

Extend command regressions for plan receipts/output, authored single/batch validation, and promotion structural-error propagation using otherwise valid fixtures, so unrelated failures cannot satisfy the assertion. Then run focused suites and appropriate repository quality/documentation checks in an authorized implementation session. None have been run here.

Uncertainty and scope

Repeated ./, traversal, absolute paths, symlinks, and new vendor mappings are beyond the requested equivalence. Reconciliation is a separate drift model; its inspected code imports creation extraction, not the mirror checker. I have not established a requirement to extend its schema. Historical ownership and routing cannot be resolved from this bounded corpus. Some search/read output was truncated; this is not an exhaustive repository audit.

Read inventory and count

10 shell/read calls total, including task read and this output write. Task: /tmp/gzkit-remedy-inputs/T12.md. Content reads under /tmp/gzkit-remedy-snapshot/ (some excerpts):

  • src/gzkit/governance/{brief_path_validity,brief_reconcile}.py
  • src/gzkit/commands/{plan_audit_cmd,obpi_cmd,adr_promote}.py
  • tests/governance/test_brief_path_validity.py
  • tests/test_plan_audit_cmd.py
  • tests/commands/test_obpi_validate_cmd.py
  • docs/user/manpages/{plan-audit,obpi-validate,adr-promote}.md
  • .gzkit/skills/{gz-obpi-specify,gz-agent-sync,gz-plan-audit}/SKILL.md

Search-only inspection also covered src/, tests/, .gzkit/, docs/, including tests/commands/test_adr_promote.py; search matches are not whole-file reads.