Skip to main content

Roadmap

What needs to happen for XTL 1.0 (spec) and xl3 1.0 (reference implementation).

The current version is 0.8.0 (npm) targeting XTL 0.1 (draft). Breaking changes are still possible during 0.x. The 1.0 cut is gated on the items below, not on a calendar date.

Deep version planning lives in docs/internal/blueprint-to-1.0.md — gap analysis, philosophy boundary (xl3 ≠ JXLS), per-version step plan. This document is the elevator pitch; the blueprint is the rationale.

Single source of truth for 1.0 gates is the table below. When this file and the blueprint conflict, this table wins; the blueprint is updated to match.

What 1.0 means for xl3

The 1.0 target is operator-readable trust: a spec that doesn't shift, a reference impl that doesn't surprise, and a surface small enough that an operator can review a template without reading code. It is not about feature completeness vs JXLS — xl3 intentionally ships a smaller surface (ADR-0043 + ADR-0048). The intended audience is Korean operations teams that manage many customer-specific invoice formats (거래명세서, 정산서, 발주서); the engine generalizes beyond this niche, but the niche is the wedge.

1.0 gate table (single source of truth)

Each gate has an owner, the artifact that closes it, the pass-fail criterion, a fallback if the gate is unreachable, and the target milestone. Per-version step plan below references these gates by ID.

