docs(sharing): say why getPolicy's disabled-branch redactFields read is kept - #14761
Conversation
…is kept `getPolicy()`'s `enabled !== true` branch reads `raw.redactFields`. Its comment justified that read by a case #14033 removed: it spoke of "tokens that still serve" on a switched-off block, and after #14033's redemption gate no such token exists. The comment was describing an impossible case, which is how a read outlives the reason anyone can still read for it. Measured before rewriting it, on `origin/main` 7a17f3b: - `policy.redactFields` has exactly one reader in the file — the union in `resolveToken` — and the `[#14033]` gate returns `null` 75 lines above it. - `createLink` never reads it on any branch; the row it writes carries the caller's `redactFields`. - `getPolicy` is module-private with exactly two callers, both in-file. So the read is unreachable, as the card says. Collapsing the branch back to `redactFields: []` was also measured: the whole `@objectstack/plugin-sharing` suite stays green, 726/726, unchanged. That is the reason the read is KEPT rather than removed — no pin distinguishes the two shapes, so a future regression of the gate would restore #13856's fail-open widening uncaught, and the read is the only thing that fails closed behind it. Comment only. Non-comment content of the file is byte-identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
…he base The first pass wrote the same explanation over 32 lines where the comment it replaced used 11. That is not free: `check-system-context-census` anchors the `context.isSystem` read sites in this file by ABSOLUTE LINE NUMBER, so +21 lines rotted 10 of them and turned the gate red. Measured both ways on `origin/main` 7a17f3b: - base file, unmodified -> `check-system-context-census: OK ... 145 anchors resolve` (exit 0) - base + the 32-line comment -> `10 problem(s) over 145 anchors`, five `[site-without-a-row]` and five `[anchor-is-not-a-read-site]` (exit 1) - `--fix` repairs it by rewriting 5 anchors, all on ONE line (`content/docs/permissions/system-context.mdx:138`) One line of churn is small, but that page is one of the two hottest generated files in the repo and is contended by two other open PRs on this branchline. A comment-only change that carries no behaviour should not need a census regenerated to land, so the comment now says the same five things in exactly the 11 lines it replaced. The file is 1006 lines before and after, no anchor moves, and the census stays green without being touched. Non-comment content remains byte-identical to the base. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 8 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin a1d6677a8e466a8b54b16313f99bc19207db731a && git checkout a1d6677a8e466a8b54b16313f99bc19207db731a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 7a17f3bf1e48e1e88f8e833a794e37bd40230f17 d2b749a92d72ec0e0ff72b33edab7feb60166a03 && git checkout -B drift-repro 7a17f3bf1e48e1e88f8e833a794e37bd40230f17 && git merge --no-ff d2b749a92d72ec0e0ff72b33edab7feb60166a03
node scripts/docs-audit/affected-docs.mjs --json 7a17f3bf1e48e1e88f8e833a794e37bd40230f17 |
Fixes #14581
Comment-only.
getPolicy()'senabled !== truebranch keeps itsraw.redactFieldsread; the comment beside it now describes why, instead of describing a case that can no longer arise.Flagging it up front, because it is a disposition disagreement and not a judgement call I should bury. The dispatch that sent me here selected route 1 (collapse the branch to
redactFields: []). The triage comment on the card, posted five hours earlier by a different seat, had already ruled route 2 explicitly — "leave the read, rewrite the comment", "⛔ Comment only. No behaviour change, no test change." The dispatch does not mention that ruling.I took route 2. It is what the prior ruling of record says, and my dispatch left the door open for it ("you may better it, with reasons"; "if you conclude route 2 is right anyway, argue it from what you measured"). The measurement below is the argument, and it points the same way. A maintainer who wants route 1 after all should say so — nothing here is hard to revisit.
Premise checks, run before editing, at
origin/main7a17f3bf11 · Is the read actually dead? Yes, exactly as the card claims.
policy.redactFieldshas one reader in the file — the union at:772inresolveToken.[#14033]gateif (!policy.enabled)sits at:697andreturn nulls, 75 lines above that reader.createLink(:442) readsenabled(:449) and at:545writesinput.redactFields— the caller's list, neverpolicy.redactFields.getPolicyhas exactly two callers repo-wide, both in this file (:442,:650). Every other hit of the name is a comment, changelog, or docs reference.share-link-routes.tsreadsresolved.redactFields, which isresolveToken's return — only ever non-null on the enabled path.2 · Does removing it red anything? No — and that is the finding. See below.
3 · Is
getPolicyexported? No. It is a module-privatefunction getPolicy(schema: any)at:89. The package entry exportsShareLinkServiceand its options type, not this helper. Clause ② staysno.The measurement that decided the route
I applied route 1's exact collapse as an ablation, proved it reached disk (blob
726dace8tob017e772; injected marker count 1, removed-text count 0, via aglobalThiswrite rather than a comment), and ran the suite:Byte-identical to baseline. Nothing reds. Restore proven afterwards by blob equality against
HEADplus an emptygit diff HEAD.A green ablation is ambiguous on its own — it can also mean the mutated line never executed. So the branch was proven live with a second, deliberately-fatal mutation in the same spot (
throw new Error('ABLATION_PROBE_14581'), also proven on disk):The branch runs 18 times across the suite, and not one of those 18 assertions cares which value it returns. That is what makes the green above a measurement rather than an artefact. Restored again, blob equality plus empty
git diff HEAD.That result cuts both ways, and the second way is the load-bearing one:
#14171pins that used to read the set with the switch off are all reversed totoBeNull(), so they pass under either version. If the:697gate ever regresses, route 1 restores#13856's fail-open widening with zero test coverage to catch it; route 2 still fails closed.Deleting a free, fail-closed read whose absence no test would notice, in a file where
#14637currently measures a stated security property being defeated one layer up, is the trade that looks clean in the diff and bad in the incident. The read stays; the comment now says exactly that, so it is documented defence in depth rather than dead code wearing a confident wrong comment.Why the comment is dense: line-count parity is deliberate
First pass wrote this over 32 lines. That rotted
check-system-context-census, which anchors this file'scontext.isSystemsites by absolute line number — measured, all three legs:OK — ... 145 anchors resolve(exit 0)10 problem(s) over 145 anchors(exit 1)--fixcontent/docs/permissions/system-context.mdx:138One line of churn — but that page is one of the two hottest generated files in the repo and is contended by two other open PRs on this branchline (#14528, #14726). A comment-only change carrying no behaviour should not need a census regenerated to land, so the replacement says the same five things in exactly the 11 lines it replaced. File is 1006 lines before and after; no anchor moves; the census stays green without being touched.
Clause ② —
no, derived from the diffThat grep cannot see a changed signature on an already-exported symbol, so that half is checked separately and mechanically: stripping
//comment lines from the base file and from this one yields byte-identical content (sha12e7b2f64695a3a3both sides). No declaration, signature, or statement moved — a comment-only diff cannot alter an export surface.Tests
pnpm --filter @objectstack/plugin-sharing testplustypecheck(check:test-typecheck: OK), both ond2b749a92, tree clean. The named control pin —control — the enabled:true path serves exactly declared ∪ per-link, as before— passes untouched; it reads the enabled branch at:122, which this diff does not go near.Gates
24 commands (23 derived by
dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, plus the always-owedcheck:nul-bytes), every exit code captured before any pipe. 21 exit 0. Zero real findings. Three non-zero, all NOT MEASURED on their own words:check-test-completeness(exit 3) — "PREREQUISITE NOT MET — this gate grades a savedturbo run testlog, and no log was named ... ⛔ It is not a red"check:dual-build-cjs-loads(exit 3) — "PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/ ... ⛔ This is NOT a pass: nothing was measured"check:i18n(exit 1, and its verdict is why the code is not the classifier) — "Nothing was checked: no bundle was compared and no config was parsed"All three want a full workspace build that a comment-only diff cannot influence; CI builds and runs them properly.
Changeset
None, deliberately. Comment-only, no behaviour change, and the compiled output is unchanged — this releases nothing from any package, so
skip-changesetis applied rather than an empty changeset, whichcheck-empty-changeset.mjsrejects outright.Left alone on purpose
#14033reversal register inshare-link-service.test.ts:459-491, including its line saying the read is "deliberately left as sharing: turningpublicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 landed it" — route 2 keeps that statement true, so it needed no edit.#14637(open, p2,needs-user-decision) — cited as evidence, not touched. Its fix spansshare-link-routes.tsand the runtime twin, both out of fence.sharing_dealcross-field-filter ERROR logs in the suite output — expected fixture noise from a passing test, unrelated.🤖 Generated with Claude Code
https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
Generated by Claude Code
Generated by Claude Code