docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst - #14764

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording
Sep 3, 2026
Merged

docs(changeset): the share-link refusal warn is per-hit and unlatched, not a one-time burst#14764
os-sales merged 1 commit into
mainfrom
claude/issue-14668-share-link-changeset-burst-wording

Conversation

@claude

@claudeclaudeBot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Fixes#14668

The changeset .changeset/share-link-enabled-at-redemption.md told an operator
that the redemption refusal "will burst the log once". Its own first half
contradicts that: per-hit and unlatched is precisely the property that means the
volume does not occur once. Prose only — one sentence is the whole diff, and
no source file is touched.

Measured at the emitting site, before one word was edited

packages/plugins/plugin-sharing/src/share-link-service.ts, lines 697-708
the if (!policy.enabled) arm of resolveToken(), the [#14033] gate:

if(!policy.enabled){this.logger?.warn?.('[share-link] redemption refused — publicSharing.enabled is not true on the object '+'(the block is switched off; its links resolve again once it is re-enabled)',{link: row.id,object: row.object_name,record: row.record_id,reason: 'SHARING_NOT_ENABLED',},);returnnull;}

Verdict: once per HIT, unlatched. The warn call is unconditional. Nothing
in the arm records that it has fired, and there is no Set, WeakSet, Map,
memo, dedupe or sampling anywhere on the path. The only mutable state on the
class is usageStampRefusalReported (declared line 423) and it latches a
different site — reportUsageStampRefusal() (line 842), the #12981
usage-stamp degradation, whose docblock argues at length why that one is
once-per-service-instance. Nothing analogous guards this one.

Scope of the firing is therefore the request: resolveToken() is called once per
HTTP redemption (share-link-routes.ts:233 and :317), with no cache and no
rate limit in front of it, so the line count tracks the refused-hit rate and does
not decay. The sibling eligibility refusal in stillEligible() (line 946) is
the same unlatched per-hit shape, as the changeset already says.

Per-hit is intended and ruled (#14033) — the comment immediately above the arm
states the design deliberately. The code is not changed here; only the
sentence that described it. If the site is ever judged to deserve a latch, that
is a behaviour change on a published log sink and belongs to its own card.

The diff — one sentence

Old:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects will burst the
log once.

New:

The refusal logs one warn line per refused hit and is not latched, so a
retroactive deploy with many live links on switched-off objects does not burst
the log once and subside: the volume tracks traffic, one line per refused hit,
for as long as requests keep reaching those links — until the block is
re-enabled or the links are revoked.

The correct first half is kept byte-identical, and the replacement states the
two facts an operator budgeting log retention needs and the old sentence denied:
the volume follows traffic, not the deploy, and it has an exit condition
(re-enable the block, or revoke the links) rather than an end of its own.

Everything else in the file is untouched: the frontmatter and its minor level,
the BREAKING banner, every surrounding paragraph, and the ADR-0087 marker.
content/docs/releases/** is not touched either — release notes are compiled
centrally from this changeset, which is exactly why the changeset's own wording
is the thing worth correcting.

Verification — at cbc0402b3, the final commit

Gates derived, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands
(no paths — the script takes the change set from the merge base itself). It named
9 families for this path. All 9 run green, plus 2 more run deliberately:

gateexitits own verdict line
node scripts/check-adr-0087-registration.mjs0✓ this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).
node scripts/check-changeset-no-major.mjs0✓ This diff introduces no 'major' bump.
node scripts/check-empty-changeset.mjs0✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
node scripts/check-keyed-text-bounds.mjs0✓ 148 keyed text-family columns judged, 148 bounded.
node scripts/pm/check-half-states.mjs0green
node scripts/pm/release-rehearsal-clone.mjs --self-test0green
pnpm check:changeset-gate-self-tests0green
pnpm check:objectui-changeset0green
pnpm check:pm-half-states0green
node scripts/check-changeset-fixed.mjs (added)0✓ .changeset/config.json "fixed" group is in sync with 69 public workspace packages.
pnpm check:nul-bytes (added)0check-nul-bytes: OK (scanned 8052 text file(s) ... no raw ASCII control bytes).

Every exit code was captured into a variable before any pipe, and each row
quotes the gate's own verdict line rather than a bare $?.

The two additions are not padding. check-changeset-fixed.mjs is the one family
the derivation itself flags as an artifact roster whose roster sits in a
directory one of these paths is in
— its silence "is not evidence in EITHER
direction" there, so it was run rather than read as a clearance.
check:nul-bytes is owed by any edit; a direct
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the file is clean too.

Clause ②: no, re-derived from the actual diff rather than recalled —
git diff -U0 origin/main...HEAD | grep -E '^\+\s*export ' matches nothing
(exit 1). No export is added, removed or re-shaped; nothing about the published
surface moves.

No ablation is owed, stated rather than silently skipped: this diff changes
prose in a changeset. There is no behaviour to mutate and no test whose failure
could be demonstrated by removing it — an ablation here would have nothing to
delete but the sentence itself. The claim the sentence makes was verified against
the source, above, which is the check that actually applies.

origin/main was re-fetched at the end and is unmoved at 7a17f3bf1, so the
gate union above ran on this branch's final tree.

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…14668)
The changeset for the #14033 redemption-time `publicSharing.enabled` hold
said the refusal "will burst the log once". Its own first half contradicts
that: per-hit and unlatched is precisely the property that means the volume
does NOT occur once.
Measured at the emitting site — `packages/plugins/plugin-sharing/src/
share-link-service.ts:697-708`, the `if (!policy.enabled)` arm of
`resolveToken()`: an unconditional `this.logger?.warn?.(…)` followed by
`return null`, with no latch, no `Set`, no memo and no sampling anywhere in
the path. The only latch in the file is `usageStampRefusalReported`, which
guards the unrelated #12981 usage-stamp degradation. `resolveToken` is
called once per HTTP redemption (`share-link-routes.ts:233` and `:317`)
with no cache or rate limit in front of it, so the line count tracks the
refused-hit rate and does not decay.
Prose only. The frontmatter, the level, the BREAKING banner and every
surrounding paragraph are byte-identical; the per-hit behaviour is ruled
and is NOT changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@os-salesos-sales added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026 — with Claude
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 00:10
@github-actionsgithub-actionsBot added size/xs documentation Improvements or additions to documentation tooling labels Sep 3, 2026
@os-sales
os-sales added this pull request to the merge queueSep 3, 2026
Merged via the queue into main with commit 8d1f22fSep 3, 2026
30 of 31 checks passed
@os-sales
os-sales deleted the claude/issue-14668-share-link-changeset-burst-wording branch September 3, 2026 02:01
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/xsskip-changesetPR has no user-facing published change; bypasses the changeset gatetooling

Projects

None yet

2 participants

@os-sales@claude