Context
PR #15997 corrected three live references from ticket-create-workflow.md §1c to §1d. The source target is unambiguous: §1c is the Agent OS Structure Map Gate, while §1d is the Ungraduated-Discussion Cross-Check. During exact-head review, removing inline-code backticks from the filename was expected to make those corrected references mechanically validated.
A direct positive/negative probe falsified that expectation. checkSkillReferenceIntegrity() returned [] for all four of these forms:
ticket-create-workflow.md §1d
ticket-create-workflow.md §1x
`ticket-create-workflow.md` §1d
`ticket-create-workflow.md` §1x
The valid and deliberately invalid digit-leading anchors are therefore indistinguishable to the lint.
The Problem
The skill-reference lint has two interacting silent-green paths:
SECTION_REF_SOURCE accepts dotted numeric identifiers such as §5.1 or letter-first named identifiers such as §template-block, but not digit-leading alphanumeric identifiers such as §1d.
- Inline-code-wrapped filename targets are not associated with following prose refs. A backticked numeric ref can then fall back to the containing file and emit a misleading dangling-reference error, while an unqualified nonnumeric ref is skipped.
Heading extraction compounds the first gap: ### 1d. The Ungraduated-Discussion Cross-Check is indexed as numeric anchor 1, not 1d. Removing backticks alone therefore does not restore validation.
This is a false proof surface. A green Skill Manifest Lint currently cannot establish that a changed target.md §1d reference resolves, even when the target file exists.
The Architectural Reality
ai/scripts/lint/lint-skill-manifest.mjs:300-308 owns the accepted section-reference and target grammar.
ai/scripts/lint/lint-skill-manifest.mjs:314-329 owns heading-anchor extraction.
ai/scripts/lint/lint-skill-manifest.mjs:644-662 owns target resolution and dangling-reference reporting.
ai/scripts/lint/lint-skill-manifest.mjs:699-725 owns changed-line prose-reference validation and currently skips unqualified nonnumeric matches.
test/playwright/unit/ai/scripts/lint/lintSkillManifest.spec.mjs:619-741 covers dotted numeric and letter-first named references, but has no inline-code-target or digit-leading-alphanumeric controls.
- #12493 introduced numeric reference-integrity coverage; #12582 extended it to named refs. Neither ticket covered
§1d-shaped headings or inline-code target equivalence.
- #11599 is not the owner: it proposes a broad semantic-heading migration and explicitly leaves historical positional refs out of scope.
Structure-map result: the existing owner is ai/scripts/lint/lint-skill-manifest.mjs, with the existing focused sibling spec under test/playwright/unit/ai/scripts/lint/. No new module or directory is needed.
The Fix
Repair the existing parser and focused spec in one PR:
- Give prose refs and heading extraction one shared accepted section-ID grammar that covers the live digit-leading alphanumeric form (
§1d) without regressing dotted numeric (§5.1) or letter-first named (§template-block) forms.
- Normalize inline-code filename targets before target association so
`target.md` §N and target.md §N have the same validation semantics.
- Never silently skip a target-qualified section token. A target that resolves but whose section token is unsupported or missing must produce an actionable error.
- Preserve
--base changed-line ownership so newly recognized historical refs do not block unrelated PRs.
- Add positive and negative controls for each supported target/anchor family.
Contract Ledger Matrix
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
Section-ID grammar and extractHeadingAnchors() in lint-skill-manifest.mjs |
Existing parser; live ticket-create-workflow.md headings; #12493 and #12582 |
Dotted numeric, letter-first named, and live digit-leading alphanumeric IDs are extracted and compared using one grammar |
Unsupported target-qualified tokens fail explicitly; they never disappear from coverage |
Update nearby JSDoc if the accepted grammar is named there |
Unit controls for §5.1, §template-block, §1d, and missing siblings |
Prose target association in checkSkillReferenceIntegrity() |
Existing changed-line reference lint |
Inline-code and bare filename targets are semantically equivalent before section validation |
Numeric refs must not fall back to the containing file merely because the target is backticked |
Actionable lint error names source line, target, and section |
Direct probe above turns from [] into a failure for both §1x forms |
lintSkillManifest.spec.mjs reference-integrity suite |
Existing focused tests from #12493/#12582 |
Locks positive/negative behavior across target formatting and anchor families |
Existing numeric, named, pointer, and changed-line tests remain green |
No new guide required unless syntax policy changes |
Focused Playwright unit run plus lint-skill-manifest --base origin/dev |
Decision Record impact
aligned-with ADR 0008 — this repairs the existing skill-manifest enforcement layer and does not change Progressive Disclosure or skill-reference policy.
Acceptance Criteria
Out of Scope
- Repo-wide heading migration or reopening #11599.
- Changing the divergence-window rule delivered by #15997.
- Rewriting every historical skill reference in the same PR.
- General-purpose Markdown link validation outside
.agents/skills/**.
Avoided Traps
- Removing backticks only: falsified;
§1d remains outside the accepted grammar.
- Treating green lint as evidence that the ref was checked: the direct negative control proves the current green is vacuous.
- Silently banning a live heading form:
§1c/§1d are current workflow anchors. If the long-term heading convention migrates them, that belongs to #11599; the current enforcement must still be truthful.
- Absorbing the repair into #15997: that PR has already exhausted two ordinary Request Changes cycles and its graduated rule is independent of this parser defect.
Related
Follow-up from #15996 / PR #15997. Extends #12493 and #12582. Related but not owned by #11599.
Retrieval Hint: lint-skill-manifest backticked target digit-leading section ref 1d silently skipped
Live latest-open sweep and A2A in-flight claim sweep: recorded immediately before creation.
Context
PR #15997 corrected three live references from
ticket-create-workflow.md §1cto§1d. The source target is unambiguous:§1cis the Agent OS Structure Map Gate, while§1dis the Ungraduated-Discussion Cross-Check. During exact-head review, removing inline-code backticks from the filename was expected to make those corrected references mechanically validated.A direct positive/negative probe falsified that expectation.
checkSkillReferenceIntegrity()returned[]for all four of these forms:The valid and deliberately invalid digit-leading anchors are therefore indistinguishable to the lint.
The Problem
The skill-reference lint has two interacting silent-green paths:
SECTION_REF_SOURCEaccepts dotted numeric identifiers such as§5.1or letter-first named identifiers such as§template-block, but not digit-leading alphanumeric identifiers such as§1d.Heading extraction compounds the first gap:
### 1d. The Ungraduated-Discussion Cross-Checkis indexed as numeric anchor1, not1d. Removing backticks alone therefore does not restore validation.This is a false proof surface. A green Skill Manifest Lint currently cannot establish that a changed
target.md §1dreference resolves, even when the target file exists.The Architectural Reality
ai/scripts/lint/lint-skill-manifest.mjs:300-308owns the accepted section-reference and target grammar.ai/scripts/lint/lint-skill-manifest.mjs:314-329owns heading-anchor extraction.ai/scripts/lint/lint-skill-manifest.mjs:644-662owns target resolution and dangling-reference reporting.ai/scripts/lint/lint-skill-manifest.mjs:699-725owns changed-line prose-reference validation and currently skips unqualified nonnumeric matches.test/playwright/unit/ai/scripts/lint/lintSkillManifest.spec.mjs:619-741covers dotted numeric and letter-first named references, but has no inline-code-target or digit-leading-alphanumeric controls.§1d-shaped headings or inline-code target equivalence.Structure-map result: the existing owner is
ai/scripts/lint/lint-skill-manifest.mjs, with the existing focused sibling spec undertest/playwright/unit/ai/scripts/lint/. No new module or directory is needed.The Fix
Repair the existing parser and focused spec in one PR:
§1d) without regressing dotted numeric (§5.1) or letter-first named (§template-block) forms.`target.md` §Nandtarget.md §Nhave the same validation semantics.--basechanged-line ownership so newly recognized historical refs do not block unrelated PRs.Contract Ledger Matrix
extractHeadingAnchors()inlint-skill-manifest.mjsticket-create-workflow.mdheadings; #12493 and #12582§5.1,§template-block,§1d, and missing siblingscheckSkillReferenceIntegrity()[]into a failure for both§1xformslintSkillManifest.spec.mjsreference-integrity suitelint-skill-manifest --base origin/devDecision Record impact
aligned-with ADR 0008— this repairs the existing skill-manifest enforcement layer and does not change Progressive Disclosure or skill-reference policy.Acceptance Criteria
ticket-create-workflow.md §1dreference resolves to the live### 1d.heading.ticket-create-workflow.md §1xreference fails with an actionable dangling/unsupported section error.--basechanged-line ownership remains intact.node ai/scripts/lint/lint-skill-manifest.mjs --base origin/dev; do not use the default unscopednpx playwright test.Out of Scope
.agents/skills/**.Avoided Traps
§1dremains outside the accepted grammar.§1c/§1dare current workflow anchors. If the long-term heading convention migrates them, that belongs to #11599; the current enforcement must still be truthful.Related
Follow-up from #15996 / PR #15997. Extends #12493 and #12582. Related but not owned by #11599.
Retrieval Hint:
lint-skill-manifest backticked target digit-leading section ref 1d silently skippedLive latest-open sweep and A2A in-flight claim sweep: recorded immediately before creation.