Skip to content

#1091 Anchor a project-deployed link where its target deploys - #1159

Merged
williamthorsen merged 5 commits into
mainfrom
1091
Aug 3, 2026
Merged

#1091 Anchor a project-deployed link where its target deploys#1159
williamthorsen merged 5 commits into
mainfrom
1091

Conversation

@williamthorsen

@williamthorsen williamthorsen commented Aug 3, 2026

Copy link
Copy Markdown
Owner

What

Fixes an issue where cross-references between guidance files that sync deploys into a project worked only if the linked guidance was also installed in the user's home directory, silently degrading agent output for anyone else.

Why

Guidance deployed into a project pointed at files that exist only in the user's home harness directory. A skill's cross-reference to a sibling resolved only when that sibling happened to also be installed globally; otherwise it named a path nothing creates. A dead reference of this kind never surfaces as an error, because the agent reading it improvises past the missing target.

Details

🐛 Bug fixes

  • The constant path prefix threaded through every Markdown link rewrite is replaced by an injected anchor, so a target's emitted absolute path is decided by the tree that actually deploys it. createSkillLinkAnchor places a skill directory the run writes under the deploying domain's base and everything else under the harness home; createContentRootLinkAnchor delegates to it for targets under skills/ and keeps the harness home otherwise.
  • The set of deployed skill directories is derived from the run's own output — declared skill slugs filtered per harness, unioned with the rulebook-delivered skill names — rather than from source layout, so the anchor names precisely the directories the run creates.
  • A link to a skill the run does not deploy stays anchored at the harness home, its only addressable location; re-pointing it into the project would make it unresolvable outright.
  • Home-domain output is unchanged by construction: sync --global sets the domain base to ~, which collapses the anchor's two branches to the same string.
  • install and content validation keep their existing behavior through a homeAnchor constructor that reproduces the single anchor every caller had before.
  • The rulebook render context is threaded as a closure rather than rebuilt per call site, so the pre-write gate and the delivery passes it guards cannot disagree about where a link target lands.

🧪 Tests

  • Unit coverage for both anchor constructors across all four target shapes in both domains, including a table-driven case pinning the home-domain identity property directly.
  • End-to-end sync coverage for each artifact type — declared skill, rulebook body, and subagent — in the project domain, plus the negative case for a target the run does not deploy and a sync --global case pinning the home-domain anchor.

📚 Documentation

  • The rulebook authoring guidance states which root a link target carries in each domain, with a delivered-skill example beside the existing support-entry one.
  • The partials reference scopes the project-root exception to bare sync.

Closes #1091

A relative Markdown link in guidance that `sync` deploys into a project now resolves against the tree that holds its target: the project's harness directory when the same run writes that target there, and the user's harness home otherwise, which is where `install` puts support content. Such a link previously addressed the harness home unconditionally, so it resolved only when its target happened also to be deployed globally, and otherwise named a path nothing creates.

A link to a skill this run does not deploy stays in the harness home, where an installed copy can answer it. Skills, subagents, and rulebook bodies alike resolve their links on these terms.
A link naming a skill the run delivers is the only target whose anchor reads the domain base, so it is the one shape that distinguishes a home domain anchored at `~` from one anchored anywhere else. Every other target is tilde-anchored outright and renders identically either way, which left the home domain's anchor base unasserted by any sync-level test.
The rulebook-link specification described a single rendered form, illustrated with a support-entry target that resolves to the harness home in either domain. It now names both roots: a target naming a skill the same run delivers anchors where that run wrote it, under the project root from bare `sync` and under the harness home from `sync --global`, while every other target keeps the harness home in both. The partials README carries the same distinction against its install-pipeline description.
The three modules that gained a `ResolveLinkAnchor` type import placed it in case-insensitive position among the value imports, which the configured sort order rejects.
Documentation of `sync`'s link rewriting now distinguishes the two deployment domains. A rewritten Markdown link carries the project root only under bare `sync`, and only when it names a skill the same run deploys; `sync --global` roots every rewritten link under the harness home, just as `install` does.
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Dependency audit

Production dependency audit passed.

@williamthorsen williamthorsen self-assigned this Aug 3, 2026
@williamthorsen
williamthorsen marked this pull request as ready for review August 3, 2026 12:51
@williamthorsen
williamthorsen merged commit 2f0ddaa into main Aug 3, 2026
3 checks passed
@williamthorsen
williamthorsen deleted the 1091 branch August 3, 2026 12:58
williamthorsen added a commit that referenced this pull request Aug 4, 2026
…e-v0.2.2 codeassembly-v0.4.0 factory-v0.2.2 codeassembly-mcp-v0.2.2

