Skip to content

resolve-convention-home: a UTF-8 BOM hides the pointer region, and the shared ladder's step 3 path is cwd-relative as written #3929

Description

@kyle-sexton

Summary

Three defects in the shared convention-home resolver and in the resolution ladder that every consuming skill restates. All three are pre-existing and none of them belongs to any one consuming slice, so they are filed here rather than against a container.

1. A UTF-8 BOM makes a bound pointer region invisible (exit 1)

scripts/../plugins/*/lib/resolve-convention-home.sh scans for <!-- BEGIN GENERATED: convention-home -->. Its trim() strips [:space:], which does not include the UTF-8 byte-order mark, so a BOM immediately before the marker leaves the line unmatched and the file is reported as carrying no region at all.

Reproduced on main at a175a0362. The two runs below differ only by the three leading bytes EF BB BF:

$ head -c 16 "$T/AGENTS.md" | od -c
0000000 357 273 277   <   !   -   -       B   E   G   I   N       G   E

$ bash plugins/planning/lib/resolve-convention-home.sh --root "$T"
no convention-home region in AGENTS.md or CLAUDE.md under ...; ask the operator for the home
EXIT=1

With the BOM removed and nothing else changed:

$ bash plugins/planning/lib/resolve-convention-home.sh --root "$T"
docs/conventions
EXIT=0

Fixture content in both runs:

<!-- BEGIN GENERATED: convention-home -->
`docs/conventions`
<!-- END GENERATED: convention-home -->

Exit 1 is documented as "no pointer line is bound", so a consuming skill takes its soft degrade and serves the default silently. A team whose AGENTS.md is BOM-prefixed, which several Windows editors produce by default, gets its convention ignored with no diagnostic naming the cause. The script is a synced carrier, so the fix belongs in the canonical copy and propagates through scripts/sync-resolve-convention-home.sh.

2. The ladder's step 3 path is cwd-relative as written

The resolution ladder tells a consuming agent to read <home>/authoring-formats/README.md. It never says that the value the resolver prints is repo-relative and must be joined to the root resolved in step 1. An agent whose working directory is not the repository root follows the instruction literally and opens whatever docs/conventions/authoring-formats/README.md exists relative to where it happens to be standing. In the verification run for #3926, that silently opened the publishing repository's own convention document instead of the fixture's, and produced a plausible-looking answer from the wrong file.

The wording is inherited from the copyable ladder block in docs/conventions/authoring-formats/README.md, so the same gap ships in plugins/planning/skills/interview/SKILL.md, plugins/planning/skills/prd/SKILL.md, and plugins/planning/skills/design/SKILL.md. Fixing the owner block and resweeping the three restatements keeps one owner.

Related: the ladder never mentions --root, so step 1's resolved anchor cannot be handed to step 2. This is narrower than it appears, because the script implements the same anchor internally through CLAUDE_PROJECT_DIR, then git top level, then cwd. It only bites when CLAUDE_PROJECT_DIR is unset, the tree is not a git repository, and the working directory is below the root.

3. Exit 3 is described as "grammar failures" but also covers a missing target directory

The exit contract restated in the consuming skills calls exit 3 a grammar failure. A pointer whose target directory does not exist also exits 3, with the message pointer target directory missing. The behaviour is right and step 6 of the ladder already lists "no convention home on disk" as a degrade, so this is a wording defect in the restated contract, not a code defect. It ships identically in interview, prd, and design.

A smaller sibling: "exit 0 prints the home" does not say the home goes to stdout. A caller that captures with $(... 2>&1) and has divergent regions in both root files gets the diagnostic line prepended to the value.

Acceptance criteria

  • A UTF-8 BOM before the BEGIN marker no longer hides the region: the same fixture resolves at exit 0 with and without the BOM, and a test case pins both arms.
  • The fix lands in the canonical copy and every carrier stays byte-identical under scripts/sync-resolve-convention-home.sh --check.
  • The ladder's step 3 states that the printed home is repo-relative and must be joined to the root from step 1, in the owner block in docs/conventions/authoring-formats/README.md and in all three planning restatements.
  • The restated exit contract describes exit 3 in terms that cover a missing pointer target directory as well as a grammar failure, and says exit 0 prints the home on stdout.
  • Any plugin version touched is bumped past its published version with a matching new changelog entry.

Context

Found by the independent verifier that attacked PR #3926 (issue #3924, the design-skill resolver text). None of these reproduces #3924's symptom, so they did not block that merge; defect 1 was reproduced independently before filing, and defect 2 was observed during that verification run.

Refs #3924, #3926

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-readyFully specified and briefed; eligible for autonomous pickup from the frontier.priority: mediumReal value, no hard deadline; normal backlog flow.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions