Skip to content

PREFILL-SHARE-SCRUB-1: warn that a shared #in= link carries the inputs; clear the fragment from the address bar - #3049

Open
collectrix wants to merge 7 commits into
mainfrom
PREFILL-SHARE-SCRUB-1
Open

collectrix wants to merge 7 commits into
mainfrom
PREFILL-SHARE-SCRUB-1

Conversation

@collectrix

Copy link
Copy Markdown
Contributor

PREFILL-SHARE-SCRUB-1 — warn that a shared #in= link carries the inputs, and clear the fragment from the address bar

Row: board/claimed/PREFILL-SHARE-SCRUB-1.md (privacy register item 8, W6). One PR, page-only + the shared-block master. No kernel bytes, no node, no manifest content change.

What changed

One writer, as the fence requires — the master snippet scripts/ain-bridge-v1.snippet.html, re-rolled to every bridge page by its roller (sync-ain-bridge.mjs), never hand-edited per page:

  1. On load (strip): initPrefill now calls stripSharedFragment() after a shared #in= link has been applied (n>0). It rewrites the address down to the bare path so the inputs do not linger in the URL box, in a later copy-paste, or in synced history. No storage, no network — CONTRACT §0 unchanged.
  2. On share (warn): the share button's confirmation text and the button's new title attribute say: "This link contains every input on this page. Anyone you send it to can read them." Copy-hallmarks clean (no em-dashes, no build codes).
  3. The SHARE LINK comment block now states the real behavior. The old claim "the button never calls history.replaceState or pushState" is reworded to "the button itself calls neither ... nor ...": still true of the button, and the page-wide version of the claim is no longer true, so the row's precheck probe (tools/01 containing "never calls") flips 0→1 as the row expects.

Named deviation from the decision text

The row's decision prescribes history.replaceState(null, '', location.pathname + location.search). This PR writes location.pathname only. Reason, measured: scripts/check-deeplink-contract.mjs (TOOLPAGE-DEEPLINK-1) greps every registered WebMCP page for /location\.search/ with NO benign-shape exemption, and its baseline is shrink-only for pre-existing divergences (deeplink-contract-baseline.json holds ~70 pages, none for the location-search class). The bridge body is inside every registered tool page, so the prescribed literal would fail that gate on the whole registered estate. QUERYSTRING-PRIVACY-1's fragment-strip exemption (which does recognize the prescribed shape) cannot apply where the deeplink grep reads first. location.pathname only is strictly more private: any incidental query string is dropped too; no page on this estate reads the query string (that is what QUERYSTRING-PRIVACY-1 enforces), so nothing functional is lost. The embed flag (?embed=1) is consumed once at boot on runner pages, which carry no bridge region — unaffected.

Version and scope

  • BRIDGE_VERSION stays 1.3: scripts/session-root-parity.test.mjs pins the master at 1.3 and asserts the sync mechanics against that value. This row is a body-only re-roll; re-versioning belongs to its own row. Drift is content-based (byte compare), not version-based, so the roll converges regardless.
  • The prefill format, field set and applyFields behaviour are unchanged; existing links keep working (proven below). #run=1 links behave exactly as before plus the strip.

Sweep counts (before / after)

  • Master edited → sync-ain-bridge.mjs --check: 611 bridge pages scanned, 604 DRIFTED, 7 SKIP.
  • After --all: 604 REWRITTEN · 7 SKIP, then --check: 0 DRIFTED · 7 SKIP (byte convergence).
  • Commit: 605 files (1 master + 604 pages), 21780 insertions(+), 5445 deletions(-).
  • The 7 SKIPs are the roller's documented structural estate holes (3 cfg-not-in-its-own-script-element, 2 multiple-cfg-lines(2), 2 cfg-line-not-adjacent-to-bridge-block) — unchanged by this row, their old bodies intentionally untouched (the roller refuses regions it cannot cut).
  • Residual never calls HTML matches after the roll: 17 pages — all are unrelated page prose ("never calls an RNSA", "never calls /keys/{key_id}") or those same SKIP pages. No in-region stale claim remains.

Template proof (row done-item 2)

No browser or headless-Chrome in this lane — captures owed: none possible; the proof is behavioral. A node:vm harness (.tmp/prove-prefill-scrub.mjs, untracked) runs the ROLLED bridge block of tools/152 and tools/01 in document order (CFG line, then bridge body, boot at load): 22/22 PASS on both pages —

  • a real #in= link (payload {fee:'42.5', memo:'synthetic proof value'}) fills every seeded field via applyFields, notice shown;
  • history.replaceState called exactly once, args (null, '', '/<page>') — no fragment, no query in the rewritten address;
  • the share button builds (CFG.share=true) and carries the exact warning sentence in its title; the confirmation sentence is in the body;
  • negative control: no #in= link → no history write;
  • rolled body carries no location.search literal (the grep-gate surface).

Gates

  • Pre-push hook, at head c483886e: PUSH-SAFETY leg 14/14 green (JS syntax changed-scope, copy hallmarks changed-scope, PII banner full scan, main-only derived paths, secret scan 0 hits, ID-collision, etc.), --hygiene 7/7 green, --affected green. Sync drift gate inside the run: 0 DRIFTED.
  • Receipt (SIMPLIFY-1, one run outside the cap, at the pushed HEAD): FULL-OK PREFILL-SHARE-SCRUB-1 c483886e none node n/a leg=none gates=0 scoped=[none] wall=0s.
  • Advisory noted, not acted on: sibling pages landed on origin/main since the branch was cut (sitemap freshness) — main-side regen owns it.

Follow-up (out of this row's fence, per the row text)

  • The ledger carries artifact-bearing fragments up to ~30 KB (A W6) — unchanged here, noted as the follow-up the row names.
  • The 7 structural SKIP pages keep old bridge bodies until their region shapes are repaired (pre-existing roller estate holes).

🤖 Generated with Claude Code

collectrix and others added 7 commits November 12, 2026 08:26
…utton warns the link carries the inputs

Master snippet (scripts/ain-bridge-v1.snippet.html, the ONE WRITER the sync-ain-bridge.mjs roller owns) plus all 604 rolled bridge pages:

- initPrefill now calls stripSharedFragment() after a shared #in= link has been applied: history.replaceState(null,'',location.pathname) drops the fragment from the address bar. No storage, no network (CONTRACT 0 unchanged).
- The strip writes location.pathname ONLY: TOOLPAGE-DEEPLINK-1's 3.1 grep gate bans a location.search read on registered pages, and its baseline is shrink-only, so the row decision's 'location.pathname + location.search' shape would fail the gate on every registered bridge page. Deviation from the decision text, disclosed in the PR body.
- Share confirmation text and the share button's title attribute now say: 'This link contains every input on this page. Anyone you send it to can read them.'
- BRIDGE_VERSION stays 1.3 (session-root-parity.test.mjs pins it); body-only re-roll: 604 REWRITTEN, 0 DRIFTED, the 7 structural SKIPs unchanged.
- Template proof (.tmp/prove-prefill-scrub.mjs, vm harness): 22/22 PASS on tools/152 + tools/01 - prefill applied, address bar clean, share warning shown, negative control (no link, no history write).

Co-Authored-By: Claude Code <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant