Skip to content

Fix documentation platform behaviour - #270

Merged
nicklambourne merged 86 commits into
masterfrom
codex/fix-docs
Aug 14, 2026
Merged

Fix documentation platform behaviour#270
nicklambourne merged 86 commits into
masterfrom
codex/fix-docs

Conversation

@nicklambourne

Copy link
Copy Markdown
Owner

Scope

Fixes the documentation platform behaviour found in review: language selection no longer hijacks deep links to Python-only pages, no longer persists a language merely from visiting a reference URL, preserves the current reference page when toggling languages, and strips the ?language= parameter after applying it; TypeScript content is reachable without JavaScript; OS colour-scheme preference is respected again; every code example in the using-blocks guide is now executed against both packages in CI and pinned to its JSON output (the stale Table, Rich Text, and Header sections are corrected); the Python reference generator escapes MDX-breaking docstring characters, checks symbol coverage exactly instead of against a magic floor, renders public methods again, and labels the Builder category distinctly; the API-reference toolchain is pinned exactly; and the compatibility page is restored to the Python sidebar.

Stack

  • Train: Review remediation
  • Step: 5 of 5
  • Base: codex/fix-release
  • This is a stacked draft; review and merge it only after its predecessor.
  • After the predecessor is merged, retarget this PR to master before merging it.

Validation

Full Docusaurus build (generation, typedoc, rendering checks, legacy contract), 21 of 21 guide sections executed and matched in both languages, Python docs harness, and TypeScript tests passed locally.

Nicholas Lambourne and others added 30 commits August 14, 2026 02:52
Run push-triggered workflows only for master (PR branches already get
pull_request runs, and stacked branches previously got neither), and drop
the pull_request branches filter so pull requests targeting stacked train
branches receive checks before they are retargeted to master.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Nicholas Lambourne and others added 22 commits August 14, 2026 14:46
Adds a shared validate_type() utility (with allow_none overloads matching
the Text.to_text precedent) so blocks, elements, and objects can reject
wrongly-typed scalar fields at construction time with the library's
standard TypeMismatchError / MissingRequiredError taxonomy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- FeedbackButtons now rejects non-FeedbackButton positive_button /
  negative_button values at construction time.
- IconButton now rejects non-ConfirmationDialogue confirm values.
- URLSource.url must now be non-empty and at most 3000 characters,
  consistent with the Button.url convention.
- CheckboxGroup now enforces the Slack cap of 1-10 options (the same
  limit already enforced for RadioButtonGroup).
- Full Google-style docstrings for FeedbackButton, FeedbackButtons,
  IconButton, and URLSource, matching the established format.

action_id on FeedbackButtons and IconButton remains optional: the live
Slack reference (feedback-buttons-element and icon-button-element pages)
marks action_id as Optional for both, as it does icon_button.value.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
RawNumber, SlackIcon, ChartSegment, DataPoint, DataSeries, AxisConfig,
PieChart, and the axis charts (BarChart, AreaChart, LineChart) were
plain classes falling back to the default object repr while every other
public class reprs as pretty-printed JSON. They now subclass
RenderableMixin (following the FeedbackButton precedent); wire output is
unchanged.

Also validates that axis charts receive an AxisConfig for axis_config,
and adds full Google-style docstrings to the chart composition classes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- CardBlock now rejects non-Image hero_image / icon values (including
  ImageBlock, which is a block, not an element) and non-SlackIcon
  slack_icon values.
- ContainerBlock now rejects non-Image icon and non-RichTextBlock
  rich_text_title values.
- TaskCardBlock now rejects non-RichTextBlock details / output values.
- TimePicker added to ALLOWED_INPUT_ELEMENTS: Slack permits timepicker
  elements inside input blocks.
- Block.from_dict docstring now accurately lists which block types
  round-trip and which raise NotImplementedError.
- Full Google-style docstrings for the nine block classes added in
  the 2.1.0 release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- AlertLevel, Chart, ContainerWidth, SlackIconName, and TaskStatus (the
  type aliases appearing in public signatures) are now importable from
  the top-level package.
- SPEC_VERSION declares the shared conformance spec version (1.0.0),
  mirroring the TypeScript package's specVersion export as required by
  the spec PRINCIPLES.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records four user-visible behaviour changes that shipped undocumented in
2.1.0: FileInput emitting its type field, HeaderBlock length-checking and
plaintext-coercing pre-built Text objects, InputBlock accepting FileInput
elements, and over-long block_id values raising LengthError.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Verify the spec's contested limits against live docs.slack.dev and align
the corpus and both implementations:

- option.value.max_length corrected from 75 to 150 per the option-object
  page; Python's hardcoded check updated, TypeScript already reads the
  shared registry.
- Overflow-menu options minimum verified: Slack documents only "up to
  five option objects" with no minimum, so min_items stays 1.
- Register limits both implementations already enforce: checkboxes
  options 1-10, radio_buttons options 1-10, option.url <= 3000, and
  url_source.url 1-3000 (TypeScript previously lacked the URL-source
  check; added to the validator with unit coverage).
- New invalid cases for every new scalar leaf plus table structural
  rules (row cap, ragged rows, column-settings mismatch) and an
  emoji-based over-limit case pinning code-point counting.
- New at-limit valid fixtures (since 1.0.1): header text at 150 chars,
  header text of 150 astral-plane emoji code points, button text at 75
  chars, option value at 150 chars.
- blocks/table_block now pins an explicit block_id so both languages
  construct it identically.
- Spec version bumped to 1.0.1 in the registries and in both declared
  constants (specVersion / SPEC_VERSION).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…egistry

Replace Python's grep-based valid-fixture enforcement with a generative
harness mirroring the TypeScript approach: a registry maps every
manifest fixture ID to a construction through the public API, and a
parametrised test asserts the rendered JSON matches the fixture, so an
unregistered manifest entry or a divergent construction now fails.

Add export-enumeration capability enforcement in both languages: each
suite enumerates its package's public JSON-producing symbols (classes in
Python, factory functions in TypeScript) and asserts a bidirectional
match with spec/coverage.json, modulo an explicit commented exclusions
list (errors, enums, abstract bases, Text helpers, utilities).

Both suites now assert the declared spec version (SPEC_VERSION /
specVersion) equals the manifest's spec_version, and both exercise the
new 1.0.1 invalid cases and at-limit fixtures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cies

- SPEC.md: replace the fictional unknown-entry/stale-skip machinery with
  the real mechanism (skiplists must be empty in released states; entries
  are temporary during multi-PR transitions; both harnesses hard-assert
  emptiness), and document the generative fixture and coverage
  enforcement.
- ADR.md: state that limits.json is normative and test-enforced (every
  scalar leaf forces an invalid case in both languages) rather than
  aspirational; implementations may hardcode, tests pin agreement.
- PRINCIPLES.md: document the intentional policies - character limits
  count Unicode code points in both implementations, and Python
  auto-generates a UUID block_id while TypeScript omits it (fixtures pin
  explicit block_ids); name the declared spec-version constants.
- CHANGELOG.md: add the 1.0.1 entry covering the docs-verified limit
  corrections and the new boundary/unicode corpus.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Windows CI decodes with cp1252 by default, which turned the emoji
boundary fixture into mojibake.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Running python -c with a working directory of python/ put the source tree
first on sys.path, so the wheel and sdist smoke tests imported the checkout
rather than the installed artifact and a broken distribution would still
pass. Add -P so the interpreter ignores the current directory.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Assert python/pyproject.toml and typescript/package.json agree on the
  version before publishing either package, alongside the existing
  tag-vs-manifest checks.
- Add an NPM_TOKEN fallback to the npm workflow so the first publish of
  @nicklambourne/slackblocks (which cannot use trusted publishing until the
  package exists) authenticates with a token; the OIDC path is used
  whenever the secret is absent.
- Make uv publish rerun-safe with --check-url so re-running a partially
  failed release skips files already on PyPI.
- Queue concurrent runs per tag instead of racing (no cancel-in-progress).
- Pin all actions in the id-token-bearing workflows to full commit SHAs
  and replace npm@latest with a pinned npm 11.19.0 (>= 11.5.1 for OIDC).
