//name addresses every tree under SRC_ROOT — one nav namespace, be.wtdir(uri), verbs receive URIs, hunks carry the full URI
Navigation is by URI, end to end. A scheme-less authority //<name>[/sub][?ref][#frag] addresses ANY beagle tree under a single SRC_ROOT — projects and ticket worktrees UNIFORMLY (//journal, //DIS-060, //src/dogs, //journal/be) — replacing the split TODO_ROOT/TREE_ROOT roots and the [a-zA-Z]+-[0-9]+ ticket shape test. One resolver be.wtdir(uri) → abs path maps //name/sub → $SRC_ROOT/name/sub (existence-probe .be there; a miss is a CACHED REMOTE like //github.com/… → the wire, never local). Verbs stop receiving a bag of pre-stripped paths: they receive the WHOLE URI and DERIVE wt (be.wtdir), path (u.path under it) and ref (u.query/u.fragment) from it. Every hunk carries the FULL //name/path?ref URI — banner + each click-target — so follow/click/refresh stay scoped and the pager always answers "where am I". cd is phased out (there is no io.chdir binding anyway). Origin: DIS-060 worktree nav + the :post 'msg' POSTNONE below. be/ (JS) only, method Issues.
//DIS-060 worktree nav CENTRALLY (core/loop.js::authorityRepo): it resolves be.repo from the //NAME authority and STRIPS it, so verbs get a scoped be.repo + a repo-relative arg. It works — but the authority lives ONLY in the URI and must be re-threaded on every pager path.//WT view, :post 'msg' drove the raw post 'msg' (the message-call path in views/bro/pager.js::_applySpell bypasses URI recomposition, DROPPING //WT) → the loop saw no authority → be.repo = the launch-cwd MAIN tree → post scanned a clean be/ → POSTNONE: no changes since base; the real changes in //WT were never seen.//WT emits repo-relative nav URIs (the loop already stripped //WT), so _followRow re-drives them WITHOUT the authority → resolves against cwd.$HOME/todo/<T>, projects $HOME/src/<p>, journal at $HOME/journal. The shape-test routing ([a-zA-Z]+-[0-9]+→TODO_ROOT else TREE_ROOT) collides with cached-remote //host refs (//github.com/gritzko/beagle?main).js/uri.cpp exposes io.cwd() but NO io.chdir; core/discover.js::find has no cache (walks up from cwd each call) — so be.repo naturally follows whatever path is resolved, no invalidation needed.//<name>[/sub][?ref][#frag] addresses every tree under SRC_ROOT, projects and tickets alike; //journal/be reaches a project's subdir. No shape test.be.wtdir(uri) → abs path is the single resolver: //name/sub → $SRC_ROOT/name/sub when .be exists there; a miss → null (a //host cached remote → the wire). SRC_ROOT = $HOME (gritzko). A bare /////. → the tree where jab was STARTED (be.find(cwd), the launch tree) — a "back to where I started" shortcut, NOT a privileged main tree.be.wtdir — never a pre-stripped path, never cwd/cd. authorityRepo resolves be.repo and PASSES the URI through (stops stripping).//name/path?ref URI (banner + every U click-target); the pager tracks it as view state — follow/click/refresh/message all inherit it from ONE place.get (bare, or get //ABC-123 FROM a peer worktree) or patch on diverge — worktrees are usually SHALLOW clones sharing the store, so //name doubles as a get/patch SOURCE; a full-clone post //name/sub is the RARE exception, deferred.$HOME/src are addressed nested — //src/dogs → $HOME/src/dogs (intended); the path component carries the nesting, no per-project root.io.chdir, no beagle/ change — the JS side owns cwd and there is no chdir binding to add from here.be.wtdir lives at/after core/discover.js::find.uri._parse to split, uri._make to compose (CLAUDE); reuse the URI-010 relative-path resolver for the path axis.//name/path hunk URIs changes bytes — goldens + any native parity oracle MOVE with the rule (or a native ticket splits).:post 'msg'-in-//WT commit, and post //journal/be cross-tree, asserted before the fix.SRC_ROOT = $HOME (gritzko), ONE root, no search-list: //<name>[/sub] → $HOME/<name>[/sub], be.wtdir probes .be there. Ticket worktrees move $HOME/todo/<T> → $HOME/<T> so //<T> resolves; projects already resolve as //journal, //src/<p>. Retires the shape test AND the TODO_ROOT/TREE_ROOT split..be PROBE, not a name shape — a project literally named like a host is resolved by whether it exists under SRC_ROOT (a real host never does).core/recurse.js::joinPrefix already prefixes a descended sub's nav URIs with its subpath — extend the same prefix to carry //name (so //journal/be/test composes authority + project subpath + submodule).driveSpell chokepoint — retiring the per-path re-threading that leaked in DIS-060.verb(context_uri, …rest) (gritzko 2026-07-04): arg 0 is ALWAYS the context URI, PRESERVED across spells to save typing; the pager _applySpell + the CLI arg-normalizer are the ONE composition chokepoint (what "inject at driveSpell" concretely becomes). arg 0 is optional — bare → the verb's default over the context dir../x/../x→PATH, //WT→AUTHORITY, ?x→REF, #x→FRAGMENT; a supplied URI with ≥2 of {scheme,authority,path} RESETS arg 0 (drops the inherited context — ssh://blah, //OTHER/), one component MERGES that slot; ?ref/#frag NEVER reset — they always merge (so ?feat#L20 keeps the current file).:post ssh://blah resets, :put ./file→put(ctx/file), :put a.txt b/c.txt→put(ctx,a.txt,b/c.txt).put("//T/dir/a.txt","b/c.txt") ≡ put("//T/dir","a.txt","b/c.txt") (both stage //T/dir/{a.txt,b/c.txt}). post: narrow = arg 0's path (commit-all UNDER it) UNLESS a selection is staged (selective → path ignored).//WORKTREE/path (gritzko 2026-07-04): core/discover.js::navCwd(dir?) is the INVERSE of wtdir (name = wt under SRC_ROOT, path = dir under wt; round-trips wtdir(navCwd())==cwd). So the context is ALWAYS present — cwd on the CLI, nav in the pager — and a verb's typed paths are ALWAYS rest under it (no "is arg 0 a target or the base" ambiguity).be.wtdir(uri) in core/discover.js (exported → folded onto be via mintBe); SRC_ROOT env, default $HOME; /////. → be.find(cwd).wt; confinement guard vs the $HOME/.be walk-up. PILOT (wt $HOME/URI-011).authorityRepo → resolves be.repo via be.wtdir (SRC_ROOT, dropped TODO_ROOT/shape-test) + exposes be.authority. STILL STRIPS to a repo-relative arg (so UNCONVERTED verbs keep working) — "stop stripping" waits until every verb is converted.status PILOT: emits full status://name/... banners + diff://name/.../cat://name/... click-targets via be.authority; scoped; bare status byte-identical. Green: 124/124 be-js incl. get/cached.get: accept //ABC-123 as a peer SOURCE (bare get + get //peer) via be.wtdir; patch //peer on diverge — the inter-tree movement path (shallow clones share the store).shared/nav.js navUri(scheme,path) (off be.authority); rolled across status/ls/tree/commit/log/diff/cat/refs/spot(grep/regex) — 6 in PARALLEL (one worker per disjoint verb file) + refs/spot by hand. Each emits <scheme>://name/path hunks; 124/124 be-js green. Notes: diff/cat URIs are C-weave-baked (a navAuthorize/verb-slot shift, not a scheme arg); cat/spot banners now carry the scheme (were scheme-less) — test-clean + more uniform.io.cwd() verbs: verbs/get/get.js:319 (handleSeed clones into cwd) and :418 must honor be.wtdir; status.js:440 cwdRel is cosmetic.//name/path hunk bytes.views/bro/pager.js::_composeCall emits verb(context_uri,…rest) per the classifier; _applySpell routes through it (retiring the message-shortcut authority leak). core/discover.js::navCwd seeds the cwd context. TABLE test test/bro/pager/driver.js (a–g + reset + ref/frag merge, 11 cases) green; errors on unpatched HEAD (repro-first). post::parseMessage ignores the emptied authority slot (clean message). 128-suite: no new failures.shared/spell.js (compose/composeArgv/mergeUri/bindRest/buildSpell) — ONE implementation for the pager (_composeCall delegates) AND the CLI (composeArgv, verb pre-split). 15 unit cases in the pager driver (a–g + CLI + bindRest) green.bindRest binds rest paths UNDER arg 0's context dir when be.authority is set (a file arg 0 is a target, its parent the base — statted); bare CLI puts untouched. Integration test/uri011/put-nav-bind: put //THERE/sub a.txt from HERE stages THERE/sub/a.txt, no root leak.composeArgv into loop.cli is UNNEEDED and HARMFUL: the CLI is already conventional (context = cwd implicit / //name explicit via authorityRepo, rest → verb), AND the address-bar classifier COLLIDES with per-verb CLI slots — #msg/?br are post's message/branch (not a frag/ref edit), size:#<sha> is a complete view URI (not a scheme merge). The "incremental context update" is a PAGER concept (edit one URI as you navigate); a CLI call is stateless. Verified: bare///WT outputs are byte-identical WITHOUT the transform. Finding: mergeUri now resets on ANY scheme (a scheme = a complete address) — kept, benefits the pager.//name/path hunk URIs (rollout emits schemed status://…). This is an OUTPUT-format change (byte-parity churn) independent of word(context_uri,…rest); land per-verb behind the pilot, MOVE goldens. follow/click (_resolveSpell) already navigates to a URI (arg 0).:post 'msg' in a //WT view commits WT (not cwd); post //journal/be cross-tree.shared/spell.js::compose reports verbExplicit; pager.js::_applySpell refuses WRITE_VERBS={post,put,delete} when not explicit ("won't re-run from a nav tweak — type the verb to confirm"). An explicitly re-typed post …, or a non-write verb, drives normally. 4 driver checks.commit://ULOG view drove diff:file?a..b with NO authority → resolved in the launch tree → EMPTY. The C-weave bakes diff:<path>?<navver> U-targets with no authority (the rollout's deferred "navAuthorize shift"). FIX: shared/nav.js::navAuthorize(bakedUri) injects be.authority into a baked scheme:path?… (diff:f?a..b → diff://ULOG/f?a..b; no authority → unchanged, byte-parity), applied in views/diff/diff.js::diffOut.feed — the INNERMOST sink, so authority lands OUTERMOST AFTER any submodule prefixingSink (else diff:sub///ULOG/… mangling, which I hit + fixed by moving it from emitHunks to diffOut). Verified cross-tree: the hunk targets carry //ULOG (incl. nested paths) and diff://ULOG/f?parent..sha renders the real diff (was empty). Chains with the schemed-scope fix below.commit://ULOG?sha from another wt threw COMMITNONE — authorityRepo's if (u.scheme) continue SKIPPED every schemed URI, so //ULOG never scoped (be.repo = launch tree). FIX (core/loop.js): skip only TRANSPORT schemes (ssh:/be:/…); a PROJECTOR scheme (commit:/status:/diff:/log:) with a //authority scopes too — resolve via the scheme-LESS form (be.wtdir rejects a scheme), and the strip KEEPS the scheme (commit://ULOG?sha → commit:?sha, resolved against scoped be.repo). Verified cross-tree: commit://ULOG?<sha> from HERE resolves ULOG's commit. Same fix makes nav-schemed-scope (b)/(c) SCOPE correctly (was "did not show THERE's file"). BANNER: views/status/status.js built the columnar banner as a hardcoded "status:" (no navlib) — routed through navlib.navUri("status", prefix) so it carries //name (byte-identical when no authority: navUri("status","")=="status:"). nav-schemed-scope NOW FULLY GREEN; whole be-js suite 129/129 (big/wire-network excluded).get/patch, NOT cross-tree post — worktrees are usually shallow clones sharing the store, so there is no repo to dup; get //peer pulls, patch //peer reconciles a diverge. A full-clone post //name/sub (copy working changes into a foreign tree + commit) is rare and DEFERRED — no mutation verb honors a foreign authority in this ticket.// / //. → the LAUNCH tree (where jab started, be.find(cwd)). The launch tree is TECHNICALLY just a shallow-clone peer like any //name — nothing structurally special; // is purely an ergonomic ALIAS for "the one I started in", handy but not a privileged "main tree". Canonical history = the shared store's refs. Return via // or Back.$HOME/todo/<T> → $HOME/<T> so SRC_ROOT=$HOME addresses them as //<T>; projects (journal, src/*) already resolve. Until a worktree is moved, //<T> won't resolve — test with a moved/symlinked one.//DIS-060/.be is a redirect FILE (shard lives in the source store); be.wtdir returns the wt dir and be.find resolves the shard — confirm the probe/be.find handles both file- and dir-.be.status pilot.core/discover.js::cwd() (be.cwd()) = be.repo.wt (which authorityRepo anchors on a SUBMODULE wt for a //name/sub/… path — verbs don't cross wts, so that scope is correct), else the launch wt, else io.cwd(). Verbs use be.cwd(), NEVER SRC_ROOT. srcRoot() is IMPLIED as one level up from the top wt (topWt climbs past submodules, STOPS at the $HOME/.be store — a real over-climb bug found+fixed), when SRC_ROOT env is unset. ROUTED off raw io.cwd(): head.js, get.js::inRepoSeed → be.cwd(). KEPT raw io.cwd() (correct): get's CLONE destination (//peer-as-source is a separate ticket), status's cwdRel cosmetic prefix, the pager launch sessionBe, and the discover primitives. Full io.cwd()/be.find() audit: every operational path uses ambient be.repo/be.cwd(); cwd only as a repo-less fallback or an explicit submodule find(join(repo.wt,sub)). 128/131 (3 pre-existing), wire green. post/put need NO cd (already use be.repo.wt).word(context_uri,…rest) is the tree's shape. REWORKED (needed context-binding): pager (composer), post (message-slot), put/delete (bindRest). ALREADY CONFORMANT (arg 0 = URI, parse-on-entry): get/patch/head; ls/cat/diff/status/log/tree/sha1/size/type/refs/commit/blob/lsr/help; grep/regex/spot (arg 0 = URI, pattern = rest). CLI: conformant by design — cwd is the implicit context (gritzko: "first is optional too… default in the current dir… not necessary"), //name explicit via authorityRepo; injecting the pager classifier was tried 3× and REVERTED (breaks verb #msg/?br slots, //host remotes, and imposes pager-navigation subpath semantics on stateless CLI calls).:post 'msg'-in-//WT → POSTNONE failure. Supersedes the shape-test / TODO_ROOT / TREE_ROOT routing with ONE SRC_ROOT namespace addressed by URI end-to-end (be.wtdir + full-URI hunks + verbs-take-URIs, no cd).$HOME/URI-011) 2026-07-03: be.wtdir (core/discover.js) + authorityRepo→be.wtdir/be.authority (core/loop.js) + shared shared/nav.js navUri, then ALL 9 projectors (status/ls/tree/commit/log/diff/cat/refs/spot) emit full <scheme>://name/path hunks — 6 converted in PARALLEL (worker per disjoint file). status //URI-011 → status://URI-011 + diff://URI-011/<path> click-targets, scoped; bare output byte-identical; 124/124 be-js green. NEXT: pager tracks the full URI (retire per-path re-threading); get/patch accept //peer; audit get's io.cwd(); then land.