@erclx/canon 4.66.0 → 4.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/claude/.claude-plugin/plugin.json +1 -1
  2. package/claude/skills/{claude-autoship → auto-ship}/REQUIREMENT.md +3 -3
  3. package/claude/skills/{claude-autoship → auto-ship}/SKILL.md +36 -36
  4. package/claude/skills/canon-cli/REQUIREMENT.md +2 -2
  5. package/claude/skills/canon-cli/SKILL.md +2 -2
  6. package/claude/skills/canon-feedback-triage/REQUIREMENT.md +1 -1
  7. package/claude/skills/canon-feedback-triage/SKILL.md +3 -3
  8. package/claude/skills/canon-operator/REQUIREMENT.md +1 -1
  9. package/claude/skills/canon-operator/SKILL.md +4 -4
  10. package/claude/skills/canon-rollout/REQUIREMENT.md +6 -6
  11. package/claude/skills/canon-rollout/SKILL.md +7 -7
  12. package/claude/skills/{claude-design-extract → design-extract}/REQUIREMENT.md +4 -4
  13. package/claude/skills/{claude-design-extract → design-extract}/SKILL.md +1 -1
  14. package/claude/skills/{claude-docs → docs-fold}/REQUIREMENT.md +3 -3
  15. package/claude/skills/{claude-docs → docs-fold}/SKILL.md +14 -14
  16. package/claude/skills/{claude-docs → docs-fold}/references/anchor-sweep.md +1 -1
  17. package/claude/skills/{claude-docs → docs-fold}/references/wireframe-sweep.md +1 -1
  18. package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
  19. package/claude/skills/docs-sync/SKILL.md +1 -1
  20. package/claude/skills/draft-and-pick/REQUIREMENT.md +4 -4
  21. package/claude/skills/draft-and-pick/SKILL.md +6 -6
  22. package/claude/skills/draft-context/REQUIREMENT.md +2 -2
  23. package/claude/skills/draft-context/SKILL.md +3 -3
  24. package/claude/skills/{claude-diagram → draft-diagram}/REQUIREMENT.md +4 -4
  25. package/claude/skills/{claude-diagram → draft-diagram}/SKILL.md +3 -3
  26. package/claude/skills/draft-docs/REQUIREMENT.md +1 -1
  27. package/claude/skills/draft-wireframes/REQUIREMENT.md +1 -1
  28. package/claude/skills/draft-wireframes/SKILL.md +2 -2
  29. package/claude/skills/git-followup/REQUIREMENT.md +1 -1
  30. package/claude/skills/git-followup/SKILL.md +1 -1
  31. package/claude/skills/git-pr/SKILL.md +3 -3
  32. package/claude/skills/git-ship/REQUIREMENT.md +2 -2
  33. package/claude/skills/git-ship/SKILL.md +8 -8
  34. package/claude/skills/git-worktree/REQUIREMENT.md +2 -2
  35. package/claude/skills/git-worktree/SKILL.md +3 -3
  36. package/claude/skills/identity/REQUIREMENT.md +2 -2
  37. package/claude/skills/identity/SKILL.md +3 -3
  38. package/claude/skills/{claude-markdown-propose → markdown-propose}/REQUIREMENT.md +8 -8
  39. package/claude/skills/{claude-markdown-propose → markdown-propose}/SKILL.md +5 -5
  40. package/claude/skills/{claude-markdown-propose → markdown-propose}/references/format.md +1 -1
  41. package/claude/skills/{claude-memory-capture → memory-capture}/REQUIREMENT.md +5 -5
  42. package/claude/skills/{claude-memory-capture → memory-capture}/SKILL.md +13 -13
  43. package/claude/skills/{claude-memory-review → memory-review}/REQUIREMENT.md +4 -4
  44. package/claude/skills/{claude-memory-review → memory-review}/SKILL.md +10 -10
  45. package/claude/skills/migration-context/SKILL.md +1 -1
  46. package/claude/skills/migration-standards-drop/REQUIREMENT.md +1 -1
  47. package/claude/skills/migration-superseded/REQUIREMENT.md +1 -1
  48. package/claude/skills/{claude-feature → plan-feature}/REQUIREMENT.md +4 -4
  49. package/claude/skills/{claude-feature → plan-feature}/SKILL.md +4 -4
  50. package/claude/skills/{claude-groundwork → plan-groundwork}/REQUIREMENT.md +5 -5
  51. package/claude/skills/{claude-groundwork → plan-groundwork}/SKILL.md +8 -8
  52. package/claude/skills/{claude-intake → plan-intake}/REQUIREMENT.md +6 -6
  53. package/claude/skills/{claude-intake → plan-intake}/SKILL.md +10 -10
  54. package/claude/skills/{claude-intake-answer → plan-intake-answer}/REQUIREMENT.md +4 -4
  55. package/claude/skills/{claude-intake-answer → plan-intake-answer}/SKILL.md +5 -5
  56. package/claude/skills/{claude-address-review → review-address}/REQUIREMENT.md +5 -5
  57. package/claude/skills/{claude-address-review → review-address}/SKILL.md +6 -6
  58. package/claude/skills/{claude-address-review → review-address}/references/rebase-conflicts.md +1 -1
  59. package/claude/skills/{claude-review → review-branch}/REQUIREMENT.md +4 -4
  60. package/claude/skills/{claude-review → review-branch}/SKILL.md +4 -4
  61. package/claude/skills/{claude-pr-review → review-pr}/REQUIREMENT.md +5 -5
  62. package/claude/skills/{claude-pr-review → review-pr}/SKILL.md +11 -11
  63. package/claude/skills/{claude-orchestrate → role-orchestrator}/REQUIREMENT.md +5 -5
  64. package/claude/skills/{claude-orchestrate → role-orchestrator}/SKILL.md +20 -20
  65. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-dispatch.md +30 -30
  66. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-handoff.md +2 -2
  67. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-parked.md +8 -8
  68. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-poll.md +8 -8
  69. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-resume.md +1 -1
  70. package/claude/skills/{claude-orchestrate → role-orchestrator}/references/orchestrator-sweep.md +1 -1
  71. package/claude/skills/{claude-orchestrate → role-orchestrator}/scripts/poll.sh +6 -6
  72. package/claude/skills/{claude-planner → role-planner}/REQUIREMENT.md +12 -12
  73. package/claude/skills/{claude-planner → role-planner}/SKILL.md +4 -4
  74. package/claude/skills/{claude-worker → role-worker}/REQUIREMENT.md +8 -8
  75. package/claude/skills/{claude-worker → role-worker}/SKILL.md +6 -6
  76. package/claude/skills/{claude-seed-sync → seed-sync}/REQUIREMENT.md +2 -2
  77. package/claude/skills/{claude-seed-sync → seed-sync}/SKILL.md +3 -3
  78. package/claude/skills/session-map/REQUIREMENT.md +2 -2
  79. package/claude/skills/session-map/SKILL.md +2 -2
  80. package/claude/skills/session-resume/REQUIREMENT.md +2 -2
  81. package/claude/skills/session-resume/SKILL.md +2 -2
  82. package/claude/skills/{claude-worktree → session-worktree}/REQUIREMENT.md +3 -3
  83. package/claude/skills/{claude-worktree → session-worktree}/SKILL.md +6 -6
  84. package/claude/skills/setup-plugins/references/plugin-catalog.md +1 -1
  85. package/claude/skills/{claude-standards-audit → standards-audit}/REQUIREMENT.md +2 -2
  86. package/claude/skills/{claude-standards-audit → standards-audit}/SKILL.md +2 -2
  87. package/claude/skills/systematic-debugging/REQUIREMENT.md +1 -1
  88. package/claude/skills/{claude-tasks → task-board}/REQUIREMENT.md +3 -3
  89. package/claude/skills/{claude-tasks → task-board}/SKILL.md +7 -7
  90. package/claude/skills/{claude-teach → teach-workspace}/REQUIREMENT.md +2 -2
  91. package/claude/skills/{claude-teach → teach-workspace}/SKILL.md +4 -4
  92. package/claude/skills/test-first/REQUIREMENT.md +2 -2
  93. package/claude/skills/{claude-ui-test → ui-test}/REQUIREMENT.md +3 -3
  94. package/claude/skills/{claude-ui-test → ui-test}/SKILL.md +3 -3
  95. package/claude/skills/{claude-ux-audit → ux-audit}/REQUIREMENT.md +6 -6
  96. package/claude/skills/{claude-ux-audit → ux-audit}/SKILL.md +5 -5
  97. package/claude/skills/{claude-ux-measure → ux-measure}/REQUIREMENT.md +5 -5
  98. package/claude/skills/{claude-ux-measure → ux-measure}/SKILL.md +5 -5
  99. package/docs/agents/commands.md +4 -1
  100. package/docs/agents/install-and-sync.md +1 -1
  101. package/docs/agents/key-changes.md +3 -3
  102. package/docs/agents/markdown-audit.md +1 -1
  103. package/docs/agents/restated.md +1 -1
  104. package/docs/agents/review-classification.md +1 -1
  105. package/docs/agents/sessions.md +1 -1
  106. package/docs/agents/state-scoped-risk.md +1 -1
  107. package/docs/agents/targets.md +1 -1
  108. package/docs/agents/tasks.md +4 -4
  109. package/docs/agents/teach.md +1 -1
  110. package/docs/target-projects.md +28 -9
  111. package/docs/workflow/ai-workflow.md +84 -84
  112. package/docs/workflow/operating-model.md +17 -17
  113. package/docs/workflow/visual-design-workflow.md +6 -6
  114. package/governance/rules/core/045-memory.md +1 -1
  115. package/governance/rules/core/085-worktrees.md +1 -1
  116. package/package.json +1 -1
  117. package/scripts/core/regen-agent-fixture.sh +1 -1
  118. package/scripts/core/regen-hero.sh +7 -7
  119. package/snippets/claude/decision-memo.md +1 -1
  120. package/src/autoship/paths.ts +1 -1
  121. package/src/claude/cases/all.ts +2 -2
  122. package/src/claude/cases/{claude-workflow.ts → workflow.ts} +33 -33
  123. package/src/commands/migrate.ts +90 -13
  124. package/src/commands/sync.ts +3 -3
  125. package/src/design/components.ts +1157 -4
  126. package/src/design/css.ts +44 -17
  127. package/src/design/tokens.ts +1 -1
  128. package/src/gov/restated.ts +2 -2
  129. package/src/markdown/structure.ts +1 -1
  130. package/src/migrate/plan.ts +13 -5
  131. package/src/migrate/rename.ts +210 -94
  132. package/src/migrate/skill-names.ts +89 -0
  133. package/src/pr/bijection.ts +1 -1
  134. package/src/pr/paths.ts +2 -2
  135. package/src/shipped/references.ts +1 -1
  136. package/src/sync/seeds-report.ts +1 -1
  137. package/src/targets/pulls.ts +2 -2
  138. package/src/tasks/answers.ts +1 -1
  139. package/src/tasks/archive.ts +4 -4
  140. package/src/tasks/record.ts +2 -2
  141. package/src/tasks/validate.ts +1 -1
  142. package/src/teach/fonts.ts +28 -0
  143. package/src/teach/nav.ts +11 -1
  144. package/src/teach/workspace.ts +4 -1
  145. package/standards/groundwork.md +1 -1
  146. package/standards/snippets.md +1 -1
  147. package/standards/tasks.md +3 -3
  148. package/standards/teach.md +2 -2
  149. package/tooling/base/configs/.husky/post-merge +3 -3
  150. package/tooling/claude/reference.md +5 -5
  151. package/tooling/claude/seeds/.claude/hooks/pr-create-log.sh +2 -2
  152. /package/claude/skills/{claude-memory-review → memory-review}/references/receipt-format.md +0 -0
  153. /package/claude/skills/{claude-orchestrate → role-orchestrator}/scripts/watch.sh +0 -0
  154. /package/claude/skills/{claude-teach → teach-workspace}/references/lesson-craft.md +0 -0
  155. /package/claude/skills/{claude-teach → teach-workspace}/references/pedagogy.md +0 -0
  156. /package/claude/skills/{claude-teach → teach-workspace}/references/promotion.md +0 -0