- Create a GitHub Release for each tag after a successful publish, using
  the matching CHANGELOG.md section as notes (generated notes as a
  fallback), from a job holding only contents: write.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
python/test/docs/test_examples.py reads docs/examples/section_hello.json,
and tsconfig.typedoc.json extends typescript/tsconfig.json, but neither
file was in the corresponding workflow's paths filters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
.changeset/config.json and the @changesets/cli devDependency were wired to
nothing; releases are versioned by hand.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add RELEASING.md covering the python/v* and ts/v* tag scheme, the one-time
PyPI/npm trusted-publishing setup (including the npm first-publish token
bootstrap), the coordinated release procedure, and partial-failure
recovery. Drop the soon-stale "(unpublished)" version note from the root
README in favour of stating that the packages version in lockstep, and fix
the semver-range wording in the installation docs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reconcile the Table, Rich Text, Header, Markdown, Divider, and Video
sections of the using-blocks guide with the JSON both packages actually
emit, and extend the Python and TypeScript docs-example harnesses to
execute every block section's snippet and assert it matches the JSON tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Stop rewriting Python-only guide URLs for TypeScript visitors, persist
the language preference only on explicit choices (selector clicks or a
?language= query, never route-derived), keep the current reference page
across a language toggle when the sibling page exists, and strip the
?language= parameter once applied. Also restore OS colour-scheme
detection and un-hide both languages' content when JavaScript is off.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep usage/compatibility inside the Usage category for Python readers
(still hidden for TypeScript) instead of splicing it next to the
Contributing link, and flag the hardcoded sidebar couplings that must
be updated when documents or categories are renamed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Escape MDX syntax characters in docstrings outside code spans (with an
idempotency round-trip check) so a brace can no longer break the build,
replace the magic symbol-count floor with an exact check derived from
the package's declared public API, give the builder module its own
Builder category instead of a second Utilities label, and render the
public methods of documented classes. The exact check also stops module
objects leaking into the Composition objects page as documented symbols.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The typedoc-domain-groups plugin and rendering checks key on exact
rendered strings, so hold typedoc, typedoc-plugin-markdown,
typedoc-docusaurus-theme, and docusaurus-plugin-typedoc to the exact
versions the post-processing was written against.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The stricter factory typings type-check the test directory, the reference
generator's drift check must recognise typing-based aliases, and Ruff
requires an explicit zip strictness in the guide parser.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@nicklambourne
nicklambourne marked this pull request as ready for review August 14, 2026 13:02
@nicklambourne
nicklambourne changed the base branch from codex/fix-release to master August 14, 2026 13:02
@nicklambourne
nicklambourne merged commit 028a93c into master Aug 14, 2026
21 checks passed
@nicklambourne
nicklambourne deleted the codex/fix-docs branch August 14, 2026 13:35
nicklambourne added a commit that referenced this pull request Aug 17, 2026
* chore: move Python package into workspace

* test: establish shared conformance contract

* feat: add TypeScript package

* ci: add coordinated package release automation

* docs: migrate site to Docusaurus

* feat: make docs language selection site-wide

* docs: complete language-specific guides

* feat: expose versioned documentation

* feat: standardise TypeScript block factory names

* docs: improve API reference generation and navigation

* test: enforce comprehensive conformance coverage

* feat: support current Block Kit blocks in Python

* feat: support current Block Kit blocks in TypeScript

* docs: bring TypeScript API reference to parity

* docs: add validated Block Kit previews

* ci: align monorepo validation

* chore: prepare 2.1.0 release

* docs: add legacy migration contract and tooling

* docs: port early 1.0 documentation

* docs: port later 1.0 and 1.1 documentation

* docs: port 1.2 documentation

* docs: port 2.0 documentation and version boundaries

* docs: harden historical documentation delivery

* docs: make version selection language-aware

* chore: scope TypeScript npm package

* ci: give stacked pull requests check runs

Run push-triggered workflows only for master (PR branches already get
pull_request runs, and stacked branches previously got neither), and drop
the pull_request branches filter so pull requests targeting stacked train
branches receive checks before they are retargeted to master.