codeassembly-v0.4.0
- #1153 feat: Make codeassembly and kb CLI tools publishable (#1164)
- #1091 fix: Anchor a project-deployed link where its target deploys (#1159)
- #1156 refactor: Rename packages to publishable names (#1157)
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- refactor: Refine typescript-preferences
- #1150 fix: State doc-description form and make comment mood opt-in (#1151)
- deps: Upgrade first-party linting deps to latest version
- #1137 feat: Establish personal rulebooks for code layout and TypeScript preferences (#1144)
- #1126 feat: Check a package's own guidance content before it ships (#1138)
- refactor: Fix lint
- tooling: Remove redundant lint rules
- #1133 fix: Reject an anchor link that names no heading (#1135)
- #1122 feat: Capture lede decisions as an accumulating corpus (#1132)
- #1125 feat: Honor invocation tokens in rulebook bodies (#1129)
- #1107 feat: Render rulebook links and path tokens per harness (#1124)
- #1115 feat: Sync guidance at build and install so an upgrade cannot leave it stale (#1123)
- #1114 feat: Adopt a dependency's guidance by naming the package (#1121)
- #1088 feat: Deliver project ambient rulebooks per harness (#1113)
- #1087 refactor: Rename the authoring rulebook and mark its enforced rules (#1092)
- #1095 tooling: Move compilation out of the install lifecycle into a bootstrap step (#1102)
- #1094 refactor: Remove the ambient ripgrep dependency from the test suite (#1097)
- deps: Upgrade all deps to latest version
- tooling: Upgrade all deps to latest version & modernize configs
- refactor: Fix lint
- #1077 feat: Show the proposed edit above post-review menu options (#1078)
- #1068 feat: Deliver ambient rulebooks mechanically, retiring GLOBAL.md (#1075)
- #1069 feat: Add a no-second-person rule to the lede-voice doctrine (#1070)
- #1050 feat: Add a redundancy rule to the lede-voice doctrine (#1058)
- #1035 internal: Add lifecycle workspace with the canonical envelope, vocabulary & lane fold (#1049)
- #1006 internal: Extend lifecycle-event instrumentation to five high-traffic skills (#1034)
- #1028 feat: Rule out absence-of-removed-code tests (#1032)
- #114 feat: Let reviewers emit gated insights into review artifacts (#1031)
- #1027 internal: Retire input.received and redundant skill.progress emits (#1030)
- #1026 fix: Name the side effects an approval ask authorizes (#1029)
- #1022 fix: Normalize action and question label identifiers across asks blocks (#1025)

factory-v0.2.2
- #1156 refactor: Rename packages to publishable names (#1157)
- deps: Upgrade all deps to latest version
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- tooling: Remove redundant lint rules
- deps: Upgrade deps to latest version
- deps: Upgrade all deps to latest version
- deps: Upgrade all deps to latest version
- tooling: Upgrade all deps to latest version & modernize configs
- refactor: Fix lint
- deps: Upgrade all deps to latest version

kb-v0.3.0
- #1153 feat: Make codeassembly and kb CLI tools publishable (#1164)
- #1156 refactor: Rename packages to publishable names (#1157)
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- tooling: Remove redundant lint rules
- #1095 tooling: Move compilation out of the install lifecycle into a bootstrap step (#1102)
- tooling: Upgrade all deps to latest version & modernize configs
- refactor: Fix lint

codeassembly-lifecycle-v0.2.0
- #1153 feat: Make codeassembly and kb CLI tools publishable (#1164)
- #1156 refactor: Rename packages to publishable names (#1157)
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- tooling: Remove redundant lint rules
- #1095 tooling: Move compilation out of the install lifecycle into a bootstrap step (#1102)
- tooling: Upgrade all deps to latest version & modernize configs
- #1038 feat: Add the read-only git adapter for worktree and base-branch ground truth (#1059)
- #1051 feat: Bound fold memory and rescan cost with a retention window (#1057)
- #1035 internal: Add lifecycle workspace with the canonical envelope, vocabulary & lane fold (#1049)

codeassembly-mcp-v0.2.2
- deps: Upgrade all deps to latest version
- #1156 refactor: Rename packages to publishable names (#1157)
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- #1095 tooling: Move compilation out of the install lifecycle into a bootstrap step (#1102)
- tooling: Upgrade all deps to latest version & modernize configs
- refactor: Fix lint

codeassembly-run-core-v0.2.2
- #1156 refactor: Rename packages to publishable names (#1157)
- #1152 tooling: Run every test in the default gate, classified by what it reaches (#1155)
- #1110 tooling: Migrate Vitest to nmr's centralized model (#1154)
- #1095 tooling: Move compilation out of the install lifecycle into a bootstrap step (#1102)
- tooling: Upgrade all deps to latest version & modernize configs
- refactor: Fix lint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Project-deployed guidance emits home-absolute links pointing outside the repository

1 participant