@@ -0,0 +1,89 @@
1
+ import { defineRenameRules, type RenameRules } from '@/migrate/rename'
2
+
3
+ /**
4
+ * The twenty-five shipped skills that carried a `claude-` prefix, and the
5
+ * two-word name each takes instead.
6
+ *
7
+ * The plugin namespace already resolves every one of them as `canon:<name>`,
8
+ * so the prefix bought grouping rather than uniqueness, and the grouping it
9
+ * bought was the wrong axis: it marked which surface a skill reads rather than
10
+ * what the skill does. The prefixes that replace it name a phase or a role, so
11
+ * a listing groups the review triple, the three planning skills, and the three
12
+ * session roles together.
13
+ *
14
+ * Every name takes two words. Ten of these would have landed as a bare single
15
+ * word under a plain strip, and a bare word such as `review` or `docs` is a
16
+ * substring of ordinary prose with no token left for a later sweep to find.
17
+ */
18
+ export const SKILL_NAME_MAP: Readonly<Record<string, string>> = {
19
+ 'claude-address-review': 'review-address',
20
+ 'claude-autoship': 'auto-ship',
21
+ 'claude-design-extract': 'design-extract',
22
+ 'claude-diagram': 'draft-diagram',
23
+ 'claude-docs': 'docs-fold',
24
+ 'claude-feature': 'plan-feature',
25
+ 'claude-groundwork': 'plan-groundwork',
26
+ 'claude-intake': 'plan-intake',
27
+ 'claude-intake-answer': 'plan-intake-answer',
28
+ 'claude-markdown-propose': 'markdown-propose',
29
+ 'claude-memory-capture': 'memory-capture',
30
+ 'claude-memory-review': 'memory-review',
31
+ 'claude-orchestrate': 'role-orchestrator',
32
+ 'claude-planner': 'role-planner',
33
+ 'claude-pr-review': 'review-pr',
34
+ 'claude-review': 'review-branch',
35
+ 'claude-seed-sync': 'seed-sync',
36
+ 'claude-standards-audit': 'standards-audit',
37
+ 'claude-tasks': 'task-board',
38
+ 'claude-teach': 'teach-workspace',
39
+ 'claude-ui-test': 'ui-test',
40
+ 'claude-ux-audit': 'ux-audit',
41
+ 'claude-ux-measure': 'ux-measure',
42
+ 'claude-worker': 'role-worker',
43
+ 'claude-worktree': 'session-worktree',
44
+ }
45
+
46
+ /**
47
+ * The skill rename.
48
+ *
49
+ * It protects nothing. The `aitk` sweep had to guard a sibling repository
50
+ * whose name contained the token, and no string here contains a skill name
51
+ * while meaning something else: the one overlap in the map is
52
+ * `claude-intake` inside `claude-intake-answer`, which the longest-first
53
+ * ordering settles rather than a protected form. Reading that off the output
54
+ * would report a pass either way, since both rows land on a name opening with
55
+ * `plan-intake`, so the ordering is asserted directly.
56
+ *
57
+ * It matches whole tokens. A skill name is a complete name rather than a word
58
+ * stem, and the corpus holds one word that opens with a name and means
59
+ * something else: `wiki/claude/claude-worktrees.md` documents the harness
60
+ * feature, not the skill, and every sibling in that folder is named
61
+ * `claude-<topic>.md` for a Claude Code concept. Sixteen occurrences of it
62
+ * would have moved to a name the folder's own convention contradicts.
63
+ *
64
+ * No article fixup travels with it. `auto-ship` is the one destination opening
65
+ * on a vowel sound, and the corpus spells an article before it once, which is
66
+ * cheaper to repair by hand than to state as a rule the rest of the map never
67
+ * fires.
68
+ *
69
+ * The map's own module and its test state the rename rather than using the old
70
+ * names, so a sweep over them turns each key into its own replacement and
71
+ * leaves a rewriter that matches nothing. The changelog and an eval transcript
72
+ * are excluded on the argument the `aitk` preset already carries: each records
73
+ * what shipped or what a session ran under whatever name was current then, so
74
+ * rewriting one makes it testify to a release or a run that never happened.
75
+ *
76
+ * The record archives need no entry here. They are gitignored, so the tracked
77
+ * listing every sweep reads never reaches them.
78
+ */
79
+ export const SKILL_NAME_RULES: RenameRules = defineRenameRules({
80
+ replacements: SKILL_NAME_MAP,
81
+ keepMarker: 'canon-keep-retired',
82
+ wholeToken: true,
83
+ excludedPrefixes: ['scripts/eval/result-'],
84
+ excludedPaths: [
85
+ 'CHANGELOG.md',
86
+ 'src/migrate/skill-names.ts',
87
+ 'src/migrate/skill-names.test.ts',
88
+ ],
89
+ })
@@ -99,7 +99,7 @@ function owesNoBullet(path: string): boolean {
99
99
  * names.
100
100
  *
101
101
  * An unanchored claim matches on a segment-anchored suffix, which is what lets
102
- * `claude-worker/SKILL.md` credit `claude/skills/claude-worker/SKILL.md`. That
102
+ * `role-worker/SKILL.md` credit `claude/skills/role-worker/SKILL.md`. That
103
103
  * asymmetry is deliberate: a partial spelling can confirm a changed file was
104
104
  * named and never accuse one of being absent, because nothing here separates a
105
105
  * path written short from a path written wrong.
package/src/pr/paths.ts CHANGED
@@ -20,7 +20,7 @@ export interface PathClaim {
20
20
  * True when the first segment names an entry the tree actually holds.
21
21
  *
22
22
  * An unanchored claim is a path written partially, such as
23
- * `claude-worker/SKILL.md` for a file under `claude/skills/`. It can confirm
23
+ * `role-worker/SKILL.md` for a file under `claude/skills/`. It can confirm
24
24
  * that a changed file was named and can never accuse one of being absent,
25
25
  * because the comparison has no way to tell a partial spelling from a
26
26
  * genuinely wrong one.
@@ -145,7 +145,7 @@ function maskSpans(text: string): string {
145
145
  * that came out. A path past the comma was left out of the claim set entirely
146
146
  * and fell to the unnamed direction, accepted here on the ground that the
147
147
  * direction reports without grading. It graded anyway, one consumer removed:
148
- * `claude-pr-review` reads `unnamed` as a question to put to the branch author,
148
+ * `review-pr` reads `unnamed` as a question to put to the branch author,
149
149
  * and on 2026-09-01 the question went to three pull requests over bullets that
150
150
  * had named the files all along, with `#1329` gaining bullets it did not need.
151
151
  *
@@ -61,7 +61,7 @@ export const REFERENCE_MARKER = 'canon-allow-reference'
61
61
  * The leading boundary is what excludes the repair form by construction rather
62
62
  * than by exemption, and that is load bearing. A lookbehind rejecting a word
63
63
  * character before `#` never matches `erclx/canon#1299`, so a qualified
64
- * reference passes with no marker, and `claude/skills/claude-worktree/SKILL.md`
64
+ * reference passes with no marker, and `claude/skills/session-worktree/SKILL.md`
65
65
  * already ships `anthropics/claude-code#58345` in exactly that form.
66
66
  *
67
67
  * The trailing boundary is what the first shape of this pattern lacked, and it
@@ -34,7 +34,7 @@ export interface SeedsReport {
34
34
  * Classifies every seed the toolkit ships against the target's copy, and never
35
35
  * returns a change. Seeds are copy-once files a project is expected to edit, so
36
36
  * the engine's copy path would overwrite `CLAUDE.md` wholesale. Reporting alone
37
- * is what lets `claude-seed-sync` merge one section at a time instead.
37
+ * is what lets `seed-sync` merge one section at a time instead.
38
38
  *
39
39
  * Attribution reuses the history reader rather than the engine's own recovery
40
40
  * pass, which is private and takes a `SyncAdapter` seeds have no way to supply.
@@ -7,8 +7,8 @@ const GH_TIMEOUT_MS = 30_000
7
7
  /**
8
8
  * The two headings a review pass posts under.
9
9
  *
10
- * Owned by `claude-pr-review`, which states the full set once, and pinned here
11
- * the way `claude-orchestrate/scripts/poll.sh` pins them. All three surfaces
10
+ * Owned by `review-pr`, which states the full set once, and pinned here
11
+ * the way `role-orchestrator/scripts/poll.sh` pins them. All three surfaces
12
12
  * ship separately, so a heading added in that skill goes stale here with
13
13
  * nothing comparing the copies.
14
14
  */
@@ -186,7 +186,7 @@ export function resolvePlanReference(
186
186
  }
187
187
 
188
188
  // An archived plan answers every question and would report as launchable, so
189
- // the name would clear a dispatch that `claude-autoship` Step 1 then refuses
189
+ // the name would clear a dispatch that `auto-ship` Step 1 then refuses
190
190
  // as already-shipped work. Catching it here is a step earlier than the worker.
191
191
  // Both roots, for the reason `resolveLivePlan` carries: the reference is a
192
192
  // string a caller wrote, and one spelling the root this tree has since left is
@@ -269,7 +269,7 @@ function isRowFor(line: string, target: string): boolean {
269
269
 
270
270
  /**
271
271
  * Resolves the `Plan:` target against the board and against the project root
272
- * both, which is how `claude-docs` reads the same line. It accepts `../plans/x.md`
272
+ * both, which is how `docs-fold` reads the same line. It accepts `../plans/x.md`
273
273
  * and `.canon/plans/x.md` as one file, so a gate reading only the first form
274
274
  * would pass the second and strand the plan this exists to protect.
275
275
  *
@@ -323,7 +323,7 @@ function atOneRoot(path: string, plans: string[], root: string): string {
323
323
 
324
324
  /**
325
325
  * Names the other live tasks whose `Plan:` line lands on the same file. This is
326
- * the rule `claude-docs` applies before it archives a plan, held here so one
326
+ * the rule `docs-fold` applies before it archives a plan, held here so one
327
327
  * question has one implementation: a plan another live task still cites is a
328
328
  * plan the sweep is correct to leave, and a guard that read the folder instead
329
329
  * refused every task sharing one plan and deadlocked the board against the
@@ -380,7 +380,7 @@ export type CitationOutcome = PlanCitations | ArchiveRefused
380
380
 
381
381
  /**
382
382
  * Answers where one task's plan sits and who else holds it, which is the whole
383
- * of the last-live-citation rule. `claude-docs` reads this rather than scanning
383
+ * of the last-live-citation rule. `docs-fold` reads this rather than scanning
384
384
  * the board itself, so the sweep that moves a plan and the gate that refuses a
385
385
  * task archive cannot drift into disagreeing about which plan is free.
386
386
  *
@@ -564,7 +564,7 @@ async function resolveStem(
564
564
 
565
565
  /**
566
566
  * Archives one task as a single unit: the move, the ordering-row removal, and
567
- * the index regen. The hook and `claude-tasks` both call this, so every gate
567
+ * the index regen. The hook and `task-board` both call this, so every gate
568
568
  * refuses rather than reports. A caller with nobody watching cannot act on a
569
569
  * warning, and two callers gating differently is the drift this exists to stop.
570
570
  */
@@ -311,7 +311,7 @@ export async function recordPullRequest(
311
311
 
312
312
  /**
313
313
  * Records a plan's path on the task it belongs to, as the task's `Plan:` line.
314
- * `claude-feature` runs this right after the plan file lands, resolving the
314
+ * `plan-feature` runs this right after the plan file lands, resolving the
315
315
  * reference the same two ways `canon tasks plan-answers` does, through
316
316
  * `planCandidates`, so a bare slug and a board-relative path both resolve.
317
317
  *
@@ -346,7 +346,7 @@ export async function recordPlan(
346
346
  }
347
347
 
348
348
  /**
349
- * Marks the named outcomes `[x]` in place. `claude-docs` runs this against a
349
+ * Marks the named outcomes `[x]` in place. `docs-fold` runs this against a
350
350
  * board it can read and cannot edit from a linked worktree, and the positions
351
351
  * come from the read it already made.
352
352
  */
@@ -557,7 +557,7 @@ async function listTaskStems(dir: string): Promise<string[]> {
557
557
 
558
558
  /**
559
559
  * Resolves a pointer against the board and against the project root both, the
560
- * way `claude-docs` reads the same line. A fragment is dropped first, since an
560
+ * way `docs-fold` reads the same line. A fragment is dropped first, since an
561
561
  * anchor is part of the link and never part of the path.
562
562
  */
563
563
  function resolves(target: string, dir: string, root: string): boolean {