* ci: give stacked pull requests check runs


* ci: give stacked pull requests check runs


* fix(docs): repair legacy converter fidelity and regenerate snapshots

The historical-docs converter (now TOOL_VERSION 2) had three fidelity
defects, all fixed at the converter and baked into regenerated snapshots
for all 22 versions:

- Raw JSX <img> tags kept site-root src attributes, so every legacy
  index page's logo 404'd on GitHub Pages (Docusaurus only rewrites
  markdown-syntax images). Site-root srcs now get the /slackblocks/
  baseUrl, matching the current docs' hardcoded logo path.
- Link/.md rewrites ran inside inline code spans and fenced code blocks,
  corrupting literal examples (e.g. contributing pages showed a rewritten
  link while telling readers to write the unrewritten form). Code spans
  and fences are now masked during markdown rewriting.
- MkDocs tab labels carried literal backticks into TabItem label
  attributes; backticks are stripped.

The generator also now preserves any newer non-legacy prefix when it
rewrites docs/versions.json, derives validation from the manifest instead
of hardcoded 22/294 counts, and records its tool version in the manifest.

Regenerated diff: 294 provenance-comment updates, 277 tab-label fixes,
22 logo URL prefixes, and 4 restored code-span lines; nothing else.


* feat(docs): future-proof legacy contract and noindex historical versions

The contract checker no longer hardcodes 22 versions / 294 routes; all
counts derive from docs/legacy/manifest.json, and docs/versions.json must
now end with the registered legacy versions in manifest order, so a future
`docusaurus docs:version 2.1` can prepend without breaking the build. The
asset-existence check understands baseUrl-prefixed image targets.

Every legacy version is marked noIndex so 22 near-duplicate historical
snapshots stop competing with the current docs in external search; the
built-site contract now asserts the robots noindex meta alongside the
existing canonical-URL assertion. The local search theme sets
forceIgnoreNoIndex so the version-scoped in-site search (also under
contract) keeps covering legacy pages.


* chore(docs): run the legacy fixture determinism check in the docs build

test:legacy-fixture existed but nothing invoked it; the docs build now
runs it before the legacy contract check, so the CI workflow picks it up
without changes.


* docs: add legacy snapshot recovery runbook

Documents the frozen manifest/inventories/fixture, the regeneration
workflow when the snapshot byte-compare fails in CI (including when to
bump TOOL_VERSION), and the versions.json forward-compatibility rule.


* fix: stop type overrides, drop nulls, and copy metadata payloads verbatim

The wire serializer previously let a caller-supplied 'type' key override the
factory's type, passed null values through to the wire (Python drops None),
and snake-cased user-defined message-metadata payload keys. Factories now
always win the 'type' field, null values are dropped exactly like undefined,
and the value under 'event_payload' is deep-copied verbatim with no key
renaming. Also adds the code-point length and empty-collection helpers used
by subsequent validation and parity changes.


* refactor!: rename RangeError to OutOfRangeError

The exported RangeError class shadowed the global RangeError, making
instanceof checks and catch clauses ambiguous. The class keeps its
'out-of-range' category string. No back-compat alias is provided because the
package is unpublished.


* feat: validate required fields, tables, and code points in raw payloads

Raw-JSON validation previously accepted structurally incomplete objects such
as {type:"header"} with no text. validateKnownObject now enforces the
required fields every factory enforces, validates type-less option,
option-group, and confirmation objects contextually through their typed
parents, and adds the previously missing 'table' block rules mirrored from
the Python TableBlock (row/column limits, rectangular rows, allowed cell
types, column_settings count).

Also in this change:
- character limits count Unicode code points instead of UTF-16 code units,
  matching Python's len() semantics for emoji and other astral-plane text
- non-finite numbers (NaN/Infinity) are rejected anywhere in a validated
  payload
- 'file_input' and 'timepicker' join the input-block element allowlist
- data_table captions must be non-empty, and an absent caption now reports
  missing-required instead of type-mismatch
- checkboxes and radio_buttons enforce 1-10 options
- image objects require alt_text and one of image_url or slack_file
- message metadata event_payload contents are skipped as opaque user data
- the validate() JSDoc documents the remaining asymmetries with factory
  validation


