Repository navigation
CONTRACT: fix(ui-server): complete shares whose retained file is linked from the note's folder - #3376
Merged
Merged
Conversation
…ed from the note's folder complete_share matched the literal text `](assets/...)`, so a working document-relative or brain-root-absolute link from a nested note was refused, and every refused turn spent one of the share's three attempts until no later turn could complete it. - Resolve each Markdown link destination (inline, image, reference definition; percent-encoded or <...>) from the filed document's folder and compare it to the retained path. Code spans, fenced blocks and wiki-links do not count. The refusal names the expected target and an example link. - When several receipts cover one path, the one matching its current content proves it; otherwise the refusal names the stale receipts. - Interactive share claims spend no attempts and ignore retry backoff. A bound session can reclaim a share earlier turns left `failed`, and its exhausted-retries Action is retired. - The complete_share description, the MCP contract doc, the share skill and the async-collaboration decision state the accepted link forms. Closes #3353 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
|
Collision report at head |
This was referenced Nov 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3353
What changed
complete_sharenow accepts a retained file's Markdown link when the link resolves to the retained path from the filed note's own folder. That covers document-relative links (../../assets/documents/x.pdffroma/b/note.md), links starting with/(from the brain root) and root-relative links from a root-level note, including percent-encoded or<…>-wrapped paths with spaces. Inline links, images and reference definitions count. Links inside code spans or fenced blocks, and wiki-links, do not. A refusal now names the expected target and gives an example link that works from that note. When several receipts cover one path, the receipt that matches the current content proves it. If none matches, the refusal names the stale receipts. A filing turn that ends without completing no longer spends the share's retry attempts. A share that earlier turns leftfailedcan be reclaimed by its filing session, and that reclaim retires its "Queued work exhausted its retries" Action, which would otherwise hold back staging cleanup. The tool description, the MCP contract doc, the share skill and the async-collaboration decision now state the accepted link forms.Why this shape
createWikiLinkResolver,packages/core/src/lib/indexer/links.ts:46-103), so[[x.pdf]]is not a link the brain follows.countAttempt: falseon the store's claim transition). The attempt budget limits autonomous retries, and the autonomous runtime and triage already skip bound interactive shares (runtime.ts:76,triage.ts:206). Each bound turn is the user retrying, so counting it only turned refusals into a permanent dead end and raised a misleading dead-letter Action. Autonomous claims still count (the existingdispatcher-firstassertion still expectsattempts: 1). Shares already exhausted by older servers are recovered by their bound session rather than left stuck.Existing test fixtures linked
[Original](assets/ithaca.pdf)fromnotes/. That link is broken when rendered, so the bug was baked into the tests. They now use../assets/ithaca.pdf. Theirattemptsexpectations for interactive turns move from 1 or 2 to 0.Proof
Tested head
91bcca2d1, baseorigin/main@3984040f1.Failing first (new tests against
origin/main'ssrc/, with the tests kept):brain-root-absolute and URL-encoded links…→Retained binary must be linked by the filed documenta retained PDF linked document-relative…→LINK_RESOLVING_ELSEWHERE_IS_REFUSED_NAMING_THE_TARGET(the old message names no target)three filing turns that end without completing…→Turn refused before its backend started: Share is not ready for recoverya share that earlier turns exhausted to failed…→Inbox attempt limit reached(this fails in its own setup, because main has no uncounted claim. It is not an independent receipt.)Mutations (each restored afterwards):
d.content.includes(\](${disposition.path})`). The document-relative completion then fails withRetained binary must be linked by the filed document: expected a Markdown link that resolves to assets/documents/ithaca.pdf from voyages/ithaca/harbor.md…`. The root-absolute case and the fourth-turn case fail the same way.countAttempt: falsefromstart(). The fourth turn then fails withInbox attempt limit reached, and the legacy-failed recovery test fails the same way.Wider runs on this host.
bun run test packages/ui-server/tests packages/ui-sdk/tests: 5482 pass, 357 fail. On untouchedorigin/mainthe same 72 files give 358 failures. Every failure but one also fails on main: they are native worker-sandbox tests, and this host refuses unprivileged user namespaces (unshare: write failed /proc/self/uid_map). The one branch-only failure,http-mount-pool.test.ts("mount disposal joins real owned Unix HTTP connections…"), passed 3 of 3 on rerun. It's a flake under load and doesn't touch inbox code. Hosted CI has the full runtime.Provider history. The change touches a turn path (the
complete_sharedescription). The keyless guardspackages/ui-backend-{claude,pi}/tests/provider-history.test.tsfail on this host and identically on untouchedorigin/main, because the Claude Code subprocess exits 1 in this sandbox. Hosted CI must give the passing receipt. The description is static per build, so within a session the tool list stays byte-identical.Checklist
docs/integration-contract/mcp.md,CONTRACT:commit. The description wording changes and accepting more link forms is additive. The input schema is unchanged.)docs/reflect the change, or nothing documented moved.docs/decisions/provider-history.md): the provider-history guard passes, or nothing on a turn's path changed. (guard can't run on this host, and fails identically on main; needs the hosted receipt)Anything left open
The provider-history guard and native-runtime receipts have to come from hosted CI. Otherwise nothing.
🤖 Generated with Claude Code