docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245) - #14385

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises
Sep 2, 2026
Merged

docs(protocol): drop phantom labels and unbacked promises from the backward-compatibility page (#14210, #14245)#14385
baozhoutao merged 1 commit into
mainfrom
claude/issue-14210-backward-compat-page-promises

Conversation

@claude

@claudeclaudeBot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes#14210
Fixes#14245

Both cards land in the same file, content/docs/protocol/backward-compatibility.mdx — triage said "one implementer, one PR is fine; two cards, two criteria," so each card's criterion is checked off separately below.

#14210 — phantom labels + unbacked deprecation-survival guarantee

Ruling (comment 5503137278, verbatim):

  1. Labels. Step 1 points at protocol:breaking (the only live label of that class). Step 2 drops the label instruction: a compatibility report is an ordinary issue — ⛔ no new compatibility label (a label exists only when it has a named reader; none does), and ⛔ not protocol:breaking, which marks proposals, not reports.
  2. The "minimum 2 MINOR releases" guarantee is deleted, not sourced. No mechanism exists (ADR-0049's dispositions are enforce / experimental / remove, with no dwell time), and the maintainer's 2026-08-27 ruling on transitions is verbatim 「项目在创业阶段,用户也很少,短期不考虑渐进」 — a promised dwell time contradicts a standing ruling, so citing one is not an option. The page states what is true: deprecations are retired at the boundary the ADR-0087 registry records, with no minimum survival window.

Criterion 1 — labels:

  • Breaking Change Process step 1 → protocol:breaking (confirmed live via GET /repos/objectstack-ai/objectstack/labels?per_page=100; breaking-change and compatibility do not exist in the label set).
    • Before: Breaking changes are proposed as GitHub issues with the `breaking-change` label.
    • After: Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
  • Reporting Compatibility Issues step 2 → label instruction dropped entirely.
    • Before: File a GitHub issue with the `compatibility` label.
    • After: File a GitHub issue describing the unintended change.

Criterion 2 — no unbacked dwell-time guarantee:

  • Deleted the "minimum 2 MINOR releases" claim everywhere it appeared on the page (Phase 2 heading, the mermaid diagram, and the callout), not just the one named callout — all three encoded the same false guarantee.
    • Before (heading): ### Phase 2: Migration Period (minimum 2 MINOR releases)
      After: ### Phase 2: Migration Period
    • Before (diagram): A["v3.2.0 — feature deprecated..."] --> B["v3.3.0 — migration period continues"] --> C["v3.4.0 — migration continues (minimum 2 minor releases)"] --> D["v3.5.0 — feature removed..."]
      After: a 3-node generic flow (Deprecation notice → Migration period → Removal) with no fabricated version numbers and no minimum-duration claim; the Removal node now names the ADR-0087 registry boundary.
    • Before (callout): **Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. ...
      After: **No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049]'s dispositions for a flagged property are enforce, experimental, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; ...

#14245 — re-derive the GA end-condition sentence

Ruling (comment 5503069318, verbatim):

Lands in:content/docs/protocol/backward-compatibility.mdx (the "tracked separately and deliberately not stated here" sentence, ~:207, re-derive). Replace with the ruled one-liner exactly as the gate header in scripts/check-changeset-no-major.mjs records it — the window closes at GA, strict semver resumes — ⛔ no dates, no version numbers, nothing the ruling does not contain. Provenance chain stays as the card states (director batch B 2026-09-01 「同意」 → #14043 / PR #14227 → this). Not releases/**, so no docs-only fence.

Criterion — GA end condition re-derived from the gate header, verbatim in substance:

  • Source read: scripts/check-changeset-no-major.mjs header, "END CONDITION" section — "**End condition: at GA — the fixed group's first general-availability major — the group returns to STRICT SEMVER.**" No dates, no version numbers in the source; none introduced here.
    • Before: the condition that closes the window is tracked separately and is deliberately not stated here.
    • After: the window closes at GA, and strict SemVer resumes from that point.

Full diff

--- a/content/docs/protocol/backward-compatibility.mdx+++ b/content/docs/protocol/backward-compatibility.mdx@@ -55,7 +55,7 @@ When a feature, schema property, or API is deprecated, ObjectStack follows a str
- Runtime warning is emitted on first use (once per session)
- Migration path is documented in the CHANGELOG
-### Phase 2: Migration Period (minimum 2 MINOR releases)+### Phase 2: Migration Period
- Deprecated feature continues to function without behavior changes
- Documentation is updated with migration guides
@@ -75,13 +75,12 @@ Shipped examples: `17.2.0` retired `http_request_errors_total` and `sys_position
FENCE mermaid
flowchart TD
- A["v3.2.0 — feature deprecated (warning emitted)"] --> B["v3.3.0 — migration period continues"]- B --> C["v3.4.0 — migration continues (minimum 2 minor releases)"]- C --> D["v3.5.0 — feature removed (earliest possible removal, MINOR)"]+ A["Deprecation notice — feature marked deprecated (warning emitted)"] --> B["Migration period — deprecated feature keeps working, no behavior change"]+ B --> C["Removal — retired at the boundary the ADR-0087 registry records (MINOR release, during the launch window)"]
FENCE
CALLOUT type="warn"
-**Minimum guarantee:** Deprecated features survive for at least **2 MINOR releases** before they can be removed. During the launch window the removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.+**No minimum survival window.** ObjectStack does not guarantee a deprecated feature survives for a fixed number of releases — [ADR-0049](https://github.com/objectstack-ai/objectstack/blob/main/docs/adr/0049-no-unenforced-security-properties.md)'s dispositions for a flagged property are enforce, `experimental`, or remove, and none of them carries a dwell time. A deprecated feature is retired at the boundary the ADR-0087 registry records for it; during the launch window that removal itself lands in a MINOR release, so a MINOR bump is where you should expect a removal to arrive.
CALLOUT
---
@@ -109,7 +108,7 @@ Read the **Breaking?** column, not the version number: during the launch window
### Breaking Change Process
-1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `breaking-change` label.+1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label.
2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above).
3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it.
4. **Release** — During the launch window the breaking change ships in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`. `scripts/check-changeset-no-major.mjs` fails any pull request that declares a `major` bump, because under lockstep one `major` would promote all 69 published packages.
@@ -204,7 +203,7 @@ MAJOR releases do still happen — 17.0.0 was cut precisely because its breaking
### When it stops applying
-The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes; the condition that closes the window is tracked separately and is deliberately not stated here. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**+The tables and deprecation timeline above now state this rule directly, so the page no longer contradicts itself. Classic SemVer — where a breaking change once again requires a MAJOR — is the settled form the project returns to once the launch window closes: the window closes at GA, and strict SemVer resumes from that point. Until it closes, **this section is the operative rule wherever any part of this page disagrees.**
---
@@ -213,6 +212,6 @@ The tables and deprecation timeline above now state this rule directly, so the page
If you encounter an unintended breaking change:
1. **Check the CHANGELOG** — Verify the change was not documented as intentional.
-2. **Open an issue** — File a GitHub issue with the `compatibility` label.+2. **Open an issue** — File a GitHub issue describing the unintended change.
3. **Include a reproduction** — Provide a minimal code sample showing the breakage.
4. **Reference the version** — Specify the exact versions where behavior changed.

(FENCE/CALLOUT above stand in for the literal MDX code-fence and Callout-component syntax — GitHub's body sanitizer strips angle-bracket-shaped text, so the diff is spelled with placeholders here; the real file uses the actual MDX syntax.)

Gates

Docs-only change (no package publishes anything) — skip-changeset label applied on open per this repo's convention: precedent PR #14211 (same page, same author, docs-only) carries documentation, size/s, skip-changeset.

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack derived 29 local gate families for this path (content/docs/protocol/backward-compatibility.mdx). Reconciliation via --ran:

Run reconciliation — 29 derived, 28 run, 1 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 29 derived famil(ies) accounted for — 28 run, 1 NOT-MEASURED.

The 1 NOT-MEASURED is node scripts/check-test-completeness.mjs, which grades a saved turbo run test CI log and cannot run standalone locally (own PREREQUISITE NOT MET message) — CI-only by design, not a finding.

All 28 runnable families passed at head 5ee7d7d93, including (after building the dependency closure — @objectstack/spec, @objectstack/formula, @objectstack/lint, @objectstack/client, @objectstack/client-react — that several pnpm --filter @objectstack/{spec,lint} doc gates import compiled output from):

  • pnpm --filter @objectstack/spec run check:docs229 generated files in sync
  • pnpm --filter @objectstack/spec run check:skill-examples259 prose examples type-check across 3 surface(s)
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions / check:doc-security-posture — clean
  • pnpm check:doc-anchors, check:docs-single-h1, check:docs-redirects, check:doc-authoring, check:role-word, check:published-readme-links, check:vendor-version-stamps, check:react-page-adapter-contract, check:corpus-claim-drift, check:cross-package-test-inputs, check:docs-audit-scope, check:skill-identifier-liveness — all clean
  • node scripts/check-doc-frontmatter.mjs, check-doc-route-spelling.mjs, check-docs-section-name.mjs, check-section-landing-index.mjs, check-ci-filter-parity.mjs, check-cross-package-test-inputs.mjs, check-shard-attestation.mjs — all clean
  • node scripts/check-nul-bytes.mjs — clean (repo-wide scan, no raw control bytes)

check:pm-dispatch-gates (its own vitest pin suite) was not part of this card's derived list — this PR does not touch any gate/tool script, only the target MDX content, so no derivation family or its self-tests apply beyond the 29 above.

CI status on this draft is in_progress at time of report — the farm runs the remaining 171 unrelated families regardless; per the dispatch contract this report is delivered at draft-PR time, not after CI convergence.

Generated by Claude Code


Generated by Claude Code

…ckward-compatibility page
- Breaking Change Process step 1: point at the label that actually exists,
`protocol:breaking`, instead of the never-created `breaking-change`.
- Reporting Compatibility Issues step 2: drop the `compatibility` label
instruction entirely — no reader exists for it, so it does not exist either.
- Delete the unsourced "minimum 2 MINOR releases" deprecation-survival
guarantee (heading, mermaid diagram, and callout) and state the true
process instead: a deprecated feature is retired at the boundary the
ADR-0087 registry records for it, with no minimum dwell time.
- Re-derive the "tracked separately and deliberately not stated here"
sentence: the launch window closes at GA and strict SemVer resumes from
that point, per `scripts/check-changeset-no-major.mjs`'s own end-condition
header.
_Generated by [Claude Code](https://claude.ai/code/session_01WLJQhde67SeTccsmnBVarV)_
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