docs(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@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(protocol): state the launch-window MINOR rule where readers meet it first - #14211

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth
Sep 1, 2026
Merged

docs(protocol): state the launch-window MINOR rule where readers meet it first#14211
baozhoutao merged 1 commit into
mainfrom
claude/issue-13893-versioning-policy-truth

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#13893

Docs-only. Exactly two files, no code riders, per the triage fence ruling (「两页合一个 docs-only PR —— 两处陈述必须同时改才自洽。⛔ 该 PR 不得夹带任何代码。」).

The defect

Both pages told customers a breaking change takes a MAJOR. The operative rule is the launch-window convention: all 69 published @objectstack/* packages sit in one Changesets fixed group, and a breaking change ships as a MINOR, mechanically enforced by scripts/check-changeset-no-major.mjs (enforcing state confirmed on this tree: .changeset/pre.json is absent).

Shipped evidence, verified in packages/spec/CHANGELOG.md on this tree — all inside ### Minor Changes sections:

  • 17.2.0**BREAKING** retirement of http_request_errors_total and of sys_position.permissions (ADR-0049 enforce-or-remove)
  • 15.1.0 — removal of tenancy.strategy and tenancy.crossTenantAccess

content/docs/releases/index.mdx — the unconditionally false page, corrected first

Triage ranked this page as the heavier of the two: it carried no overriding clause at all (re-grepped on this tree for launch window / lockstep: 0 hits), so its Minor sentence was false with nothing anywhere on the page to correct it.

  • Before:"Minor releases add capabilities without breaking existing metadata or code."
  • After: the Versioning policy section now leads with the override as the rule in force today, splits major/minor/patch into a list where the minor bullet states that it may remove or change the same surfaces, cites the 17.2.0 and 15.1.0 removals, quotes the v17 notes' own framing ("minors by version number, not by blast radius" — verified verbatim at content/docs/releases/v17.mdx:28), and ends with the version number not being the upgrade-safety signal plus a link into the protocol page's Launch Window section.

content/docs/protocol/backward-compatibility.mdx — the ordered-false page

Triage's diagnosis was order of encounter: the false table sat at :20, the correction #13779 added at :177, 157 lines apart. So the primary fix is a hoist, not a reword.

AnchorBeforeAfter
Above the SemVer table(nothing)New warn callout: breaking ships as MINOR during the launch window, overrides the tables below, links to the Launch Window section
MINOR row Guarantee"Existing code continues to work""Existing code may require migration; the release notes lead with what broke", and the When column now names breaking changes
Breaking-change table6 rows mapping to Version Impact: MAJORColumn is Version Impact (launch window), those 6 rows read MINOR, with a lead-in making Breaking? the risk signal rather than the bump size
Process step 5"Breaking change ships in the next MAJOR version"Ships in the next MINOR, with the **BREAKING** changeset entry and the check-changeset-no-major.mjs mechanism named
Phase 3"Removal (next MAJOR release)""Removal (MINOR release, during the launch window)", plus the shipped 17.2.0 / 15.1.0 examples
Timeline diagramv4.0.0 — feature removedv3.5.0 — feature removed (earliest possible removal, MINOR) — it illustrates Phase 3 three lines below it
Phase-3 summary callout"…removed in the next MAJOR version"removal lands in a MINOR (the "2 MINOR releases" dwell promise is preserved verbatim — see below)

The 30-day claim: deleted, per the binary ruling

Triage ruled this in-scope and binary. Re-grepped independently on this tree over .github/ scripts/ docs/ for 30-day|30 day|thirty.day: the grep is live (many hits) but every hit is unrelated — dependency-freshness windows, PM horizon arithmetic, attachment grace periods, grant lifecycle, a backup checklist. A wider check is more decisive: the only occurrence of the phrase "review period" anywhere in the repository was the claim itself. A prior audit agrees — docs/audits/2026-06-handwritten-docs-accuracy-followups.md:51 lists "RFC/30-day review" among policy statements with no backing implementation.

⇒ No mechanism to cite, so the promise is deleted and the process renumbered to 4 steps.

Alignment with #13779's overriding section

Per the fence, that section is the source-of-truth wording and was not reworded — its authority sentence is kept and strengthened ("this section is the operative rule wherever any part of this page disagrees"). One sentence in it did have to change: it opened "The versioning tables and deprecation timeline above describe the policy in its settled form", which stopped being true once those tables were corrected to state today's rule. It now says the tables state this rule directly and that classic SemVer is the settled form returned to when the window closes.

⛔ The window's end condition is deliberately not stated — no date, no version, no criteria — because that belongs to #14043.

Two same-class lines fixed in place, declared

Beyond the anchors the card enumerates, two lines carried the identical false mapping and would have re-stated it inside the very PR removing it:

  • :26"removing or renaming properties is a MAJOR change", one bullet below the corrected table
  • :166"Removing an export is always a MAJOR change"

Both now say "breaking change, which during the launch window ships in a MINOR release". The "within a MAJOR version" stability phrasings elsewhere on the page were left alone — they are a different claim shape, and the hoisted callout governs them.

Verification

Gate family derived from the real diff by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (the script computes its own change set) — 32 families. All run at 655c21fa:

  • 31 green. Five initially returned non-zero and every one printed PREREQUISITE NOT MET (unbuilt @objectstack/formula, @objectstack/lint, @objectstack/spec, @objectstack/client-react, or a missing json-schema tree) — not findings. After turbo run build for those packages, four re-ran green, including check:skill-examples ("259 prose examples type-check across 3 surface(s)") and check:docs.
  • 1 NOT MEASURED:scripts/check-test-completeness.mjs grades a saved turbo run test log and none exists locally; the gate's own text instructs recording it as NOT MEASURED and states it is not a red.
  • pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed): exit 0.
  • Exit codes captured by redirecting to a file before reading, never across a pipe.

Control on the load-bearing anchor. Both pages now link #launch-window-minor-releases-can-contain-breaking-changes, so a green check:doc-anchors had to be shown non-vacuous. Mutating that slug to a bogus one in releases/index.mdx — mutation confirmed on disk by counting both the removed and injected strings — turned the gate red with the expected message ("renders no heading with id"). Restored via git checkout HEAD -- ABSOLUTE_PATH; restore proven by blob hash matching the HEAD blob and git diff HEAD empty.

No changeset: docs-only, nothing published. Expecting the skip-changeset label.

Filed, not fixed here

#14210 — the page's Breaking Change Process names a breaking-change label and the reporting section names a compatibility label; measured against the live 61-label set, neither exists (nearest is protocol:breaking). Same card also records that the "minimum 2 MINOR releases" dwell guarantee has no located mechanism — the same shape as the 30-day claim, but a different defect class from this card's version-number defect, so it was preserved verbatim here rather than widened into this PR.


Generated by Claude Code

… it first
The Backward Compatibility page and the releases Versioning policy both told
customers that a breaking change takes a MAJOR. The repo's operative rule is
the launch-window convention: all 69 published `@objectstack/*` packages sit in
one Changesets `fixed` group and a breaking change ships as a MINOR, enforced
by `scripts/check-changeset-no-major.mjs`.
- protocol page: hoist the governing rule above the SemVer table so it is the
first thing a reader meets, correct the MINOR row, the breaking-change
table's Version Impact column, the process ending, and deprecation Phase 3
(plus its diagram and summary callout), and align the existing Launch Window
section's authority sentence with the now-corrected tables.
- delete the "Minimum 30-day community review period" claim: the only
occurrence of "review period" in the repo is the claim itself.
- releases/index.mdx: the Minor sentence said releases add capabilities
"without breaking existing metadata or code"; it now states the override and
cites the shipped 17.2.0 / 15.1.0 removals.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 1, 2026
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation labels Sep 1, 2026
@baozhoutao
baozhoutao marked this pull request as ready for review September 1, 2026 13:14
@baozhoutao
baozhoutao added this pull request to the merge queueSep 1, 2026
Merged via the queue into main with commit 63fb473Sep 1, 2026
35 checks passed
@baozhoutao
baozhoutao deleted the claude/issue-13893-versioning-policy-truth branch September 1, 2026 13:37
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

2 participants

@baozhoutao@claude