You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(docs): stamp the release index's "current series" at version time (#15332) (#15392)
* fix(docs): stamp the release index's "current series" at version time (#15332)
`content/docs/releases/index.mdx` ends each major's entry with a status
parenthetical — "(current series: 17.2.0, released 2026-08-23)". That sentence
is derived data: the newest GA of a major is already readable from
`packages/spec/CHANGELOG.md`, which is where check-release-section-coverage
reads it from to produce its finding. Kept by hand, it went stale on three
consecutive minors (#10232, #11649, and this one), each caught after the fact.
The gate that names it cannot fire on the change that causes it: the version
commit is opened by changesets/action with the default GITHUB_TOKEN and gets no
CI, and lint.yml runs the gate without `--strict`, where a finding is advisory
and the job is green by design. Only release-coverage-patrol.yml passes
`--strict`, and a standing patrol is an after-the-fact reader by construction.
Adds scripts/sync-release-index-currency.mjs to the root `version` chain, beside
the three sync scripts already there — the shape this repo chose for the
identical drift class in #2907 and #9064 (e569cac). It imports INDEX_PATH,
SPEC_CHANGELOG, the scope predicate, the entry and status lookups and the
verdict from check-release-section-coverage.mjs, so the rewriter and the gate
read one contract and the rewriter's verdict IS the gate's.
It rewrites exactly one shape, `current series: <x.y.z>, released <date>`, and
refuses the ones that need a human sentence — a "final release:" claim a newer
minor contradicts, an entry with no status field — which then reach the gate's
own words and stop the run. Assertion 1 (a published minor has a section) is
never consulted here: a rewriter cannot write curated prose, and hard-failing
the version lane on unwritten prose would wedge it for a debt the release did
not create.
release.yml's post-version shape assertion and cut-rc.yml's staging allowlist
both resolve the surface from `syncedPaths()` at run time, joining the two lists
they already resolve on the same terms — a fourth literal would be a fourth
contract. Measured: without that third list the post-version validation rejects
content/docs/releases/index.mdx as "outside the reviewed surface", which is the
treadmill guard doing its job.
The index line itself was fixed by running the stamper, not by hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
* ci(lint): run the release-index rewriter's self-test at PR time (#15332)
Third version-time rewriter, third self-test step, beside the two already in
this job (check:docs-image-tag-sync, check:template-version-sync).
Two gates require a PR-time caller, and both name this family:
check-self-test-wired ("a script CI runs that ships a --self-test must have that
self-test run by CI"), and dispatch-gates' "a deferred pair defers the LEAD, not
the load break" — a family no every-PR workflow runs leaves its import edges
unwatched at PR time, so a change breaking the module load of
check-release-section-coverage.mjs would not redden the PR that made it. With
release.yml as the only caller that self-test was 2-of-1382 RED naming
check:release-index-currency-sync.
⚠️ Not the --strict promotion, and it cannot become one: this step runs only
--self-test, whose cases are string fixtures. It never reads the live index for
currency and never reads a release page, so no corpus state can red it. The
advisory, non-strict `Release section-coverage guard` step is untouched — this
diff is 44 insertions and 0 deletions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
---------
Co-authored-by: Claude <noreply@anthropic.com>
-e 'import { syncedPaths } from "./scripts/sync-release-index-currency.mjs"; for (const p of syncedPaths()) console.log(p);' \
687
+
> "$INDEX_LIST"; then
688
+
echo "::error::could not resolve syncedPaths() from scripts/sync-release-index-currency.mjs, so the release-index half of the release file surface is unknown. Refusing to push."
689
+
exit 1
690
+
fi
691
+
if [ ! -s "$INDEX_LIST" ]; then
692
+
echo "::error::syncedPaths() in scripts/sync-release-index-currency.mjs resolved EMPTY, so the release index would go unstaged even though the version pass can rewrite it. Refusing to push."
693
+
exit 1
694
+
fi
695
+
mapfile -t INDEX_SURFACES < "$INDEX_LIST"
696
+
echo "release-index surfaces declared by syncedPaths() (${#INDEX_SURFACES[@]}):"
697
+
sed 's/^/ /' "$INDEX_LIST"
698
+
676
699
git add -A -- \
677
700
'*package.json' \
678
701
'*CHANGELOG.md' \
679
702
.changeset \
680
703
.objectui-sha \
681
704
packages/spec/src/kernel/protocol-version.ts \
682
705
"${TEMPLATE_SURFACES[@]}" \
683
-
"${DOCS_SURFACES[@]}"
706
+
"${DOCS_SURFACES[@]}" \
707
+
"${INDEX_SURFACES[@]}"
684
708
685
709
STAGED="$(git diff --cached --name-only)"
686
710
if [ -z "$STAGED" ]; then
@@ -689,16 +713,17 @@ jobs:
689
713
fi
690
714
691
715
# Re-check every staged path against the allowlist. The pathspec above is
692
-
# convenience; THIS is the guarantee. Three filters, same allowlist the
716
+
# convenience; THIS is the guarantee. Four filters, same allowlist the
693
717
# pathspec used: the fixed release paths by pattern, then the declared
694
-
# templateand doc surfaces by WHOLE-LINE EXACT match (`-xF`) against the
695
-
# very lists that were staged — so neither derived filter can accept a path
696
-
# its declaration does not name, and neither needs regex-escaping of the
697
-
# paths to stay exact.
718
+
# template, doc and release-index surfaces by WHOLE-LINE EXACT match
719
+
# (`-xF`) against the very lists that were staged — so no derived filter
720
+
# can accept a path its declaration does not name, and none needs
echo "::error::the version commit would carry paths outside the release file surface. Refusing to push. Offending paths follow; if the version pass legitimately grew a new output, widen the allowlist in this workflow deliberately."
704
729
printf '%s\n' "$BAD" | sed 's/^/::error:: unexpected: /'
-e 'import { syncedPaths } from "./scripts/sync-release-index-currency.mjs"; for (const p of syncedPaths()) console.log(p);' \
555
+
> "${INDEX_LIST}"; then
556
+
echo "::error::could not resolve syncedPaths() from scripts/sync-release-index-currency.mjs, so the release-index half of the post-version surface is unknown. Refusing to call this tree validated."
557
+
exit 1
558
+
fi
559
+
if [ ! -s "${INDEX_LIST}" ]; then
560
+
echo "::error::syncedPaths() in scripts/sync-release-index-currency.mjs resolved EMPTY, so the release index the version pass rewrites would read as an unexpected path. Refusing to call this tree validated."
561
+
exit 1
562
+
fi
563
+
564
+
# Same four filters cut-rc.yml applies to the same surface: the fixed
565
+
# release paths by pattern, then the three declared lists by WHOLE-LINE
566
+
# EXACT match, so no derived filter can accept a path its declaration
printf '%s\n' "${UNEXPECTED}" | sed 's/^/::error:: unexpected: /'
551
574
echo "::error::the version pass wrote outside the reviewed post-version surface (paths above). This is the treadmill guard: a new version-time output must arrive together with the gate that judges it. Add the surface to the declaration its rewriter reads, and its gate to the content half below — deliberately, in one reviewed diff."
Copy file name to clipboardExpand all lines: content/docs/releases/index.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -18,7 +18,7 @@ migration steps, then covers new capabilities and notable fixes.
18
18
19
19
## Versions
20
20
21
-
- [v17.0.0](/docs/releases/v17) — Files become owned `sys_file` records with server-enforced `accept`/`maxSize` and a governed download path, bulk export becomes its own opt-in privilege, the SDK is reconciled against the routes the server actually mounts (21 dead methods out, 40+ real ones in), approval nodes route approvers dynamically via CEL expressions and decision outputs, a datasource that cannot connect fails the boot, and Node 22 becomes the supported floor; 17.1 adds partial field masking, record-view auditing on `sys_audit_log`, and a per-object read-only approval visibility tier — and makes a deactivated permission set or position actually stop granting access, withdraws the bulk-export wildcard from the shipped admin sets, and gives all three flow doors one honest HTTP status table; 17.2 tightens by-id `update`/`delete` against a silently-dropped `where` predicate or a mismatched id, retires `sys_position.permissions` and other dead ADR-0049 surfaces, and stops analytics from answering the wrong number on a cross-object filter (current series: 17.2.0, released 2026-08-23).
21
+
- [v17.0.0](/docs/releases/v17) — Files become owned `sys_file` records with server-enforced `accept`/`maxSize` and a governed download path, bulk export becomes its own opt-in privilege, the SDK is reconciled against the routes the server actually mounts (21 dead methods out, 40+ real ones in), approval nodes route approvers dynamically via CEL expressions and decision outputs, a datasource that cannot connect fails the boot, and Node 22 becomes the supported floor; 17.1 adds partial field masking, record-view auditing on `sys_audit_log`, and a per-object read-only approval visibility tier — and makes a deactivated permission set or position actually stop granting access, withdraws the bulk-export wildcard from the shipped admin sets, and gives all three flow doors one honest HTTP status table; 17.2 tightens by-id `update`/`delete` against a silently-dropped `where` predicate or a mismatched id, retires `sys_position.permissions` and other dead ADR-0049 surfaces, and stops analytics from answering the wrong number on a cross-object filter (current series: 17.3.0, released 2026-09-04).
22
22
-[v16.0.0](/docs/releases/v16) — One org identifier (`organizationId`) across hooks and actions, quorum + per-group sign-off (会签) approvals with metadata-declared decision actions, time-relative automations, filtered roll-ups, strict dashboard widgets, an identity-scoped MCP stdio transport, and a platform-wide enforce-or-remove sweep that makes dead metadata loud; 16.1 adds a `requires` capability-provider preflight, two more dashboard build gates, and `runAs:'user'` automations that run with the triggering user's real grants (final release: 16.1.0).
23
23
-[v15.0.0](/docs/releases/v15) — Explain record access layer by layer, a docked AI workspace in the Console, project-ready Gantt charts, and phone sign-in; 15.1 adds permission-following attachments, no-code third-party connectors, dashboard-wide filters, pinyin search, and whole-record inline editing — with materially safer multi-tenant and write-path defaults (final release: 15.1.1).
24
24
-[v14.0.0](/docs/releases/v14) — ADR-0090 vocabulary convergence completed, object `enable.*` flags become real gates, admin user management, phone/SMS auth, book-audience enforcement, data-lifecycle contract, and effective-dated grants (final release: 14.8.0).
0 commit comments