* refactor: source composition limits from the shared registry

Replaces hardcoded confirmation, option, option-group, and chart limit
literals in the composition-object factories with the values imported from
spec/limits.json, so a registry change cannot silently diverge from the
factories. No effective limit changes.

Also:
- option() enforces the 3000-character URL limit (Python parity)
- ensureLength counts Unicode code points
- axisChart's bespoke pre-validation is removed in favor of the shared
  series-chart validator that already runs through create()


* feat!: tighten factory input types and required fields

Element input interfaces previously extended Record<string, unknown>, which
destroyed TypeScript excess-property checking and allowed callers to smuggle
arbitrary fields (including 'type') past the compiler. The index-signature
inheritance is removed, so unknown input fields are now compile errors; the
unused ElementInput and ActionInput exports are gone.

Also in this change:
- imageBlock altText is required (type level; the validator enforces it at
  runtime), matching Slack's alt_text requirement
- richTextList style is required (type + runtime) and the docstring no
  longer claims a never-applied 'bullet' default
- empty optional collections are omitted from the wire where the Python
  implementation drops them: sectionBlock fields, static select options and
  option groups, and the initial selections of checkbox and multi-select
  elements
- iconButton and fileInput limits come from spec/limits.json
- the inputBlock parameter is named 'input' like every other factory


* feat: validate attachment colors and mirror Python collection omission

attachment() now validates its color as a hex code, mirroring the Python
implementation: '#rrggbb' passes through, bare 'rrggbb' is normalized to
'#rrggbb', the Slack aliases good/warning/danger are accepted, and anything
else raises TypeMismatchError. A Color constant object mirroring the Python
Color enum values is exported.

message() and messageResponse() drop empty blocks/attachments arrays and
attachment() drops an empty blocks array, matching the Python truthiness
serialization; webhookMessage() keeps empty arrays because Python serializes
them there.


* test: cover serialization, validation, and parity fixes

Adds regression coverage for metadata event_payload opacity, factory type
ownership, compile-time excess-property rejection, input-block element
allowlist additions, required alt text and list style, table-block rules,
raw-JSON required-field and composition-object validation, code-point
length limits, empty-collection omission parity, null dropping, non-finite
number rejection, checkbox/radio option counts, option URL limits, the
OutOfRangeError rename, attachment colors, data-table captions, and the
card/container text-coercion defaults.


* feat: add validate_type helper for single-valued object fields

Adds a shared validate_type() utility (with allow_none overloads matching
the Text.to_text precedent) so blocks, elements, and objects can reject
wrongly-typed scalar fields at construction time with the library's
standard TypeMismatchError / MissingRequiredError taxonomy.


* feat: validate new context-actions elements and element limits

- FeedbackButtons now rejects non-FeedbackButton positive_button /
  negative_button values at construction time.
- IconButton now rejects non-ConfirmationDialogue confirm values.
- URLSource.url must now be non-empty and at most 3000 characters,
  consistent with the Button.url convention.
- CheckboxGroup now enforces the Slack cap of 1-10 options (the same
  limit already enforced for RadioButtonGroup).
- Full Google-style docstrings for FeedbackButton, FeedbackButtons,
  IconButton, and URLSource, matching the established format.

action_id on FeedbackButtons and IconButton remains optional: the live
Slack reference (feedback-buttons-element and icon-button-element pages)
marks action_id as Optional for both, as it does icon_button.value.


* fix: render chart composition objects as JSON in repr

RawNumber, SlackIcon, ChartSegment, DataPoint, DataSeries, AxisConfig,
PieChart, and the axis charts (BarChart, AreaChart, LineChart) were
plain classes falling back to the default object repr while every other
public class reprs as pretty-printed JSON. They now subclass
RenderableMixin (following the FeedbackButton precedent); wire output is
unchanged.

Also validates that axis charts receive an AxisConfig for axis_config,
and adds full Google-style docstrings to the chart composition classes.


* feat: validate object fields on new blocks and allow timepicker input

- CardBlock now rejects non-Image hero_image / icon values (including
  ImageBlock, which is a block, not an element) and non-SlackIcon
  slack_icon values.
