file:/be: STORE — the URI never reaches the push arm, misparsing as narrow-commit or local branch advanceThe wire layer fully supports a local store push: shared/wire.js classify() maps an authority-less file:/be: URI to a LOCAL keeper receive-pack exec, and pushRemote's GIT-019 session ("ssh/local push") rides it. But verbs/post/post.js parseSlots() (:119) routes to the Host/push slot ONLY on u.host (authority present + scheme), so an authority-less store URI CANNOT reach pushRemote. Live 2026-07-16, gritzko syncing the stale ~/src/jab.be store from ~/src/journal/jab: jab post file:/home/gritzko/src/jab.be → no host, no query → the path fell into the NARROW slot → a scoped commit over an unchanged tree → "no changes since base" (post.js:866); jab post 'file:...jab.be?/jab' → Query slot, the file: path DROPPED → local advanceBranch → "? is already at cur's tip". Neither error hints the store target was ignored. POST-027's matrix crossed ssh/https hosts + //X worktrees; a local-store target is not even a row in /wiki/POST "invocation patterns" (row 5 is ssh://host only) — spec gap AND code gap.
Every layer below post already speaks local-store push; only the arg parse (and the spec table) is blind to it.
wire.classify (shared/wire.js:35): file:/bare/be:-no-authority/keeper:-local → {bin: keeper, argv: [keeper, verb, path?/proj]} — the local exec twin of ssh host keeper <verb>.pushRemote (post.js:455) is transport-agnostic past classify: session, advert, FF gate, thin pack, saveRemoteRef.parseSlots (post.js:119): Host slot requires u.host truthy AND a scheme; file:/path?/proj has NO authority → u.query=/proj wins → Query slot (path dropped); query-less file:/path → Path/narrow slot.work/JAB-SYNC-TEST): give the URI an authority — DOG_REMOTE_PATH=/home/gritzko/bin jab post 'be://localhost/<HOME-rel store>?/proj' → ssh-to-localhost exec; moved the peer trunk b58be37a→016be1b3, fresh clone off the peer confirms. DOG_REMOTE_PATH because a non-login ssh shell lacks ~/bin. NB it rode the RETIRED native keeper binary still in ~/bin — works today, dies with the leftover.?/libX push from the mounted sub (bottom-up), or the target's later whole-tree get hits SUBFETCH on the bumped pins (one transient SUBFETCH seen; the retry ran clean — unexplained, may be its own bug).RULED by gritzko 2026-07-16: a file: target runs the SERVER-SIDE process, same as ssh — file: URIs address other projects on the same host, possibly backed by a DIFFERENT store, so they unify with the ssh transport rather than getting a direct-write special case. The server side is the jab JS daemon (native keeper is RETIRED — GIT-020 shared/serve.js, jab-to-jab wire).
jab post file:/path?branch (path = a wt or a store shard) FF-pushes cur's tip through a locally-exec'd server process — POST.mkd row 5 with a local transport; wire.classify already maps authority-less file:/be: to a local server exec, so the slot fix is ROUTING, not transport.serve.js implements only uploadPack (fetch); there is NO JS receive-pack serve yet, and wire.classify still spawns the keeper BINARY (KEEPER_BIN default). The push serve leg (jab receive-pack <sel>) + pointing classify at jab are prerequisites for this ticket's file: push (and for ssh push outliving the native keeper leftover in ~/bin).file:/path?branch, same as ssh. The ?/project selector is RETIRED — the PATH does project selection by pointing at either a WORKTREE or a STORE SHARD (e.g. file:/…/.be/jab?branch); wire.classify's servePath ?/<sel> append and the ?/proj clone idiom go with it.parseSlots: a schemed authority-less URI routes to the Host/push slot; its query is the branch want, its path resolves wt-or-shard.file:/be:) is the push-slot discriminator; a bare /abs/path or ./dir stays a Path/narrow slot.work/JAB-SYNC-TEST shape); assert trunk motion + FF refusals (at-tip, non-FF) mirror the ssh cells.Reuse the push spine; no second transport or parser.
wire.classify stays the ONE transport map; parseSlots only ROUTES — no URI hand-parsing (RULE ZERO / meta resolve law applies to refs).receivePack(selector, rfd, wfd) in shared/serve.js beside uploadPack: advert (branch codec, report-status ofs-delta caps), read <old> <new> <ref> commands, wire.drainToFile + ingest.land (objects-only), per-ref CAS + dag.isAncestor FF gate + store.set, ok/ng report. All framing/pack via pkt/wire/ingest — nothing forked; dispatch = core/loop.js pre-verb intercept mirroring GIT-020's upload-pack line.wire.classify localish arm spawns KEEPER_BIN || selfBin() (/proc/self/exe, argv[0] fallback) — jab serves jab; upload-pack moved too, fetch tests green. The ssh remote arm still says keeper (follow-up below).file: / authority-less be: → push slot; query = branch (?=trunk, none → the "no remote branch selected" refusal); bare /abs/./dir stay narrow. Server-side serveStore(): wt path (.be-is-a-FILE probe) → GET-038 redirect to backing store+project; else store root/.be/shard. Self-push refuses on shard equality (string compare — a symlinked alias slips to the at-tip refusal instead; no realpath binding).test/post/push-local/run.sh (be-js-post-push-local): pre-fix file:<peer>/.be → "no changes since base", ?main → silently CREATED a local main in the pusher's store (rc=0, peer unmoved); post-fix FF-push + at-tip/non-FF refusals, peer byte-identicaljab receive-pack server-side post (see design decisions)keeper — follow-up needed before the ~/bin leftover diesfile:...#msg refuses "commit first" (POST-027 ground)jab <verb> (keeper retirement leg 2)ng — normal clients gate client-side first; acceptable garbage or its own ticket??/x on a file: push now reads as an absolute branch address (trunk of /x) per the codec — the retired project-selector muscle memory gets a surprising arm, not an error.work/JAB-SYNC-TEST (rm -rf-able).work/POST-028 (uncommitted, nothing staged): shared/serve.js + shared/wire.js + core/loop.js + verbs/post/post.js + new test; suite 242/242 (orchestrator re-ran). Awaiting review/land order.