Repository navigation
[Decision] What does turning publicSharing.enabled off mean for an ALREADY-MINTED share link? — the parent switch is mint-only while its own child predicate is now a standing policy #14033
Description
Split out of #13856 by the domain:services PM seat (#6021) at dispatch. #13856 keeps the unambiguous half (the redactFields fail-OPEN, wrong under either answer here) and is dispatched; this card is the policy question that half cannot answer.
⛔ Not mine to rule: it is a deployment-visible behaviour change on tokens already handed out, which is the same class the maintainer ruled explicitly for #13608 (ruling comment 5478574829) rather than letting a dev infer.
The question
ShareLinkService.getPolicy() returns an empty policy when the object's publicSharing block is absent or enabled !== true. Nothing in resolveToken() reads policy.enabled — the opt-in is checked only at mint (createLink refuses with SHARING_NOT_ENABLED).
⇒ Turning the object-level opt-in OFF does not stop anonymous serving of tokens already handed out.
Two defensible answers, and #13856's filer named both:
A — de-opt-in is a standing policy (links die). Symmetric with what #13608 just landed for eligibility.
B — de-opt-in is an authoring-surface switch (stops NEW mints only; existing links live on).
⭐ Why this is now sharper than when #13856 filed it
#13608 shipped on 2026-09-01 (PR #13857, fc9ba76a5). Its maintainer ruling made publicSharing.**eligibility** a standing policy, re-evaluated on every redemption, on the stated reasoning that the alternative is "a declared policy the platform does not hold".
eligibility is a child key inside the publicSharing block. So the platform now holds this shape:
| declaration | when enforced |
|---|---|
publicSharing.eligibility (the predicate inside the block) |
mint and every redemption |
publicSharing.enabled (the switch that governs the whole block) |
mint only |
⇒ Tightening a predicate inside the block kills existing links, while turning the entire block off does not. An author who wants to stop anonymous serving would have to narrow the predicate rather than switch the feature off — the opposite of what the surface reads like.
Independent corroboration that the switch is load-bearing
The #13857 contract review reached the same seam from the contract side, unprompted, and recorded it as a scoped observation:
enforcement is governed by the parent
publicSharing.enabledmaster switch —resolveTokenapplies no policy gate when the block is disabled, so a link minted through the documented system/permissive bypass (system-context.mdx ledger row 37), or minted while enabled and later orphaned by the author disabling the whole block, redeems without the eligibility re-check. This is pre-existingresolveTokenstructure, uniform across all sibling policy keys (redactFieldslikewise).
⭐ Two independent readings — the #13608 implementer and the contract reviewer — landed on the same structure from different directions. That is what makes it worth a ruling rather than a note.
What a ruling needs to settle
- A or B for
enabled. - If A: is it retroactive (every existing token on a disabled block stops resolving at deploy) or forward-only? sharing:
publicSharing.eligibilityis evaluated only at mint — a link keeps serving a record after it stops being eligible #13608 chose retroactive-and-immediate, and said so in its changeset as a breaking runtime change — the precedent exists but is not automatic. - Whether the system/permissive-bypass mint path is in or out of whatever is decided.
- ⛔ Uniformity across sibling keys is part of the answer, not a rider. The reviewer measured that
redactFieldssits under the same switch. sharing: turningpublicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 is fixingredactFieldsspecifically because it is wrong under either answer; the ruling should say whether the remaining sibling keys followenabledor their own rule, so the next key does not need its own card.
Not established
- ⛔ No behavioural measurement. sharing: turning
publicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856's filer read this from source and said so; nothing was run. Whoever implements the ruling measures first. - ⛔ No claim any deployment has been bitten.
- ⛔ Not established how many live links sit on disabled blocks in any real deployment — that is a deployment query (
sys_share_linkjoined against the objects' currentpublicSharing.enabled), not answerable from this repo.⚠️ Do not size the remedy from an in-repo zero.
Related: #13856 (the redactFields half, dispatched) · #13608 / PR #13857 (the eligibility half, ruled and landed) · system-context.mdx ledger row 37 (the bypass path)
Generated by Claude Code
Activity
huangyiirene commentedon Sep 1, 2026
裁决:A ——
enabled是持续策略,四问一次裁全(维护者 2026-09-01,总监批 #21)项目总监席 · session
session_01KGtaLpkW1mycWgkbSb3H6t维护者对本批逐字:「其他同意」—— 本卡呈报的推荐整体获批,即:
publicSharing.enabled为持续策略,resolveToken()每次兑付重查;关掉 ⇒ 存量链接停止兑付。publicSharing.eligibilityis evaluated only at mint — a link keeps serving a record after it stops being eligible #13608 先例):部署一落地,禁用块上的所有既有 token 即停止解析。changeset 必须标注 breaking runtime change,与 HoldpublicSharing.eligibilityat redemption, not only at mint #13857 同型。enabled关则一律拒付(含 system-context.mdx ledger row 37 的旁路铸造路径)。enabled关 ⇒ 整块不生效 ⇒ 拒付(子键求值 moot);enabled开 ⇒ 块内各策略键按持续策略在兑付期求值(eligibility已然,redactFields由 sharing: turningpublicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 修复)。后续兄弟键⛔ 不再单独立卡。实施注意(随派发单同行)
redactFields半边在 sharing: turningpublicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 已派发,⛔ 不合卡、不重做;plugin-share-link服务侧;若触packages/spec/**或扩宽 accept 面,条款②照走。状态转移(同笔)
needs-user-decision→pm:queue;priority:p1securitydomain:services不动。Generated by Claude Code
os-sales commentedon Sep 2, 2026
Claim:
domain:servicesPM seat, sessionsession_01AUF1NoViznQK32gqpK8wS8(GitHubos-sales), 2026-09-02 ~11:50Z.session_01AUF1NoViznQK32gqpK8wS8claude/issue-14033-public-sharing-enabled-standing-policy/home/user/objectstack-issue-14033(dev-created, worktree-first)domain:services(packages/plugins/plugin-sharing)packages/plugins/plugin-sharing/src/share-link-service.ts(getPolicy/resolveToken— the redemption gate onpublicSharing.enabled, including the system/permissive-bypass-minted links);packages/plugins/plugin-sharing/src/share-link-routes.tsONLY if the refusal's HTTP mapping needs an existing code wired (no new code); tests underpackages/plugins/plugin-sharing/src/*.test.ts; one changeset (@objectstack/plugin-sharing, marked as a breaking runtime change per the ruling's point 2, same form as PR HoldpublicSharing.eligibilityat redemption, not only at mint #13857's).content/docs/permissions/system-context.mdxONLY through the census gate's own regeneration (share-link-service.tscarries fiveisSystemanchors on row 37 — any line shift above them moves the page; declared adjacent mechanical fix). ⛔ Not touched:packages/spec/**(a spec change is a stop condition),content/docs/releases/**,rule-hooks.ts/sharing-plugin.ts(held by PR fix(plugin-sharing): let system writes materialize sharing rules — drop the isSystem skips in bindRuleHooks (#13533) #14528),redactFieldshandling (landed by PR fix(plugin-sharing): declared publicSharing.redactFields survive the object opting out #14171 for sharing: turningpublicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 — not redone).share-link-service.ts— PR fix(plugin-sharing): report a refused resolveToken usage stamp once as a durability degradation — #12981 batch 9 #14383 (The durability log-level gate cannot see thecatch { return null; }seeder family — 15 files outside #12923's five, and neither widening path is cheap #12981 b9) MERGED 09:49Z; every open PR branch diffed against its merge-base at ~11:48Z, no open PR holdsshare-link-service.tsorshare-link-routes.ts;sharing-plugin.tsis held by PR fix(plugin-sharing): let system writes materialize sharing rules — drop the isSystem skips in bindRuleHooks (#13533) #14528 (审批回写(系统身份)不触发共享规则物化,「批准后团队看不见」——平台只记一条日志、无补偿、无声明式手段 #13533) and is outside this surface.os-devsubagent, model fable.node scripts/pm/dispatch-gates.mjs --tier packages/plugins/plugin-sharing/src/share-link-service.tson53d368921: "Model tier — no path-derived mandate: the surface hits none of the 3 declared glob(s) … Clause ② is NOT reachable from paths: a card that changes contract accept/reject behaviour or widens the public surface is fable-mandatory too, judged from the card CONTENT. This line is a FLOOR, never a clearance."resolveToken's accept set narrows (tokens on a disabled block: accepted today, refused after; retroactive).needs:contract-reviewgoes on the PR and on this card the moment the PR exists; in-seat isolated fable review before landing.publicSharing.eligibilityat redemption, not only at mint #13857 did; bypass-minted links governed too; sibling-key uniformity rule); implementation notes on the same comment (measure first;redactFieldshalf not redone; Clause-② if the accept set moves).Race re-read follows this comment before the dev is launched.
Generated by Claude Code
publicSharing.enabledon the three prose surfaces #14580 could not touch (follow-up to #14033) #14582claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 14033, "status": "done", "branch": "claude/issue-14033-public-sharing-enabled-standing-policy", "pr": "https://github.com/objectstack-ai/objectstack/pull/14580", "head": "a52cfa363", "premise_still_valid": true, "summary": "Implemented the 2026-09-01 ruling (A, retroactive, bypass-minted links governed, sibling-key uniformity) as one gate in ShareLinkService.resolveToken(): the object's CURRENT publicSharing block is read on every redemption and the token is refused with the undifferentiated null (HTTP 404 INVALID_OR_EXPIRED, identical to an unknown token) when enabled is not true — before the record probe, before the usage stamp, before any sibling key; re-enabling the block restores the same tokens; a permissive- or system-minted link on a switched-off (or never-declared) block refuses the same way. P1 was measured before the edit (15 new pins red against the unmodified service, all 'expected { link } to be null'). Reason goes to the server-side log at warn (the guaranteed sink member, same shape as the #13857 eligibility refusal); no new error code, no route change, no spec change, no export change. Changeset for @objectstack/plugin-sharing in PR #13857's form: minor + BREAKING runtime banner + adr-0087 not-required (no-migration-prescription) — minor rather than the dispatch's default patch because scripts/check-changeset-no-major.mjs records the launch-window convention (breaking ships as minor) and the ruling says 同型 #13857, which is minor. Four #14171 pins that asserted serving on a disabled/absent block are reversed in place and registered with the ruling quoted verbatim. Adjacent mechanical fixes declared in the PR body: system-context row 37 re-anchored by the census gate's own --fix (+5 lines from the permissive TSDoc), then origin/main merged via os-regen-merge.sh (merge-tree listed the page as driver-deferred) and the page regenerated on the merged tree. Hypotheses: H1 holds, H2 holds, H3 holds, H4 holds, H5 holds (routes untouched; null already mapped on both the plugin route and the dispatcher twin). Clause-2: yes, declared from the actual diff — zero export lines; behavioural accept-set narrowing on the anonymous redemption door; needs:contract-review hung on PR #14580 and on #14033 (additive POST, read-back clean, nothing stripped). Deliberately NOT edited under the claim's file surface and flagged in the PR body: row 37's 'link creation/resolution while the policy is off' prose (now half false), security.mdx's enabled paragraph, and two spec TSDocs — filed as sub-issue #14582. CI status at report time: in_progress (not waited for, per contract). Label-policy note: the dispatch said 'finding label on every out-of-scope finding' while the agent contract labels only observation-class findings — the contract was followed: #14581 (dead branch, observation) carries finding; #14582 (concrete docs follow-up, sub-issue of the queued parent) is unlabelled for PM triage.", "tests": "PRE-FIX (BASE 53d368921 + the new pins, unmodified service, lock-gated): 'Tests 15 failed | 61 passed (76)' — every red is 'AssertionError: expected { link: { …(16) }, …(1) } to be null' (the token serves with the block off). POST-FIX on final head a52cfa363 (after the origin/main merge and closure rebuild, 15 turbo tasks 0 cached): 'pnpm --filter @objectstack/plugin-sharing exec vitest run --maxWorkers=2 src/share-link-service.test.ts src/share-link-eligibility.test.ts' → 'Test Files 2 passed (2)' / 'Tests 76 passed (76)' (lock VERDICT command-exit 0). TYPECHECK on a52cfa363: 'pnpm --filter @objectstack/plugin-sharing typecheck' exit 0; 'check:test-typecheck: OK — @objectstack/plugin-sharing's test layer compiles … 2 file(s) / 3 error(s) / 3 pinned signature(s) held' (ledger unchanged; the two edited test files carry no debt entry). ABLATION on the committed tree a4f128f6b (share-link-service.ts is byte-identical on a52cfa363; the subject is imported from source in the same package, not through a dependency's exports, so no dist/rebuild leg applies): gate condition replaced by false + marker; mutation proven on disk by anchored counts ('if (!policy.enabled) {' 1→0, marker 0→1, git diff --stat 1 file 1+/1-); ablated run 'Tests 15 failed | 61 passed (76)' — exactly the 15 pins the gate protects went red, all #13857 eligibility pins + mint-gate pin + enabled:true control stayed green; restore 'git checkout HEAD -- ABSOLUTE_PATH' proven by empty 'git diff HEAD' + clean status + equal blob hashes d00a6e53 (HEAD and worktree). Direction note: pin (b) 'turn the block back ON restores the token' went RED under ablation (it asserts the OFF refusal first), not green as the dispatch template predicted; (d) and (e) stayed green as predicted. GATES on a52cfa363 (derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands; derivation identical before and after the merge; run unlocked in four batches, exit captured before any pipe): 61 green (56 derived + the 5 dispatch-named: check:dispatcher-error-vocabulary, check:error-code-casing, check:durability-log-level 'read-seam invention … none invents an unreported answer', check:optional-error-sink 'every sink declaring an optional error guarantees a warn channel', check:nul-bytes 'OK (scanned 7955 text file(s) … no raw ASCII control bytes)'; check:system-context-census 'OK — 109 elevation read sites … 145 anchors resolve'; check:engine-double-contract, check:adr-0087-registration, check:changeset-no-major, check:doc-authoring, check:query-options-erasure, check:test-source-alias, check:cross-package-test-inputs all 0). NOT MEASURED, each on its own stated prerequisite, none a red: check-test-completeness.mjs (exit 3, needs a saved test-run log — gate text says local reading is NOT MEASURED), check:dual-build-cjs-loads (exit 3 PREREQUISITE NOT MET, 54 packages without dist), check:i18n (exit 1 PREREQUISITE NOT MET, CLI not built; diff touches no translation bundle), spec check:skill-examples (exit 1, client-react dist has no .d.ts; diff touches no skill/doc example/SDK), check:type-check-debt (exit 3 PREREQUISITE NOT MET, 40 unbuilt deps; no ledger number touched). check:error-code-provenance: no such script in package.json. Repo-wide pnpm lint: CI-owned, not run.", "mcp_calls": "0 — every GitHub read and write went through the repo-scoped REST channel (probe: GET issues/14033/comments 200; writes: POST pulls 201, POST labels 200 x2 with read-back, POST issues 201 x2, POST sub_issues 201) plus the zero-quota public issue page payload for the ruling comment and the issue body", "open_questions": [ { "question": "system-context.mdx row 37 still says the bypass buys 'link creation/resolution while the policy is off'; after this PR resolution is refused. The claim limited that page to the census gate's own regeneration, so the one-cell prose correction was NOT made. Fold it into #14580 now, or leave it to sub-issue #14582?", "options": [ "A — the dev seat pushes the one-cell correction (and, if wanted, the security.mdx paragraph) onto #14580 on request; census anchors are unchanged so the gate stays green; the spec TSDocs stay with #14582", "B — leave all three prose surfaces to #14582 (Blocked-by #14580)" ], "recommendation": "A for row 37, because that table is the stated authority on what isSystem buys and will be half false the moment this lands (declared ≠ enforced prose in the authoritative ledger); B for the spec TSDocs, which sit under the claim's stop-condition path even though they are prose only." }, { "question": "Changeset level: the dispatch said 'patch unless an export is added'; the ruling says 同型 #13857 and the repo's launch-window convention (check-changeset-no-major.mjs header) ships breaking changes as minor with the BREAKING banner as the carrier. The changeset is minor. Confirm, or should it be patch?", "options": [ "A — keep minor (the convention and the ruling's 同型 both point here; plugin-sharing is in the fixed group so the group bump is the same either way)", "B — patch + BREAKING banner (the dispatch's default)" ], "recommendation": "A, because the in-repo convention text names minor as the launch-window level for a breaking change and #13857 — the ruling's stated model — is minor; B would make this the one breaking runtime change in the family shipped at a different level." } ], "out_of_scope_findings": [ "filed as #14581 (finding label, unassigned): getPolicy()'s enabled!==true branch still reads raw.redactFields (PR #14171) but has no reader once resolveToken refuses before any sibling key — dead read + stale comment; not touched because #14171's redactFields logic was fenced by the dispatch", "filed as #14582 (sub-issue of #14033, unassigned, Blocked-by #14580): three prose surfaces outside this PR's claimed file surface still describe the mint-only switch — system-context.mdx row 37 'What you get' cell, security.mdx 'Public Share Links' section, and the resolveToken / publicSharing.enabled TSDocs in packages/spec (prose only, no schema change)" ] }Generated by Claude Code
Generated by Claude Code
os-sales commentedon Sep 2, 2026
Seat collection of the dev report (PR #14580 at
a52cfa363) — two answers, one patch round before the contract reviewVerified on the tree (not from the report): 5 changed files, all inside the claim's surface (
share-link-service.ts, two test files, the changeset, the census page);git diff -U0 origin/main...origin/claude/issue-14033-public-sharing-enabled-standing-policy | grep exportmatches no code line (one changeset prose line only);git merge-filethree-way text merge ofcontent/docs/permissions/system-context.mdxagainstorigin/mainf60ab90aeis clean (the local merge driver's refusal is the generated-file rule, not a textual conflict);needs:contract-reviewpresent on PR #14580 and on this card; CI ona52cfa363all green (Lint & Repo Gatessuccess 13:30:34Z,Test Coresuccess 13:25:52Z).Answer to open question 1 (row 37 prose) — A for row 37, B for the rest. The claim's file surface is widened by exactly one cell: the "What you get" cell of
content/docs/permissions/system-context.mdxrow 37 (thepermissiveshare-link bypass), corrected to say the bypass buys link creation only and that resolution is refused since this card. That table is the stated authority on whatisSystembuys, and the ruling's point 3 names this very row; landing the PR with the cell half-false would put a declared-not-enforced sentence into the authoritative ledger. Anchors are not touched, so the census gate must stay green on its own reading.security.mdx,sharing-service.mdx(named by the docs-drift bot on PR #14580) and the twopackages/specTSDocs stay with #14582 — the spec half is thedomain:specseat's.Answer to open question 2 (changeset level) — A, keep
minor. The ruling says 「与 #13857 同型」 and #13857 shipped asminorwith the BREAKING runtime banner under the launch-window convention recorded inscripts/check-changeset-no-major.mjs; the dispatch order'spatchdefault was written before that form was checked. Clause-② staysyes(accept-set narrowing on the anonymous redemption door); the isolated contract review judges the level as well.Patch round 0 (the dev is resumed on the same branch): the row-37 cell;
origin/main(now2a2653619) merged viascripts/pm/os-regen-merge.shif the merge-tree lists the census page; the two pin files and the package typecheck re-run on the new head; the PR body's "Known stale prose" item 1 marked done. The in-seat isolated fable contract review runs on the head that push produces;needs:contract-reviewstays on both carriers until it passes.Dev interruption record: the dev subagent was terminated by the platform's session usage limit (HTTP 429) at ~13:09Z immediately after posting the report above; the limit reset at 14:20Z. Not a maintainer stop, not a dead claim — the same agent is resumed with an idempotent order.
Generated by Claude Code
claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 14033, "status": "done", "round": "patch round 0", "branch": "claude/issue-14033-public-sharing-enabled-standing-policy", "pr": "https://github.com/objectstack-ai/objectstack/pull/14580", "head": "b2d9fd87b", "premise_still_valid": true, "summary": "Round 0 executed idempotently on a re-created worktree confirmed at a52cfa363. One content change: the 'What you get' cell of system-context.mdx row 37 now reads 'Get: link **creation** while the policy is off — resolution is **not** bypassed since #14033 (`publicSharing.enabled` is a standing policy held at every redemption): a link minted this way does not resolve until the block is enabled' (commit 349ff85a0; anchors untouched). Merge state: merge-tree listed the census page as driver-deferred against origin/main 2a2653619, so origin/main was merged via scripts/pm/os-regen-merge.sh (merge commit b8484b47b; step 2 took main's side of the page, which dropped the cell; the cell was re-applied as the hand edit the script's header names, then pnpm gen:system-context-census re-derived the five row-37 anchors and the page was committed as b2d9fd87b). The cell SURVIVED the gate's regeneration (the generator re-anchors lines, it does not rewrite prose) — grep count 1 after --fix. Pushed -u origin (first attempt); remote head = local head = b2d9fd87b. PR body patched: 'Patch round 0' section added, 'Known stale prose' item 1 marked DONE in this PR, items 2–3 stay with #14582; the PATCH normalised the session footer to bare as measured before (platform behaviour, not repaired). Not touched: security.mdx, sharing-service.mdx, packages/spec/**, releases/**, the changeset level (stays minor per the seat's answer A on open question 2). needs:contract-review remains on PR #14580 and on #14033 (not re-written this round). CI on b2d9fd87b: in_progress at report time (not waited for).", "tests": "On head b2d9fd87b after the merge, dependency closure restored from the turbo cache under the lock ('Tasks: 15 successful, 15 total / Cached: 15 cached'): 'pnpm --filter @objectstack/plugin-sharing exec vitest run --maxWorkers=2 src/share-link-service.test.ts src/share-link-eligibility.test.ts' → 'Test Files 2 passed (2)' / 'Tests 76 passed (76)' (lock 'VERDICT command-exit 0'). 'pnpm --filter @objectstack/plugin-sharing typecheck' → exit 0; 'check:test-typecheck: OK — @objectstack/plugin-sharing's test layer compiles … 2 file(s) / 3 error(s) / 3 pinned signature(s) held'. 'pnpm check:system-context-census' → exit 0: 'check-system-context-census: OK — 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-read' (green on its own reading after the cell edit at a52cfa363+cell, again on the merged tree, and again on b2d9fd87b). 'pnpm check:doc-authoring' → exit 0: '✓ doc authoring guard: 393 files clean — no bare metadata literals' / '✓ … 14500 customer-facing string(s) across 710 spec sources clean' / '✓ … sibling-package prose ids hold the baseline — 831 pinned site(s) … no growth'. 'pnpm check:nul-bytes' → exit 0: 'check-nul-bytes: OK (scanned 7979 text file(s) … no raw ASCII control bytes)'. All exits captured before any pipe.", "mcp_calls": "0 this round (REST only: GET comments 200, GET/PATCH pulls 200, POST comment); 0 in the whole run", "open_questions": [], "out_of_scope_findings": [] }Generated by Claude Code
Generated by Claude Code
os-sales commentedon Sep 2, 2026
Contract review round 1 at
b2d9fd87b(PR #14580): PASS — adopted verbatim; one prose-only patch round before landingIn-seat isolated Clause-② review (fable tier; transcript verified 54/54 assistant turns served as
claude-fable-5-1; fed only this card's body and rulings, the PR, and read-only git). The verdict below is the reviewer's text, unedited.Seat disposition of the §5 notes
publicSharingoff leaves already-minted links serving — and silently drops the object'sredactFields#13856 reading; the unresolvable-schema clause in the changeset and thepermissiveTSDoc; one operator-impact line on the per-hitwarn) and note 2 (PR body behind the head) — taken into patch round 1 now, prose only, in files the PR already carries; the changeset is the release-notes input, so a stale companion entry should not compile next to this one. The same reviewer runs a delta review on the pushed head;needs:contract-reviewstays on both carriers until then.findingafter this PR merges.record-share-cascade.test.ts:728title) — noted on docs: state the standing-policy semantics ofpublicSharing.enabledon the three prose surfaces #14580 could not touch (follow-up to #14033) #14582 for the docs follow-up; not this PR's.getPolicy()'s disabled-branchredactFieldsread has no reader oncepublicSharing.enabledis held at redemption (#14033) #14581.Generated by Claude Code
os-sales commentedon Sep 2, 2026
Contract review — delta after round 1, at
c55d849d7(PR #14580): PASS — adopted verbatimSame isolated reviewer resumed on the patch-round-1 head (fable tier; transcript verified 71/71 assistant turns across both rounds served as
claude-fable-5-1). The verdict below is the reviewer's text, unedited.Seat disposition
:67wording) — the sentence before it already states the per-hit, unlatched behaviour, so this is a nuance rather than a false statement; not worth a third head for a Clause-② PR. Recorded for the seat's grouped follow-upfindingtogether with round-1 note 5 (the routes-side 401 probe) after this PR merges; the release process compiles changesets centrally and can take the wording from there.needs:contract-reviewcomes off this card and PR fix(plugin-sharing): holdpublicSharing.enabledat redemption, not only at mint (#14033) #14580 in one stroke (read back) once the body is corrected;check-clause2-carriers --pair 14580re-run; ready + auto-merge once every check onc55d849d7is green.Generated by Claude Code
claude commentedon Sep 2, 2026
os-dev-report
{ "issue": 14033, "status": "done", "round": "patch round 1", "branch": "claude/issue-14033-public-sharing-enabled-standing-policy", "pr": "https://github.com/objectstack-ai/objectstack/pull/14580", "head": "c55d849d7", "premise_still_valid": true, "summary": "Round 1 executed idempotently on a re-created worktree confirmed at b2d9fd87b. Prose only, per the seat's disposition of contract-review notes 1-4: (a) the changeset names the superseded #13856 reading ('an opted-out object's links keep resolving with the declared redactions' is superseded — with the block off they do not resolve at all); (b) one 'What changed' clause: an object the engine cannot return a schema for (no getSchema, or not registered at the moment of redemption) is enabled: false by getPolicy's definition and is refused at redemption, fail-closed, the same definition createLink uses; (c) one operator-impact line: one warn per refused hit, not latched, so a retroactive deploy with many live links on switched-off objects bursts the log once; (d) the permissive TSDoc: during a late schema scan the option helps minting, not serving. Level minor and the BREAKING banner untouched; #13856's changeset file not edited; the gate, the tests and the changeset level untouched. The four TSDoc lines moved the five row-37 isSystem anchors: check:system-context-census went red (8 problems), the gate's own --fix re-anchored 5 (final row: :449, :503, :507, :580, :610), and the row-37 'What you get' cell SURVIVED (grep count 1; no other row text changed). Merge state: merge-tree listed the census page (mixed, text-merged) against origin/main a5b95ee6e, so origin/main was merged via scripts/pm/os-regen-merge.sh (merge commit 82a722cf5; step 2 took main's side of the page, the cell was re-applied as the hand edit the script's header names, pnpm gen:system-context-census regenerated the anchors, committed as c55d849d7). Pushed -u origin on the first attempt; remote = local = c55d849d7. PR body PATCHed and read back: 'What changed' census line names both the --fix re-anchoring AND the hand-corrected cell, the Clause-2 declaration lists the one-cell hand edit, 'Known stale prose' item 1 stays DONE, round-0 section kept, 'Patch round 1' section added naming the four prose changes and the new head; the PATCH normalised the footer to bare (platform behaviour). needs:contract-review remains on PR #14580 and on #14033 for the delta review. CI on c55d849d7: in_progress at report time (not waited for).", "tests": "On head c55d849d7 (dependency closure restored from the turbo cache under the lock, 'Cached: 15 cached, 15 total'): 'pnpm --filter @objectstack/plugin-sharing exec vitest run --maxWorkers=2 src/share-link-service.test.ts src/share-link-eligibility.test.ts' → 'Test Files 2 passed (2)' / 'Tests 76 passed (76)' (lock 'VERDICT command-exit 0 · held the lock 17s'; the non-comment diff of share-link-service.ts since b2d9fd87b is empty and no test file changed, so this is the same tree the round-0 76/76 measured). 'pnpm --filter @objectstack/plugin-sharing typecheck' → exit 0; 'check:test-typecheck: OK — … 2 file(s) / 3 error(s) / 3 pinned signature(s) held'. 'pnpm check:system-context-census' → exit 0: 'check-system-context-census: OK — 109 elevation read sites in 20 packages across 45 files, all anchored; 145 anchors resolve, 27 declared non-read'. 'pnpm check:doc-authoring' → exit 0: '✓ doc authoring guard: 391 files clean — no bare metadata literals' / '✓ … 14500 customer-facing string(s) across 710 spec sources clean' / '✓ … sibling-package prose ids hold the baseline — 831 pinned site(s) … no growth'. 'node scripts/check-changeset-no-major.mjs' → exit 0: '✓ This diff introduces no `major` bump.' 'node scripts/check-adr-0087-registration.mjs' → exit 0: '::notice … ADR-0087 exemption (no-migration-prescription)' on .changeset/share-link-enabled-at-redemption.md. 'node scripts/check-empty-changeset.mjs' → exit 0: '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).' 'pnpm check:nul-bytes' → exit 0: 'check-nul-bytes: OK (scanned 7980 text file(s) … no raw ASCII control bytes)'. All exits captured before any pipe. Lock note: four queue-timeouts (exit 99) behind issue-13918's gate-runner batches before the build and the pin run acquired; no unlocked heavy run was made.", "mcp_calls": "0 this round (REST only: GET comments, GET/PATCH pulls, POST comment); 0 in the whole run", "open_questions": [], "out_of_scope_findings": [ "not filed, handed to the PM (observation, self-corrected on the final head): check-system-context-census --fix rewrites anchors sequentially, so when a new line number equals another anchor's OLD number the second replacement lands on the wrong token — measured once this round on row 37 (':499→:503' then ':503→:507' hit the just-rewritten anchor, leaving ':449, :507, :503, …' out of order; the SET was still correct and the gate green). The later regeneration from main's side produced the monotonic ':449, :503, :507, :580, :610' that is on c55d849d7. Worth a --fix ordering guard in scripts/check-system-context-census.mjs; dedup not run for it this round." ] }Generated by Claude Code
Generated by Claude Code
Merge remote-tracking branch 'origin/main' into claude/issue-13533-sy…
docs(security): add publicSharing.enabled standing-policy paragraph
docs(sharing): say why getPolicy's disabled-branch redactFields read …
docs(changeset): correct the share-link refusal log-volume sentence (#…