Skip to main content

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:

InventorySource and required evidence
Types, fields and rolesEvery type, interface, enum and field kind in spec/model.json; native names/types, parent membership, requiredness, defaults and coercions
Wire contractEvery valid fixture in spec/manifest.json, constructed through the public API
ValidationEvery invalid case in spec/fixtures/invalid/manifest.json; every scalar leaf in spec/limits.json; vocabulary equality with spec/vocabulary.json
Public coverageEvery capability in spec/coverage.json, plus independent discovery of public JSON-producing symbols and justified exclusions
Supporting APIs and helpersStyles, text conveniences, composition helpers, inspection/editing, JSON integration and any supported Slack SDK adapter
User and maintainer experienceInstallation, 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:

DecisionQuestions to settle
Package and support policyPackage name/namespace ownership, minimum compiler/runtime, supported platforms, dependency policy, and how raising the minimum is treated
ConstructionConstructors, builders, keyword arguments or literals; required versus optional fields; text and collection conveniences; method chaining where idiomatic
Value semanticsOwnership/mutation, equality, getters, copying and safe editing of nested values; whether invalid intermediate builders can escape
Type systemConcrete values, parent-role types, enums, reserved-word/name collisions, aliases and discoverable public exports
Future compatibilityHow new enum variants, fields and role members affect callers, including exhaustive matches and source compatibility
JSON and errorsNative 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 diff can 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.

  1. 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.
  2. 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-required or invalid-usage. Isolate the intended failure and assert representative complete error paths. Error classes and prose remain native.
  3. 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.
  4. Assert every scalar limit leaf has an invalid case with the matching constraint path; compare generated/bundled limits and vocabulary to their registries. Add positive boundary tests as well as the invalid corpus.
  5. 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.
  6. Assert the declared spec version and keep conformance/skiplist.txt empty. 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:

LanguageRegistryHighlight
PythonPyPI3776AB
TypeScript / JavaScriptnpm3178C6
GoGo module proxy00ACD7
JavaMaven CentralE76F00
C#NuGet512BD4
RubyRubyGemsCC342D
Rustcrates.ioCE422B
PHPPackagist777BB4
ElixirHex8847B9

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​

AreaFiles to review and extend
Language identity, routing and contentdocs/src/components/LanguageContext/index.tsx, LanguageContent/index.tsx, LanguageSelector/index.tsx in the same components directory
Version availability and labelsdocs/src/components/VersionSelector/index.tsx, docs/src/theme/DocVersionBadge/index.tsx; new versions infer languages from their reference pages; preserve historical availability
Navigationdocs/src/theme/DocSidebar/index.tsx, DocBreadcrumbs/index.tsx, DocItem/Paginator/index.tsx, TOCItems/index.tsx in the same theme directory
Searchdocs/plugins/language-search-indexes.cjs, docs/src/theme/SearchBar/, docs/src/theme/SearchPage/
Site configuration and reference generationdocs/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 guardsdocs/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 instructionsRoot 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:

  1. Relevant path filters and version extraction/equality checks.
  2. Changelog presence/date checks and readiness conditions.
  3. The complete tag list, existing-tag verification and atomic tag creation.
  4. 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:

StageExit evidence
API designNative consumer prototype, parity inventory, naming/default/error decisions and dependency/support policy reviewed
FoundationPackage scaffold, generation, local checks and stable CI; prototype works as an installed package; publication disabled
Complete contractAll types/roles/rules and conformance registries pass, independent export coverage, empty skip list
Helper and quality parityAgreed helpers, native behavior/negative tests, handwritten coverage and package consumers pass
Documentation and versionsCorrect outgoing snapshot, coordinated version preparation, full native docs/examples/site integration and guards pass
Release integrationPublisher 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 .crate itself, 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.lock and 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 \n escapes 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.Z workflow tag, uploads and verifies its ZIP, then adds vX.Y.Z at the same source commit. Check retry identity for both assets and tags, and document --prefer-source for 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-ignore and inspect git ls-files in every nested consumer project. A root lib/ 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 sending and Elixir release checks. Read protection back and verify actual GitHub-hosted runner metadata; do not weaken existing protection to merge the new implementation.