- ContainerBlock now rejects non-Image icon and non-RichTextBlock
  rich_text_title values.
- TaskCardBlock now rejects non-RichTextBlock details / output values.
- TimePicker added to ALLOWED_INPUT_ELEMENTS: Slack permits timepicker
  elements inside input blocks.
- Block.from_dict docstring now accurately lists which block types
  round-trip and which raise NotImplementedError.
- Full Google-style docstrings for the nine block classes added in
  the 2.1.0 release.


* feat: export public type aliases and declare spec version

- AlertLevel, Chart, ContainerWidth, SlackIconName, and TaskStatus (the
  type aliases appearing in public signatures) are now importable from
  the top-level package.
- SPEC_VERSION declares the shared conformance spec version (1.0.0),
  mirroring the TypeScript package's specVersion export as required by
  the spec PRINCIPLES.


* docs: document 2.1.0 behaviour changes in changelog

Records four user-visible behaviour changes that shipped undocumented in
2.1.0: FileInput emitting its type field, HeaderBlock length-checking and
plaintext-coercing pre-built Text objects, InputBlock accepting FileInput
elements, and over-long block_id values raising LengthError.


* feat(spec): release 1.0.1 with docs-verified limits and boundary corpus

Verify the spec's contested limits against live docs.slack.dev and align
the corpus and both implementations:

- option.value.max_length corrected from 75 to 150 per the option-object
  page; Python's hardcoded check updated, TypeScript already reads the
  shared registry.
- Overflow-menu options minimum verified: Slack documents only "up to
  five option objects" with no minimum, so min_items stays 1.
- Register limits both implementations already enforce: checkboxes
  options 1-10, radio_buttons options 1-10, option.url <= 3000, and
  url_source.url 1-3000 (TypeScript previously lacked the URL-source
  check; added to the validator with unit coverage).
- New invalid cases for every new scalar leaf plus table structural
  rules (row cap, ragged rows, column-settings mismatch) and an
  emoji-based over-limit case pinning code-point counting.
- New at-limit valid fixtures (since 1.0.1): header text at 150 chars,
  header text of 150 astral-plane emoji code points, button text at 75
  chars, option value at 150 chars.
- blocks/table_block now pins an explicit block_id so both languages
  construct it identically.
- Spec version bumped to 1.0.1 in the registries and in both declared
  constants (specVersion / SPEC_VERSION).


* test(conformance): build fixtures generatively and enforce coverage registry

Replace Python's grep-based valid-fixture enforcement with a generative
harness mirroring the TypeScript approach: a registry maps every
manifest fixture ID to a construction through the public API, and a
parametrised test asserts the rendered JSON matches the fixture, so an
unregistered manifest entry or a divergent construction now fails.

Add export-enumeration capability enforcement in both languages: each
suite enumerates its package's public JSON-producing symbols (classes in
Python, factory functions in TypeScript) and asserts a bidirectional
match with spec/coverage.json, modulo an explicit commented exclusions
list (errors, enums, abstract bases, Text helpers, utilities).

Both suites now assert the declared spec version (SPEC_VERSION /
specVersion) equals the manifest's spec_version, and both exercise the
new 1.0.1 invalid cases and at-limit fixtures.


* docs(spec): describe the actual skiplist, limits, and divergence policies

- SPEC.md: replace the fictional unknown-entry/stale-skip machinery with
  the real mechanism (skiplists must be empty in released states; entries
  are temporary during multi-PR transitions; both harnesses hard-assert
  emptiness), and document the generative fixture and coverage
  enforcement.
- ADR.md: state that limits.json is normative and test-enforced (every
  scalar leaf forces an invalid case in both languages) rather than
  aspirational; implementations may hardcode, tests pin agreement.
- PRINCIPLES.md: document the intentional policies - character limits
  count Unicode code points in both implementations, and Python
  auto-generates a UUID block_id while TypeScript omits it (fixtures pin
  explicit block_ids); name the declared spec-version constants.
- CHANGELOG.md: add the 1.0.1 entry covering the docs-verified limit
  corrections and the new boundary/unicode corpus.