IDGateOwnerArtifactPass criterionFallbackTarget
G1Conformance corpus ≥ 140maintainerconformance/fixtures/ls conformance/fixtures/ | wc -l ≥ 140DONE (156 fixtures; ADR-0066 added 141-145 mid-0.7.x; ADRs 0067-0069 added 146-155 in 0.8.0; 156 (#49 native value preservation) + 157 (#51 grouped side cells) added post-0.8.1; ADRs 0051-0065 reserved further numbers for 0.7.1)
G2Stage 2 OOXML canonicalization spec'dmaintainerADR-0006 + canonicalizer in src/covered by fixtures 024-027, 093 + ADR-0006 amendmentDONE
G3Error code catalog frozenmaintainersrc/__tests__/error-codes.test.ts snapshotcatalog snapshot unchanged for 30 days0.9-rc (clock reset 2026-05-23 by 0.8.0's 4 new codes: xl3/expression/bracket-outside-block, xl3/block/overlap, xl3/block/empty-table, xl3/directive/orphan — earliest tick: 2026-06-22 if no further changes)
G4JXLS boundary publishedmaintainerADR-0048file exists, references PORTERS_GUIDEDONE
G5Deferred-impl ADRs landedmaintainerADR-0038 impl ✅ (2026-05-18) + ADR-0040 PE implADR-0038 portion shipped (fixtures 132-135); ADR-0040 CF/DV range-extension still pending0.6 (partial) / 0.7.1
G6Public API surface frozenmaintainersrc/__tests__/api-surface.test.ts snapshotsnapshot unchanged for 30 days0.9-rc
G7JSDoc examples on @stable exportsmaintainerTypeDoc outputevery @stable symbol has @example block0.8
G8Performance characterizedmaintainerscripts/BENCH.md1k/10k/100k row × 5/10/20 col matrix + memory-ceiling + parse/eval/write split published0.7.1
G9Perf regression fixturesmaintainerconformance corpus≥ 2 large fixtures with ratio-based assertion0.7.1
G10Cross-browser smokemaintainerci.ymlSafari + Firefox bundle-load + 1 convert() per run0.7.1
G11Stage 2 in CImaintainerci.ymlnpm run conformance:stage2 runs on every PR0.7.1
G12Undecided behavior pinned (pivot/sparkline/ListObject/page break)maintainerconformance fixtures + ADR per itemeach: fixture pinning current behavior OR ADR explicitly deferring to 1.xdefer to 1.1 with ADR0.7.1 / 0.8
G13Second-language impl validationexternal (xl3-py)conformance/reports/*.jsonxl3-py passes ≥ 80% Stage 1 OR ≥ 80% Stage 2, OR documented 50% skeleton in another language (Rust/Go/Java) within 12 months of all other gates closingaccept single-impl 1.0 via public ADR amending GOVERNANCE0.7.x–0.8.x
G14External-contributor ADRexternalspec/decisions/NNNN-*.md≥ 1 ADR with non-maintainer as Author (≥ 60% of Context/Decision sections by line count)18-month time-box, then: ≥ 2 external-authored cookbook recipes OR ≥ 5 external-authored conformance fixtures0.8
G15Production reference caseexternal (with maintainer help)IMPLEMENTATIONS.md "Production users" row≥ 1 named user, satisfied by EITHER (a) external company with permission to list, OR (b) the maintainer's own employer running xl3 in scheduled production with a public case study0.8.x — in progress via maintainer's-employer production deployment (template setup complete 2026-05-24; live usage starts week of 2026-05-26); G15 ticks when the case study is published
G16Maintainer set wideningmaintainerGOVERNANCE.md≥ 2 people with accept/reject rights for ADRs and impl PRsexplicit accept of single-maintainer 1.0 governance shape via amendment to GOVERNANCE0.8
G17Korean cookbook i18n completemaintainerwebsite/i18n/ko/.../guides/all cookbook recipes have Korean translationDONE (0.6)
G18Production use case in READMEmaintainerREADME.mdreplaces "alpha" status with concrete production reference (tied to G15)1.0 (with G15)
G19Migration guide 0.x → 1.0maintainerdocs/migration-0.x-to-1.0.mddocuments every behavior change or confirms additive-onlydowngrade to CHANGELOG note if confirmed additive-only0.8
G20SECURITY.md + threat modelmaintainerSECURITY.md + spec amendmentdocs zip-bomb / oversized workbook / formula-execution stance + limits API0.7.1
G21Hard limits documented (no streaming until 1.1)maintainerspec/evaluation.mdrow / memory hard limit values + AbortSignal API documented0.7.1
G22API surface — internal model types separatedmaintainersrc/index.ts exports + STABILITY.mdonly convert/preview/analyze + stable interfaces marked @stable; model/parser types marked @experimental or moved to xl3/internalDONE (0.6)
G23RC soakmaintainergit tagsRC published; ≥ 21-day soak (extended from 7 day per review feedback); 0 critical issues0.9-rc
G24"Stable quarter" post-checklistmaintainerrelease calendar90-day window after the FINAL gate above ticks ✅; no breaking spec/API/error-code change during the windowbreaking change → restart clockbetween final-gate-tick and 1.0 cut

Definitions (testable)

  • External contributor (G14): not in GOVERNANCE.md maintainer set AND not in Co-authored-by history of merged ADR commits at PR open time. Drive-by typo edits do not count; named Author in ADR front-matter; authored ≥ 60% of Context/Decision sections by line count.
  • Breaking change (G24, G23): any change to (a) public API surface snapshot, (b) error code catalog (rename/removal/repurpose), (c) ADR acceptedrejected or contradicting status flip. Patch releases and additive ADRs do NOT reset the quarter clock.
  • Critical bug fix (G23 RC exception): (a) silent data loss in convert(), (b) error code catalog inconsistency between docs and runtime, OR (c) an accepted ADR's MUST that cannot be implemented as written. Maintainer cites which of (a)/(b)/(c) in the PR.
  • Data-loss test (G24 testable form): corpus has a dedicated data-loss/ fixture group (≥ 8 fixtures) exercising silent- stringify, numFmt drop, formula rewrite, and date round-trip paths; all pass on the reference impl.
  • Quarter clock start (G24 vs G23): the 90-day quarter starts on the day the LAST gate ticks ✅. RC publication does NOT start the clock; the clock must have started BEFORE RC publication. If a breaking change happens during RC soak, both the soak (G23) and the quarter (G24) reset.

Per-version step plan

Gate-based, not date-based. Calendar estimates have been removed — each milestone closes when its listed gates close.

0.6.0 — Deferred-impl, narrow scope

Theme: close the highest-impact deferred-impl gate cleanly.

Gates closed: G5 (@group/@subtotal impl only — the rest of ADR-0040 PE moves to 0.6.1), G17 (Korean cookbook 16/17 missing translations), G22 (API surface cleanup before @group exposes new internal types).

The previous "single 0.6.0 with everything" plan was scoped too ambitiously per the engineering-feasibility review. ADR-0038 impl alone is a full pipeline insertion (new directive, group-boundary state machine, transform-pass partition, renderer rewrite, group-scoped aggregate eval). Splitting 0.6.0 keeps the milestone shippable.

0.6.1 — Rest of deferred-impl (planned, not yet shipped)

Gates closed: G5 completion (ADR-0040 PE: CF/DV sqref extension), pivot/page-break behavior fixtures toward G12.

Status as of 0.7.0 shipping: this milestone was bypassed by the spec-audit batch (0.7.0). G5/G12 work folded into 0.7.1.

0.7.0 — Spec-audit batch (shipped 2026-05-22)

Theme: close 17 syntactic-conflict gaps surfaced by a deep audit of the lexer, cell classification, directive composition, aggregate args, and reserved-sheet semantics. Unplanned in the original gate table; the perf/CI/limits work originally tagged 0.7.0 moves to 0.7.1.

Shipped artifacts:

  • 15 new ADRs (0051–0065) + amendments to ADR-0021 (group-order catalog entry) and ADR-0041 (header-cell multi-line normalization).
  • 4 new error codes — xl3/parser/unbalanced-literal, xl3/lists/invalid-use, xl3/eval/bad-aggregate-arg, xl3/expression/unknown-name.
  • Grammar additions: positive_integer, group_directive, subtotal_directive, aggregate_call, lexical-disambiguation note.
  • src/directive-parser.ts strictness for leading-zero integers.
  • Two-pass parallel review (claude-general + codex); all CRITICAL/HIGH findings closed before tag.

Gate impact:

  • G1 — 139 fixtures today. The 0.7.0 ADRs reserved fixture numbers 141–187; impl is pending. G1 closes when those fixtures land in 0.7.1.
  • G3 — 30-day error-code-catalog clock reset on 2026-05-22 by the 4 new codes.
  • G6 — no public API surface change; G6 clock unaffected.

0.7.1 — Performance + external validation begins (relabeled from old 0.7.0)

Gates closed: G5 completion (ADR-0040 CF/DV sqref range), G8 (perf benchmarks), G9 (perf regression fixtures), G10 (cross-browser), G11 (Stage 2 in CI), G20 (SECURITY.md draft + threat model), G21 (hard limits + AbortSignal docs).

Also closes the G1 ≥ 140 fixtures floor by landing fixtures 141–187 reserved by the 0.7.0 ADRs.

Progress toward: G12 (undecided behavior pinning), G13 (xl3-py).

Relabel: alphabeta after G8 publishes and xl3-py reaches ≥ 50% Stage 1.

0.8.0 — Data-block design overhaul (shipped 2026-05-23)

Theme: unplanned in the original gate table. A late-0.7.x audit of data-block expansion surfaced two structural bugs (#46 duplicate shared-formula owners, #47 stale references in displaced side cells) that needed a column-scoped rewrite before further feature work. The original "0.8.0 = sociological gates" plan moved to 0.8.x patches.

Shipped artifacts:

  • ADR-0066 — data block is now column-scoped: bounding box of {{...}} markers extended through contiguous non-empty cells. Cells outside that range keep their row positions when the block expands. Closes #46 / #47 by construction.
  • ADR-0067 — explicit @block directive in three forms (bare, A:D column range, A2:D7 rectangle).
  • ADR-0068 — strict multi-block detection on opt-in sheets: every [Column] marker must sit inside some block; block rectangles cannot overlap.
  • ADR-0069 — per-block directive scoping by proximity: @filter/@sort/@top/@source/@join/@group/@repeat attach to the closest data block whose column range overlaps the directive's column.
  • 4 new error codes: xl3/expression/bracket-outside-block, xl3/block/overlap, xl3/block/empty-table, xl3/directive/orphan.
  • Conformance fixtures 146-155 (multi-block, side-by-side blocks with different sources, vertically stacked blocks, per-block filter, per-block ROW() scoping, the three new error paths).

Gate impact:

  • G1 — corpus 139 → 154 fixtures. Floor cleared again.
  • G3 — 30-day error-code-catalog clock reset 2026-05-23 by the 4 new codes. Earliest tick: 2026-06-22, contingent on no further code additions/renames during 0.8.x patches.

0.8.x — Sociological gates (in flight)

Gates closed: G14 (external ADR), G15 (production case; in progress via maintainer's-employer deployment), G16 (maintainer widening or explicit single-maintainer acceptance), G19 (migration guide), G20 completion.

The plan is to ship 0.8.x patches during the recruitment period rather than wait silently. G3 quarter discipline: while sociological gates run, error code additions and renames are deferred — any addition resets the G3 clock and pushes the 0.9-rc target. Only critical-bug-fix codes (per the definition in this file) may be added during the 0.8.x window.

0.9.0-rc.x — Pre-1.0 freeze

Gates closed: G3, G6, G7, G23 (≥ 21-day RC soak).

After G23 starts, the quarter clock for G24 begins (it must have ticked while G3/G6/G7/etc. were closing — see definitions above).

1.0.0 — Final cut

Gate closed: G24 (90-day quarter complete after last gate ticked).

Recruitment and outreach

Sociological gates (G13/G14/G15/G16) require people, not code. The project has two distinct recruitment surfaces:

Korean operations audience (G15, future cookbook contributors)

Channels: Korean developer communities (Naver Café, Kakao 오픈톡, LinkedIn KR), internal company / vendor template-author surveys. Each minor release publishes a Korean-language post tied to the release moment (0.6 = @group/@subtotal demo for invoice subtotal patterns; 0.7 = perf numbers; 0.8 = case study).

English OSS audience (G13, G14)

Channels: HN, lobste.rs, r/excel, conference CFPs (JSConf, EuroPython for xl3-py). Each major moment ships with one specific external artifact:

  • 0.7.0 release: "Show HN: xl3 0.7 — 100k-row Excel template engine"
  • 0.8.0 release: case study + xl3-py conformance dashboard
  • 1.0.0 release: spec + multi-impl validation

Non-goals for 1.0

These are intentionally deferred. Each has an ADR explaining why:

  • Date arithmetic beyond Y/M/D/EOMONTH/EDATE/DATEDIF — the rest of the family deferred per ADR-0019 amendment.
  • Locale-aware string collationADR-0020.
  • Multi-join, left-join, multi-row matchesADR-0014 out-of-scope section.
  • XLOOKUP wildcard / approximate / reverse searchADR-0013 out-of-scope section.
  • Dynamic image insertionADR-0037.
  • Runtime cell mutationADR-0042.
  • Functions rejected per ADR-0043 gate — math expansion, type tests (except ISBLANK per ADR-0047), NOW / WEEKDAY etc., conditional aggregates, TEXT() format-token expansion. See ADR-0045.
  • Streaming output / SXSSF analog. Deferred to 1.1+. At 1.0, hard memory/row limits are documented (G21) instead.
  • Template compile caching API. Deferred to 1.1+.
  • PDF / HTML output. Out of scope; xl3 is xlsx-in, xlsx-out.
  • Cross-writer Stage 2 fixtures beyond 093ADR-0006 amendment.

These remain candidates for XTL 1.1, 1.2, 1.x based on demand.

How to help close items

ItemHow to help
G13 second-impl ≥ 80%Contribute to xl3-py, or start a new port (Rust, Java, Go). See PORTERS_GUIDE.md.
G14 external ADRPick a deferred item (pivot table preservation, page-break, ADR-0045 carved-out function), draft an ADR in spec/decisions/. See GOVERNANCE.md "How changes enter the project." A few "starter ADR stubs" are available as good-first-ADR issues on GitHub.
G15 production caseUse xl3 internally, share what worked / didn't. Drop a row in IMPLEMENTATIONS.md if appropriate. The maintainer's own employer (Snack24h) qualifies if it ships a public case study.
G17 Korean cookbook 16+17 i18nTranslate the two newest recipes (the rest are done).
G8 benchmarksRun npm run bench on representative templates, share results.
G10 cross-browserAdd Safari + Firefox to the bundle smoke test.
Function re-proposalIf you need a function rejected per ADR-0045, file the Function re-proposal issue template.

How this roadmap evolves

This document is the public elevator pitch + the gate table is the single source of truth. The deeper docs/internal/blueprint-to-1.0.md carries the gap analysis, philosophy boundary, and per-version rationale. As gates tick, both documents update. As new gaps surface, both add them.

Cuts and additions to the 1.0 gate table are discussed via the same ADR/issue process as everything else.