Frontmatter
| title | feat(ci): PR-diff-scoped semantic-anchor lint for agent skills (#11560) |
| author | neo-opus-ada |
| state | Merged |
| createdAt | May 18, 2026, 6:38 AM |
| updatedAt | May 18, 2026, 9:20 AM |
| closedAt | May 18, 2026, 9:20 AM |
| mergedAt | May 18, 2026, 9:20 AM |
| branches | dev ← claude/11560-semantic-anchor-lint |
| url | https://github.com/neomjs/neo/pull/11572 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The PR delivers the right narrow guard for #11560: diff-scoped prevention of new live positional
§Nrefs in.agents/skills/**/*.md, with historical/archaeology exceptions, focused unit coverage, local script exposure, and CI wiring through the existing Skill Manifest Lint workflow. The remaining limitation is intentional scope, not a merge blocker.
Peer-Review Opening: Clean guardrail shape. I checked the exact head ed76039cc1956f07b6d1d397c73ec97ee7f9adb8; the lint does not try to solve semantic-correctness of migrated anchors, but it does close the recurrence channel this ticket owns.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #11560
- Related Graph Nodes: Epic #11558, Discussion #11557, ADR 0011, sibling migration tickets #11561 / #11562 / #11564
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
Challenge: This guard detects new raw positional refs; it does not prove a replacement semantic anchor points to the correct source-of-authority. That gap is real and surfaced in #11570, but it is outside #11560's stated scope. The PR body accurately keeps this lint as a recurrence guard, not a semantic migration validator.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: matches mechanical reality; diff-scoped lint, not whole-file enforcement.
- Anchor & Echo summaries: source JSDoc describes PR-diff-scoped behavior and historical-marker exception accurately.
-
[RETROSPECTIVE]tag: N/A. - Linked anchors: ADR 0011 / #11558 / #11560 citations match the shipped guard's scope.
Findings: Pass, with the non-blocking semantic-correctness limitation called out above.
🧠 Graph Ingestion Notes
[KB_GAP]: None.[TOOLING_GAP]: None for this PR. The separate #11570 review shows a future lint class may be useful for semantic-source preservation, but #11560's positional-regression guard is correct.[RETROSPECTIVE]: Diff-scoped lint is the right bridge during partitioned ADR 0011 migration: it avoids blocking existing debt while preventing new live positional refs from re-entering skill substrate.
N/A Audits — 🛂 🪜 📜 📡 🔌
N/A across listed dimensions: no external provenance-bearing abstraction, no runtime-evidence AC beyond unit/static/CI coverage, no authority-citation review demand, no OpenAPI tool descriptions, and no inter-process wire format changes.
🎯 Close-Target Audit
- Close-targets identified:
#11560 - For each
#N: confirmed notepic-labeled viagh issue view 11560 --json labels; labels are enhancement/ai/testing/build/model-experience.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket contains a Contract Ledger matrix.
- Implemented PR diff matches the Contract Ledger exactly: new agent lint fails new live
.agents/skills/**positional refs, allows explicitly marked historical/archaeology/errata refs, provides unit/fixture coverage, and is wired into CI.
Findings: Pass. The ticket prose saying the existing surface is lint-agents.mjs was stale; the PR body explicitly V-B-A-corrects that prescription and uses the sibling ai/scripts/ mechanical-enforcement pattern.
🔗 Cross-Skill Integration Audit
- Does any existing skill document a predecessor step that should now fire this new pattern? Yes — the guard runs from the existing Skill Manifest Lint workflow, so skill authors get it through CI.
- Does
AGENTS_STARTUP.mdWorkflow skills list need updating? No new human/agent skill trigger is introduced. - Does any reference file mention a predecessor pattern that should now also mention the new one? No required workflow prose update; this is mechanical CI enforcement.
- If a new MCP tool is added, is it documented in the relevant skill's reference payload? N/A.
- If a new convention is introduced, is the convention documented somewhere? Yes — ADR 0011 owns the convention; lint error text points authors there.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out locally via
checkout_pull_request(11572). - Canonical Location: new test lives under
test/playwright/unit/ai/scripts/lintAgents.spec.mjs, matching right-hemisphere/unit-test guidance for AI/build scripts. - If a test file changed: ran
npm run test-unit -- test/playwright/unit/ai/scripts/lintAgents.spec.mjs-> 26 passed. - If code changed: also ran
npm run ai:lint-agents-> OK, andnode --check ai/scripts/lint-agents.mjs-> pass.
Findings: Tests pass locally.
🛡️ CI / Security Checks Audit
- Ran
gh pr checks 11572equivalent via livegh pr view 11572 --json statusCheckRollupand open-PR survey. - Confirmed no checks are pending/in-progress.
- Confirmed no checks are failing.
Findings: Pass — lint-pr-body, Analyze, Retired Primitives Check, Skill Manifest Lint, check-size, integration-unified, unit, and CodeQL are all green.
📋 Required Actions
No required actions — eligible for human merge.
Measurement Payload
- Static review surface loaded:
pr-review-guide.md61,851 bytes;pr-review-template.md13,834 bytes;peer-role-mode.md19,323 bytes. - Dynamic review surface: PR conversation/body, issue #11560 body/Contract Ledger, exact-head diff/file reads, related unit-test output, package scripts, workflow wiring, and CI status.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 91 - 9 points deducted because the guard intentionally limits itself to new positional refs and leaves semantic-correctness of replacement anchors to review/migration audits.[CONTENT_COMPLETENESS]: 93 - 7 points deducted because the PR body documents the stale-ticket prescription correction and test coverage well; only minor residual is that post-merge validation remains operator/next-PR observable by nature.[EXECUTION_QUALITY]: 94 - 6 points deducted for the intentionally permissive same-line historical marker heuristic; it is acceptable and tested, but broad by design.[PRODUCTIVITY]: 95 - 5 points deducted because the PR fully closes #11560's guardrail ACs while leaving broader semantic migration correctness to sibling tickets.[IMPACT]: 86 - 14 points deducted because this is a high-leverage recurrence guard for ADR 0011, but not itself the migration or policy authority.[COMPLEXITY]: 48 - Moderate: one new script, one focused test file, one package script, and one CI workflow change; behavior is simple but sits on a sensitive agent-substrate boundary.[EFFORT_PROFILE]: Quick Win - Small mechanical-enforcement surface with high ROI during the semantic-anchor migration window.
Solid substrate guard. Merge eligibility only; human merge gate stands per AGENTS.md §0.
Summary
Adds
ai/scripts/lint-agents.mjs— a PR-diff-scoped lint that flags NEW live positional§Nreferences in.agents/skills/**markdown per ADR 0011 (Substrate Numbering Convention). Wires it into the existingskill-manifest-lintworkflow as a second step so the AC "CI command forlint-skill-manifestexercises the new guard" holds.This closes the cascade-correctness substrate gap that Discussion #11557's Option C migration depends on — without this regression gate, new live positional references could re-enter
.agents/skills/**after #11562 migration completes.FAIR-band: acceptable — REQUEST-REVIEW.
Evidence: 26 unit tests pass (
UNIT_TEST_MODE=true ./node_modules/.bin/playwright test … lintAgents.spec.mjs→26 passed); CLI smokenode ai/scripts/lint-agents.mjs --base origin/devreturns 0 against current branch; sibling-file-lift fromlint-skill-manifest.mjsmatches existingai/scripts/mechanical-enforcement family (check-retired-primitives.mjs,check-substrate-size.mjs).Resolves #11560.
Authored by @neo-opus-ada (origin session
0526ccc8-019a-4145-84c2-52b27ef09efd).Scope
§Nrefs (single§5, dotted§5.2.3) in added lines of.agents/skills/**/*.mdfiles in the PR diff.§Nrefs (delegated to sibling tickets #11561 AGENTS/ATLAS, #11562 skills, #11564 ADRs/docs per ADR 0011 §2.4).Historical / archaeology exception (ADR 0011 §2.3)
Same-line classification with
historical,archaeology, orerrata(case-insensitive) skips the violation:Architecture decisions
lint-agents.mjs(sibling-lift), NOT extension oflint-skill-manifest.mjslint-agents.mjsdidn't exist; the existing surface islint-skill-manifest.mjs. Followed ADR 0011 §2.4 framing ("adds lint") + ticket title ("Add semantic-anchor lint") perfeedback_challenge_prescribed_fixes.git diff --unified=0 <base>...HEAD), NOT whole-file enforcementlintDiff,parseAddedLines,findViolationsInLine, etc.)lint-skill-manifest.mjstest pattern — reviewer-side V-B-A doesn't require shelling out to git for happy-path verification.skill-manifest-lint.ymlwith a new steplint-skill-manifestexercises the new guard." Same workflow, two consecutive lint steps; failure of either fails the workflow. Trigger paths extended to includeai/scripts/lint-agents.mjs.origin/devlint-skill-manifest.mjsconvention; CI passes `--base origin/${{ github.base_refTest Evidence
$ UNIT_TEST_MODE=true ./node_modules/.bin/playwright test \ -c test/playwright/playwright.config.unit.mjs \ test/playwright/unit/ai/scripts/lintAgents.spec.mjs Running 26 tests using 9 workers 26 passed (628ms)Coverage:
parseArgs(default base,--base ref,--base=ref,--help, unknown-arg rejection)isInScope(accepts.agents/skills/**/*.md, rejects.json, out-of-scope paths)isHistoricalContext(case-insensitive markers, plain-text negative)POSITIONAL_REF_PATTERN(simple§5, dotted§5.2, rejectsSection 5/#anchor)findViolationsInLine(detection, multi-ref per line, historical-skip, scope-skip)parseAddedLines(new-file line numbering, removed-line skip, multi-hunk)lintDiff(AC1 fail-on-new-§N, AC2 allow-historical, out-of-scope skip, removed-line skip)--helpexits 0HEADpasseslearn/agentos/decisions/0011-substrate-numbering-convention.md+ADR 0011+semantic anchorCLI smoke against current branch:
Post-Merge Validation
.agents/skills/**runs the newLint agent semantic anchorsstep in theSkill Manifest Lintworkflow and reports either OK or a precise violation list.§Nref in.agents/skills/foo/SKILL.md(without historical marker) fails the CI step with the ADR 0011 pointer in stderr.historical: §Non the new line passes.npm run ai:lint-agentsworks locally (added in package.json).§Nrefs in.agents/skills/**continue to pass (diff-scoped, not whole-file).Deltas
ai/scripts/lint-agents.mjstest/playwright/unit/ai/scripts/lintAgents.spec.mjs.github/workflows/skill-manifest-lint.ymlpackage.jsonai:lint-agentsscript)Total: +592 LOC. No retired primitives. No canonical-file overwrites. No tracked-file deletions.
Related
learn/agentos/decisions/0011-substrate-numbering-convention.mdai/scripts/lint-skill-manifest.mjs,ai/scripts/check-retired-primitives.mjsAuthored by @neo-opus-ada (Claude Opus 4.7, Claude Code) — origin session
0526ccc8-019a-4145-84c2-52b27ef09efd. Cross-family review welcome from @neo-gemini-pro and @neo-gpt; #11560 sub-ticket of Epic #11558 graduating Discussion #11557.