Strengthen the shared conformance contract - #268
Merged
Conversation
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>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atim 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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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() Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>
nicklambourne
marked this pull request as ready for review
August 14, 2026 13:02
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.
---------
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Scope
Releases spec 1.0.1 and makes the conformance contract enforce what it claims: Python now builds every valid fixture generatively from a construction registry (replacing the grep-based check that a comment could satisfy), both languages enumerate their public API against the capability registry so a new block cannot ship without conformance coverage, limits verified against live Slack documentation (
option.valueraised 75 → 150; the overflow-menu minimum was checked and left at 1 as documented), new shared limits for checkbox/radio option counts and option/URL-source URLs, structural invalid cases for table blocks, at-limit and astral-plane-emoji boundary fixtures that pin the code-point counting policy, and SPEC/ADR/PRINCIPLES rewritten to describe the actual skiplist mechanism, the normative status of limits.json, and the deliberateblock_iddivergence.Stack
Validation
641 Python tests at 93.19 percent coverage, mypy, Ruff, and 269 TypeScript tests with typecheck passed locally.