Adding a language implementation
Use this workflow to take a new implementation from an API proposal to a maintained package. Completion means a language-native public API, the full shared contract, useful helpers, tested documentation, and working package and release integration. Passing the JSON fixtures is one part of that work.
The maintained implementations are currently Python, TypeScript, Go, Java, C#, Ruby, Rust (introduced in 2.6.0), PHP (introduced in 2.7.0), and Elixir (introduced in 2.8.0). Recheck that list against the packages and coordinated release at the start of the project. A plan or an unfinished implementation does not add a language to the release set.
Paths and commands below are relative to the repository root unless stated otherwise. Read Contributing, the specification, the implementation principles, and ADR 0002 first. ADR 0002 permits idiomatic generated APIs and requires coordinated package versions. It supersedes the earlier decisions on those subjects in ADR 0001.
1. Establish the baseline and parity inventory
Start from current master. Record its commit, the shared spec version, package
versions, actual published versions, and the proposed first release. A manifest
version may still be unreleased; check tags and registries before choosing the
next version. The new package joins a coordinated release rather than choosing
an independent 0.1.0 or 1.0.0 version.
Create a checked-in implementation plan with a parity inventory. Give each item an implementation location, test, documentation location, and status. Include:
| Inventory | Source and required evidence |
|---|---|
| Types, fields and roles | Every type, interface, enum and field kind in spec/model.json; native names/types, parent membership, requiredness, defaults and coercions |
| Wire contract | Every valid fixture in spec/manifest.json, constructed through the public API |
| Validation | Every invalid case in spec/fixtures/invalid/manifest.json; every scalar leaf in spec/limits.json; vocabulary equality with spec/vocabulary.json |
| Public coverage | Every capability in spec/coverage.json, plus independent discovery of public JSON-producing symbols and justified exclusions |
| Supporting APIs and helpers | Styles, text conveniences, composition helpers, inspection/editing, JSON integration and any supported Slack SDK adapter |
| User and maintainer experience | Installation, quick start, cookbook, API comments/reference, examples, development commands, compatibility policy, package checks and release setup |
Compute inventory counts from the files; do not make today's totals the test
oracle. Compare with Python's public exports and helpers as well as the newer
implementations. The model and capability registry do not enumerate every
useful convenience. For example, review Accordion, AccordionSection,
Paginator, Python's block_kit_builder_url, Workflow.from_url, and Color
utilities. Implement equivalent useful behavior using the new language's
idioms, or record a specific reviewed reason for a difference. Historical aliases
and deprecated APIs, such as Python's legacy attachment Field, need an explicit
disposition; they need not be copied into a new API.
Adding a language to the existing contract does not itself change the spec version. If the work discovers a missing capability or conflicting rule, resolve it through Adding New Block Kit Features and update the existing implementations too. Do not change shared fixtures just to fit the new implementation.
Gate: the plan covers the entire current contract, helpers and integration work, with no unexplained omissions.
2. Review a native API before generating it at scale
Choose conventions from the target ecosystem and demonstrate them in a small consumer project. Existing implementations supply behavioral evidence; their class hierarchies and naming are not templates for every language.
Write down these decisions:
| Decision | Questions to settle |
|---|---|
| Package and support policy | Package name/namespace ownership, minimum compiler/runtime, supported platforms, dependency policy, and how raising the minimum is treated |
| Construction | Constructors, builders, keyword arguments or literals; required versus optional fields; text and collection conveniences; method chaining where idiomatic |
| Value semantics | Ownership/mutation, equality, getters, copying and safe editing of nested values; whether invalid intermediate builders can escape |
| Type system | Concrete values, parent-role types, enums, reserved-word/name collisions, aliases and discoverable public exports |
| Future compatibility | How new enum variants, fields and role members affect callers, including exhaustive matches and source compatibility |
| JSON and errors | Native encoder integration, when validation runs, error category/path access, optional parsing scope and checked extension behavior |
Prototype plain/markdown text, a button in a section, a complete message, a table, nested rich text, a parent-sensitive task and a composition helper. Show construction, serialization, inspection, editing, an invalid input and normal error handling. Compile or execute the consumer against the public package boundary, using the minimum supported toolchain as well as current stable.
Define missing, explicit null, false, zero, empty string and empty collection
behavior separately. Document deliberate defaults. Python's auto-generated
block_id and the existing message-response defaults are documented divergences
in spec/PRINCIPLES.md; choose and document the new language's behavior rather
than inheriting an accident from a port.
If a Slack SDK adapter is appropriate, prove the downstream send path and its dependency/version requirements. SDK types can lag Slack: do not silently drop fields or rely on unchecked casts. A documented native JSON path can be the integration when the ecosystem has no suitable SDK. Keep HTTP clients, transport and framework dependencies outside the core unless the design specifically requires them. Provide a runnable example using an established SDK when available, and a native HTTP client alternative. Exercise the displayed source against a local mock server: preserve the full payload, check authentication and API/HTTP errors, and prove bounded rate-limit retries. Serialization plus a curl command alone does not prove a native sending integration. Verify the client dependencies on the supported minimum compiler independently of the core package; record any separate requirements. State whether testing used mock servers or a real Slack workspace.
Gate: reviewers can judge representative real calls, type safety, errors and editing ergonomics before a generator repeats the design across every type.
3. Build the package foundation and generation boundary
Create the language directory with its manifest, public entry point, tests,
README, changelog, license packaging, formatter/linter configuration and one
repeatable local verification command. Declare package and spec versions
separately, and test the exported spec version against spec/manifest.json.
Choose the ecosystem's lockfile policy and test dependency resolution at the
minimum supported toolchain.
Decide which code is generated. Generation is appropriate when it produces a native API and removes repetitive maintenance; it is not mandatory. Handwritten and generated implementations meet the same contract.
- Read the shared model directly. Do not derive the new API by parsing another implementation's source.
- Source scalar limits from
spec/limits.json, either by bundling it or generating checked-in constants. Generate or equality-test native vocabulary tables. - Use one resolved naming/type map for generated code, signatures and reference metadata. Fail on unknown field kinds, unresolved roles and name collisions.
- Keep contextual validation and helpers explicit; the model is not the whole implementation. Document ownership of generated and handwritten files.
- Make output deterministic, including formatting and file order. A check mode
must detect added, missing, changed and obsolete generated files. A plain
git diffcan miss untracked output. - Check generated sources into Git. Users installing the package must not need the monorepo, the generator's toolchain or files outside the package.
Existing examples include ruby/generator/generate_models.py with --check,
java/generator/generate_models.py, csharp/generator/generate_models.py, rust/generator/generate.py, php/generator/generate.py, and
go/internal/generatebuilders/. Adapt their contract handling to the chosen
idiom; do not copy their language-specific assumptions.
Keep registry publication disabled while the package is incomplete. Do not add it to the active coordinated release just to make the scaffold look complete.
Gate: the prototype installs in a clean consumer, regeneration is repeatable, and a deliberate stale/missing generated output makes the check fail.
4. Implement the complete behavior
Cover every model kind and role, supporting values such as RichTextStyle, all
payloads and the agreed helper inventory. Preserve exact wire names,
discriminators, omission rules and ordering. Types sharing a wire discriminator
can still have different roles: an image block and an image element are not
interchangeable.
Maintain a rule inventory linking each shared rule to its native validator and positive/negative tests. Use the spec and existing rule implementations together; resolve disagreements against the shared contract and official Slack evidence. Check both local values and their receiving parent:
- Required fields, exclusive combinations, text kinds, numeric ranges and allowed child roles.
- Message, modal, App Home and attachment restrictions; expanded helper block counts; aggregate payload text limits; modal submit requirements.
- Rules that change with context, such as pending tasks accepted inside a plan but rejected outside it. Validate at the point that knows the parent context.
- Different structures with similar names: plain tables permit ragged rows; data tables require rectangular rows.
Count text limits in Unicode code points, not bytes, UTF-16 units or grapheme clusters. Preserve false and zero when supplied. Reject nonfinite JSON numbers; define integer/decimal representation and overflow behavior, and test precision boundaries so conversion cannot silently round a supported value.
For each supported JSON ingress path, validate nested values and parent context as rigorously as native construction. Test serialization/reconstruction round trips and omitted defaults. State which payloads parsing supports; construction support does not imply a parser for arbitrary inbound Slack events. Any explicit extension/raw JSON facility must prevent collisions with modeled fields or the discriminator and cannot replace typed support for a modeled feature.
Test helpers as APIs: ordering, empty inputs, page boundaries, IDs, labels, encoding and validation after expansion. Include native getters, safe editing, copy/ownership behavior and JSON protocol integration in the behavior tests.
Gate: the full inventory is implemented with documented intentional differences and no modeled feature hidden behind raw JSON.
5. Build an independent conformance harness
Use the shared files as expected results, with explicit native construction code as the implementation under test. A serializer and parser generated from the same model can agree with each other while both are wrong.
- Discover the valid manifest IDs and canonical fixture files. Assert exact set equality with the construction registry, detecting missing, duplicate and stale registrations. Construct each fixture through public typed APIs; do not load its JSON into a parser or extension map as a substitute. Compare parsed JSON independently: object order and whitespace do not matter; array order, keys and values do.
- Map every invalid-case ID to a public attempt and assert its exact normative
category:
length-exceeded,out-of-range,mutually-exclusive,type-mismatch,missing-requiredorinvalid-usage. Isolate the intended failure and assert representative complete error paths. Error classes and prose remain native. - If the type system prevents a runtime construction, exercise the corresponding bad input through a checked public ingress boundary. Plan that boundary at the API checkpoint. Compile-time rejection tests supplement the required invalid-case category assertion; they do not silently replace it.
- Assert every scalar limit leaf has an invalid case with the matching
constraintpath; compare generated/bundled limits and vocabulary to their registries. Add positive boundary tests as well as the invalid corpus. - Independently enumerate the real public API and map JSON-producing symbols to shared capabilities. Name and justify helper/supporting-type exclusions and test them separately. An export list emitted by the implementation generator is insufficient on its own: also detect newly added handwritten exports. Prove the guard catches an unmapped public producer.
- Assert the declared spec version and keep
conformance/skiplist.txtempty. The released harness must hard-fail on entries. Scope an early foundation suite honestly until full conformance is implemented; do not weaken a full conformance gate to make a partial stage appear complete.
Useful references are python/test/conformance/,
typescript/test/conformance.test.ts, go/conformance_test.go,
go/invalid_conformance_test.go, and Ruby's separate
ruby/test/valid_constructions.rb, ruby/test/invalid_cases.rb and
ruby/test/registry_test.rb.
Gate: all current fixture IDs, capabilities and limits are accounted for, with independent public API coverage and an empty skip list.
6. Verify code quality and the installed package
Conformance does not cover every language-specific failure. Add native unit and
integration tests for ergonomics, validation branches, mutability/ownership,
serialization, helpers and any promised SDK/framework integration. Use property
or fuzz tests where they expose a concrete risk, such as recursive round trips
or numeric conversion. Every editing test must require its expected outcome:
valid setters, appenders and clears must succeed; invalid combinations must
assert the category and relevant path. Conditional assertions such as
if let Ok(...) can silently skip regressions. Include compatible starting
values for fields that depend on another field, and distinct values for append
operations subject to uniqueness rules. Spot-check deliberate broken operations
to confirm the behavior tests fail.
Test the runtime's indirect-call and aliasing behavior as well as ordinary calls. PHP readonly properties can retain references inside copied arrays, and internal callbacks can coerce scalar arguments despite a strict caller. Exercise referenced collection entries, nested rows and exported copies; reject invalid element types before convenience normalization. Check long and escaped JSON strings around numeric boundaries so tokenizer limits cannot silently skip validation.
Keep metadata and extension JSON opaque during Block Kit validation. Test maps
containing unrelated type, text, rows and tasks keys through native
construction and each parsing boundary. Check original numeric literals before
any lossy conversion, including precision loss, underflow and overflow, in a
fresh default-feature consumer as well as supported dependency feature modes.
Define required formatting, lint/static-analysis, type-checking, documentation and coverage commands. Measure handwritten validation/core/helper coverage separately so generated accessors cannot mask untested branches. Document the coverage scope and a meaningful threshold. Where compile-fail or negative type checks are used, assert the expected diagnostic with a passing companion case; an unrelated syntax/import error must not count as success.
Build the actual distributable, inspect its file list and metadata, then install it in an external temporary consumer with no workspace/path override:
- Verify package name/version, minimum runtime, dependencies, license, README, documentation and any required type/signature/source files.
- Ensure no runtime code resolves
../spec, imports from the checkout, or requires development tools. Exclude caches, credentials and build scratch. - Compile/run a realistic payload, native JSON encoding, error handling and public types using only the installed artifact.
- Test minimum and current supported toolchains, clean dependency resolution, and relevant optional-feature combinations. For library ecosystems with feature unification or consumer-selected dependencies, test those paths too.
Use ruby/bin/package-smoke as one example of installation and type checking
outside the checkout. Select equivalent ecosystem tools; parity means comparable
assurance, not identical coverage percentages or dependencies.
Gate: ordinary users can install the artifact and use its documented API without the repository or maintainer environment.
7. Integrate continuous integration and maintenance
Add a language workflow covering the minimum and current stable toolchains on
Linux, macOS and Windows where supported. Record any narrower platform promise.
Development/nightly versions can be informational; required checks must work on
supported stable versions. Use GitHub-provided runners, following AGENTS.md.
Pin Actions consistently with existing workflows and keep routine PR jobs free
of publishing credentials.
Run conformance, native tests, generation drift, quality/coverage, examples and
package-consumer checks. The documented complete local check must execute
separately resolved integration/example suites too; compiling them is insufficient.
Cache dependencies with keys that reflect toolchain and
lockfile changes. Add dependency update configuration to .github/dependabot.yml
for the new ecosystem when supported.
Follow the change-detection convention in .github/workflows/ruby.yml: required
check names still report on unrelated PRs; relevant work happens at step level.
Include shared spec inputs, docs/examples, manifests, generator inputs and the
workflow itself in filters. On a changed-file lookup failure, run checks rather
than accidentally skipping them. Extend .github/workflows/docs.yml with the
new source paths, toolchain and generator prerequisites.
Update branch protection/rulesets to require the agreed stable checks once they exist. Test a relevant change and an unrelated change: inspect the actual jobs, steps and runner metadata, not just green skipped checks or YAML. Record the required matrix and local equivalents in the package README and contributor guide.
Gate: CI exercises the delivered scope, required checks remain available, and contributors can reproduce them locally.
8. Bring documentation to parity
Preserve release history first
Before advancing the coordinated package version or adding the new language to current user docs, freeze the outgoing release as described in RELEASING.md. Generate the outgoing API reference before taking the snapshot. If feature docs already changed on the implementation branch, generate the snapshot from the verified outgoing release sources. Do not copy the new language into a release that never contained it, or modify older frozen/legacy snapshots to advertise it.
Check direct historical URLs with a stored preference for the new language, including paths with and without a trailing slash. Unsupported versions must return to current documentation without relabelling an older page as the new language. Verify this in a browser after hydration as well as in generated HTML.
Current docs use the coordinated package version. Coordinate their integration with the version update and mark availability honestly while unreleased; do not tell users to install a registry version that does not exist.
Cover the user journey
Add native installation and quick-start examples, a package README, root README example and language badges, compatibility/troubleshooting guidance, sending messages, cookbook and composition examples. Update every applicable language-switched section, including less obvious pages such as migration and validation guidance. Shared concepts can stay shared, but selecting the new language must not reveal empty content or another language's calls.
Document public types, parameters, return values, defaults, validation errors,
limits, role restrictions and Slack links in native comments/docstrings. Generate
the API reference from the language's documentation tool or reviewed metadata;
include handwritten helpers and public exports as well as model types. Compile
or execute examples from the actual README/guide sources, not manually copied
test strings. Add reusable examples under docs/examples/ and compare common
examples with docs/examples/section_hello.json where applicable.
For each new language, add one <details> accordion under Installation and
another under Quickstart in the root README. Omit the open attribute so both
start collapsed. Use a plain-text <summary> with the language name and, for
installation, runtime requirements. Keep compatibility links, commands and native
examples inside the relevant accordion; leave shared illustrations outside.
Keep blank lines around fenced code blocks so GitHub renders them as Markdown.
Check GitHub's rendered README: every language must have both accordions, all
must start collapsed, and every existing command and example must be preserved.
Keep README badges consistent
Add the new language to both badge rows in the root README: its registry version
and its CI workflow. Use the same language order as the installation examples,
style=flat and labelColor=555. Registry badges use a white logo and a highlight
associated with the language, not a shared registry blue. The current palette is:
| Language | Registry | Highlight |
|---|---|---|
| Python | PyPI | 3776AB |
| TypeScript / JavaScript | npm | 3178C6 |
| Go | Go module proxy | 00ACD7 |
| Java | Maven Central | E76F00 |
| C# | NuGet | 512BD4 |
| Ruby | RubyGems | CC342D |
| Rust | crates.io | CE422B |
| PHP | Packagist | 777BB4 |
| Elixir | Hex | 8847B9 |
Choose and record a recognisable language colour when extending this table.
Keep the shared Docs highlight purple (4A154B) and Licence highlight green
(2E8B57). CI badges retain status-driven colours and must report default-branch
pushes (branch=master&event=push), with a link to the matching workflow.
Verify the package identity, badge rendering and destination links. Before the
first registry release is available, describe preparation honestly; do not
substitute a hard-coded released version for a missing registry result.
Extend the shared download total
The README and ndl.au/projects share the public
Watch download endpoint.
Add a new registry's comparable all-time counter to the Watch exporter in the
noodle monorepo (state/ci-cluster/monitoring/slackblocks_exporter.py), alongside
its collector, aggregate registration, dashboard and tests. Document the source's
meaning and cache/freshness limits in state/ci-cluster/README.md. Ecosystems
without a comparable download count remain explicitly excluded.
Stage collection separately from activation until the first stable release is indexed and its counter is verified. Test aggregate arithmetic, a real zero, missing/malformed data and stale sources: an unavailable required source must make the aggregate unavailable, never silently contribute zero or a partial sum. After activation, verify the public endpoint and update the README badge's alt text and Download statistics definition to match the active source set.
The consumer in projects/ndl_au/ui/src/lib/projects/downloads.ts already reads
this aggregate with a five-minute revalidation interval. New Slackblocks registry
sources need no second calculation or copied database total there. Check both
public surfaces after deployment and cache expiry.
Extend the complete site integration
| Area | Files to review and extend |
|---|---|
| Language identity, routing and content | docs/src/components/LanguageContext/index.tsx, LanguageContent/index.tsx, LanguageSelector/index.tsx in the same components directory |
| Version availability and labels | docs/src/components/VersionSelector/index.tsx, docs/src/theme/DocVersionBadge/index.tsx; new versions infer languages from their reference pages; preserve historical availability |
| Navigation | docs/src/theme/DocSidebar/index.tsx, DocBreadcrumbs/index.tsx, DocItem/Paginator/index.tsx, TOCItems/index.tsx in the same theme directory |
| Search | docs/plugins/language-search-indexes.cjs, docs/src/theme/SearchBar/, docs/src/theme/SearchPage/ |
| Site configuration and reference generation | docs/docusaurus.config.ts (versions, labels, syntax highlighting), docs/package.json, docs/scripts/generate_*_reference.*, docs/scripts/reference_pages.mjs, docs/docs/reference/index.mdx |
| Regression guards | docs/scripts/check_language_content.mjs, check_search_language.mjs, check_version_selector.mjs, check_release_snapshots.mjs in the same scripts directory; add native snippet/reference-rendering checks to the build |
| Maintainer instructions | Root README.md, AGENTS.md, RELEASING.md, docs/docs/contributing/index.mdx, this guide and docs/docs/contributing/maintaining-block-kit.mdx; update language maps in spec/SPEC.md and spec/PRINCIPLES.md |
Search for existing language lists and patterns as a cross-check; this table is not a substitute for finding integration points added since it was written. Adapt routing names and reference groups deliberately instead of mechanically replacing another language's name.
From the repository root, after installing the documented toolchains and workspace dependencies, run the following. Build the TypeScript package first so the docs' TypeScript consumer example can resolve its exported declarations on a clean checkout:
pnpm --filter @nicklambourne/slackblocks build
pnpm --filter @slackblocks/docs build
pnpm --filter @slackblocks/docs typecheck
pnpm --filter @slackblocks/docs check:legacy-snapshots
pnpm --filter @slackblocks/docs check:release-snapshots
Extend these gates for the new language. Inspect rendered signatures, required and optional parameters, links and helper entries. Exercise desktop/mobile language and version selectors, direct reference URLs, search filtering and navigation. Verify the new language is absent from versions predating its first release and that switching to an older version has a valid destination.
Gate: a user can discover, learn and use the new language throughout the site, and the examples and reference are verified automatically.
9. Prepare publishing and coordinated releases
Treat release setup as implementation work, with actual publication a separate maintainer action. Extend RELEASING.md with concrete instructions for the chosen registry.
Package ownership and authentication
Verify name/namespace ownership and registry metadata early. Check the registry's official documentation for first-publish rules, trusted publishing, signing, provenance and immutable-version behavior; record the links and date checked. Do not assume a pending publisher or bootstrap method works the same way in every registry.
Document the exact GitHub environment, workflow filename, repository identity, required account settings, secret/variable names and credential scopes. Verify account-side setup with the maintainer when it is not inspectable from CI. Prefer trusted publishing where supported. If a first release needs a token, make the bootstrap mode explicit and scoped, and document how to revoke it after trusted publishing is verified. An authentication check is not evidence of publication.
Workflow and guard changes
Build and validate on PRs without publishing. Add a tag-bound publisher with manual dispatch at the same tag, restricted write permissions, the intended environment and release concurrency protection. Verify the tag's format, version, commit, and agreement with coordinated package versions before any registry write. Test positive and negative guards, including wrong tag prefixes, version mismatches and dispatch from a branch.
In .github/workflows/coordinated-release.yml, update all of:
- Relevant path filters and version extraction/equality checks.
- Changelog presence/date checks and readiness conditions.
- The complete tag list, existing-tag verification and atomic tag creation.
- The publisher dispatch matrix and failure monitoring.
Audit existing publisher workflows and guard scripts, including
.github/scripts/verify-release.py and the compatibility entry point
ruby/bin/verify-release, for package-version and activation coverage. The shared
guard is invoked by every publisher, so extend its registry and tests as well as
the coordinator’s matrix and atomic tag list. Extend the
version check in docs/docusaurus.config.ts, package version constants/tests,
release instructions, release notes and package-install examples too. Go derives
its version from tags and its major module path; count packages, stored manifest
versions, changelogs and tags separately.
Keep the new publisher and coordinator activation gated until implementation, docs, package and account setup are ready. Test an attempted release of a partial train is rejected before tags or registry writes. When activating it, set every package to the chosen coordinated version, date every changelog consistently and update Java's reproducible-build timestamp. Follow the release guide's explicit publisher dispatch procedure; do not rely on a multi-tag push alone to start all publishers.
First publication and recovery
At the approved release commit, run the coordinator and monitor every publisher, with particular attention to the new registry. Verify the exact version and metadata at the registry, native hosted API docs where applicable, its GitHub Release and a clean installation from the registry itself. Record artifact checksums/provenance where available; if publishing rebuilds the package, verify its source/toolchain inputs rather than claiming it uploaded a previously tested archive.
Document registry-specific recovery. After an uncertain or failed upload, inspect registry state before retrying the failed publisher at the existing tag. Complete missing documentation/GitHub Release steps separately when the package is already published. Never move public tags or replace a successful artifact. A bad shipped artifact requires the next coordinated patch release.
Gate: dry-run/package checks and account setup are evidenced, the coordinator covers every maintained package, and release/recovery steps are executable.
10. Deliver a reviewable train and completion record
The exact number of PRs can vary. Keep dependencies explicit, validate each stage's delivered scope and merge in dependency order. A useful split is:
| Stage | Exit evidence |
|---|---|
| API design | Native consumer prototype, parity inventory, naming/default/error decisions and dependency/support policy reviewed |
| Foundation | Package scaffold, generation, local checks and stable CI; prototype works as an installed package; publication disabled |
| Complete contract | All types/roles/rules and conformance registries pass, independent export coverage, empty skip list |
| Helper and quality parity | Agreed helpers, native behavior/negative tests, handwritten coverage and package consumers pass |
| Documentation and versions | Correct outgoing snapshot, coordinated version preparation, full native docs/examples/site integration and guards pass |
| Release integration | Publisher dry run, coordinator/version guards and account setup verified; activation only after the complete train is ready |
Rebase on current master and rerun affected gates when the shared contract or
release target changes. Perform a final audit against the original inventory;
record remaining work rather than calling a partial stage full parity.
Copy this completion record into the final PR or implementation plan:
## Baseline and design
- Baseline commit; spec version; proposed coordinated release:
- Native API prototype and reviewed naming/default/error decisions:
- Runtime/platform/dependency policy:
- Parity inventory, helper decisions and intentional differences:
## Completion evidence
- [ ] All model types/fields/roles and contextual rules implemented natively.
- [ ] Every valid/invalid ID, scalar limit, vocabulary and capability enforced.
- [ ] Independent export/helper coverage and empty release skip list.
- [ ] Native behavior, negative type/error, generation and quality gates pass.
- [ ] Minimum/current toolchains and supported platform matrix pass.
- [ ] Standalone artifact and external consumer checks pass.
- [ ] Public comments, READMEs, guides, runnable examples and API reference complete.
- [ ] Every language enumeration reviewed, including README reference/changelog/layout lists.
- [ ] Installation and quickstart accordions render collapsed for every language without losing examples.
- [ ] Registry/CI badges and language colours verified; download-source activation and public total documented.
- [ ] Every separately resolved example/integration project's own-package lock entry guarded.
- [ ] Site language/search/version/navigation checks pass; history remains accurate.
- [ ] CI filters, stable required checks and dependency maintenance configured.
- [ ] All version/tag/changelog/publisher guards updated and tested.
- [ ] Registry ownership, environment and authentication setup verified.
- [ ] First-publish verification and partial-failure recovery documented.
## Results and status
- Commands, toolchain versions, coverage scope/results and CI run links:
- Package/consumer and docs review evidence:
- Outstanding work or unavailable checks:
- Release status (prepared, authorized, published and verified):
Lessons verified while adding Rust
-
Keep a complete implementation distinct from registry activation. Record the published release set separately from the source tree; Rust's train leaves publication disabled and all release/account prerequisites explicit.
-
For Cargo, test the extracted
.crateitself, then a fresh project depending on that extracted directory. A path dependency on the checkout is not package evidence. Keep monorepo-dependent conformance tools outside the archive. -
Test minimum and current compilers, fresh dependency resolution, declared lower dependency bounds, and consumer-enabled dependency features. A workspace lock can hide downstream compatibility problems.
-
Exercise adversarial application JSON and numeric literals at every ingress boundary. Keep application metadata outside modeled Block Kit traversals. Assert success explicitly for valid edits and test conditional field dependencies with both accepted and rejected combinations.
-
Audit associated constants, enum variants, public fields and methods as well as export names. Document handwritten conveniences alongside generated values.
-
A language-content guard must check every supported language, including the original one. This audit found an empty Python component panel and a Ruby component example that instead demonstrated a plan block; both were corrected.
-
Compile the actual README/MDX snippets and compare block examples to their JSON tabs. Keep names/metadata tied to generation, but derive handwritten API docs from native comments or maintained descriptions with an exact export check.
-
Selecting a language unavailable in an older snapshot must lead to supported current docs. Test stored preferences and direct historical URLs as well as clicking the language selector. A new menu item alone does not establish parity.
-
Preserve the outgoing snapshot before touching current language panels or package versions. Record its source commit, language set and tree digest. Leave published installation examples honest while the next version is unreleased.
-
Wire required CI checks only after their stable names exist on the default branch. Inspect actual steps and runner metadata; a green skipped job proves neither language tests nor platform support ran.
-
Update each package’s own lockfile entry when advancing coordinated versions (including Ruby’s
Gemfile.lockand both Rust Cargo workspaces). Include each separately resolved integration/example project in preflight and prove stale entries are rejected. Run existing-language snippet harnesses as well as the new language’s checks; shared guide headings can affect extraction. -
Test release preflight with isolated Git refs and fake registry responses, then run the native package publish dry run without credentials. Cover disabled activation, missing/partial tags, dated notes, dirty or unmerged commits, duplicate versions and registry errors. Keep source activation unchanged.
-
If Cargo republishes from source, pin the packaging toolchain and compare its reproduced archive to the tested artifact before authentication. A successful archive download alone does not prove Cargo will publish those contents.
Lessons verified while adding PHP
- Audit competing libraries and their real transport behavior before picking an integration. JoliCode accepts nested Block Kit JSON as form strings; Laravel's template boundary is blocks-only and converts empty objects to arrays. Test the boundary and reject lossy conversions rather than claiming full payload parity.
- Treat named parameter spelling as public API. Use reflection to discover classes, properties, enums, signatures and defaults independently of generated metadata. Collection PHPDoc and positive/negative static consumers complement native types.
- Preserve empty objects, explicit false/null, signed 64-bit numbers and Unicode limits. Check cycles/depth before recursively decoding untrusted arrays.
- Test an actual archived package in a new Composer consumer. Resolve executable
launchers through PATH on Windows, resolve filesystem aliases on macOS, and compare
parsed JSON inventories rather than line-ending-sensitive text. Use explicit
\nescapes where fixture text has semantically significant LF characters. - Packagist can index the existing monorepo through a root Composer descriptor whose autoload paths point into the language directory. Generate it from the development manifest and reject drift. PHP verifies native VCS discovery, conventional tags, trimmed archives and stable/development source installs.
- For a custom release archive, make the tested bytes available before exposing
the conventional Composer version tag. PHP keeps its
php/vX.Y.Zworkflow tag, uploads and verifies its ZIP, then addsvX.Y.Zat the same source commit. Check retry identity for both assets and tags, and document--prefer-sourcefor unreleased development commits without archives. Gate activation on the existing monorepo's Packagist registration, update hook and protected environment. - Keep first-release docs explicit about availability. Use a neutral preparation badge until publication, then activate the registry badge and download provider. Historical snapshots must retain their actual language set.
- Execute all literal block/cookbook examples and compare block output with the separately documented JSON. Reflect handwritten helper comments as well as generated constructors into the reference; API member tables need defaults, collection types and error boundaries.
Lessons verified while adding Elixir
- Audit ignore rules with
git check-ignoreand inspectgit ls-filesin every nested consumer project. A rootlib/ignore can hide native sources even when local tests pass. Validate a fresh checkout and the actual registry archive. - Test required struct fields through construction behavior. Compiler metadata formats may change between supported releases without changing runtime behavior.
- Test adding the library to an application with already compiled dependencies. Jason's optional Decimal decoder is selected at compile time; install-time coexistence alone does not prove checked parsing works. Document and test the exact rebuild command in each environment and consolidated release.
- Read the published client artifact, not only upstream source. A helper that sends form data may flatten or mishandle nested JSON; exercise real HTTP bytes against a local server with the complete payload and failure responses.
- Native API references must account for default-argument arities, struct fields, typespecs and explicit hidden compiler/internal functions. Compare compiled exports against an independent inventory and rendered reference anchors.
- Preserve outgoing docs before adding current panels. Execute literal README and guide code, compare every block to its JSON tab, and verify older snapshots retain their original language set. Use a pending package badge before the registry has a release; activate the live version badge and download source only after publication is verified.
- A coordinated version bump must update version constants in independent API inventories as well as package manifests, integration requirements and locks.
- Register stable aggregate checks in branch protection only after those names
exist on the default branch, preserving the existing required-check set.
Elixir adds
Elixir native checks,Elixir quality and package,Elixir Erlang compatibility,Elixir sendingandElixir release checks. Read protection back and verify actual GitHub-hosted runner metadata; do not weaken existing protection to merge the new implementation.