* fix: read spec files as UTF-8 regardless of platform locale

Windows CI decodes with cp1252 by default, which turned the emoji
boundary fixture into mojibake.


* fix(ci): smoke-test built distributions instead of the source tree

Running python -c with a working directory of python/ put the source tree
first on sys.path, so the wheel and sdist smoke tests imported the checkout
rather than the installed artifact and a broken distribution would still
pass. Add -P so the interpreter ignores the current directory.


* fix(release): guard, pin, and harden the publish workflows

- Assert python/pyproject.toml and typescript/package.json agree on the
  version before publishing either package, alongside the existing
  tag-vs-manifest checks.
- Add an NPM_TOKEN fallback to the npm workflow so the first publish of
  @nicklambourne/slackblocks (which cannot use trusted publishing until the
  package exists) authenticates with a token; the OIDC path is used
  whenever the secret is absent.
- Make uv publish rerun-safe with --check-url so re-running a partially
  failed release skips files already on PyPI.
- Queue concurrent runs per tag instead of racing (no cancel-in-progress).
- Pin all actions in the id-token-bearing workflows to full commit SHAs
  and replace npm@latest with a pinned npm 11.19.0 (>= 11.5.1 for OIDC).
- Create a GitHub Release for each tag after a successful publish, using
  the matching CHANGELOG.md section as notes (generated notes as a
  fallback), from a job holding only contents: write.


* fix(ci): cover missed trigger paths in unit-tests and docs workflows

python/test/docs/test_examples.py reads docs/examples/section_hello.json,
and tsconfig.typedoc.json extends typescript/tsconfig.json, but neither
file was in the corresponding workflow's paths filters.


* chore: remove unused Changesets configuration

.changeset/config.json and the @changesets/cli devDependency were wired to
nothing; releases are versioned by hand.


* docs: document the coordinated release process

Add RELEASING.md covering the python/v* and ts/v* tag scheme, the one-time
PyPI/npm trusted-publishing setup (including the npm first-publish token
bootstrap), the coordinated release procedure, and partial-failure
recovery. Drop the soon-stale "(unpublished)" version note from the root
README in favour of stating that the packages version in lockstep, and fix
the semver-range wording in the installation docs.


* docs: pin every using-blocks example to executed output

Reconcile the Table, Rich Text, Header, Markdown, Divider, and Video
sections of the using-blocks guide with the JSON both packages actually
emit, and extend the Python and TypeScript docs-example harnesses to
execute every block section's snippet and assert it matches the JSON tab.


* docs: fix language selection side effects

Stop rewriting Python-only guide URLs for TypeScript visitors, persist
the language preference only on explicit choices (selector clicks or a
?language= query, never route-derived), keep the current reference page
across a language toggle when the sibling page exists, and strip the
?language= parameter once applied. Also restore OS colour-scheme
detection and un-hide both languages' content when JavaScript is off.


* docs: return the compatibility page to the Python sidebar

Keep usage/compatibility inside the Usage category for Python readers
(still hidden for TypeScript) instead of splicing it next to the
Contributing link, and flag the hardcoded sidebar couplings that must
be updated when documents or categories are renamed.


* docs: harden the Python reference generator

Escape MDX syntax characters in docstrings outside code spans (with an
idempotency round-trip check) so a brace can no longer break the build,
replace the magic symbol-count floor with an exact check derived from
the package's declared public API, give the builder module its own
Builder category instead of a second Utilities label, and render the
public methods of documented classes. The exact check also stops module
objects leaking into the Composition objects page as documented symbols.


* docs: pin the API-reference toolchain exactly

The typedoc-domain-groups plugin and rendering checks key on exact
rendered strings, so hold typedoc, typedoc-plugin-markdown,
typedoc-docusaurus-theme, and docusaurus-plugin-typedoc to the exact
versions the post-processing was written against.


* fix: reconcile guide harnesses with the stricter API surface

The stricter factory typings type-check the test directory, the reference
generator's drift check must recognise typing-based aliases, and Ruff
requires an explicit zip strictness in the guide parser.


* fix: format the reference generator and read guides as UTF-8


---------
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant