Skip to content

fix(cli): make the release surface and its tutorial pages executable - #147

Merged
zhen8838 merged 17 commits into
tile-ai:mainfrom
zhen8838:fix/cli-release-surface
Sep 1, 2026
Merged

fix(cli): make the release surface and its tutorial pages executable#147
zhen8838 merged 17 commits into
tile-ai:mainfrom
zhen8838:fix/cli-release-surface

Conversation

@zhen8838

@zhen8838 zhen8838 commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Why

What

  • Command surface. The hand-written overview and _Parser are gone, so --help
    is argparse's and the introduction has one copy; --version is added, usage errors
    print usage before the error, and models --source prints a path on stdout with
    the provenance on stderr. An unknown document name is refused the same way at
    every kind.
  • Cost analysis. memory measured residency twice and disagreed with itself by
    the ratio between a value's own shard factor and the topology's declared unit
    count, so a program that does not fit was answered rather than refused. It now
    reads the same local_type_of projection traffic reads, and refusal is per value
    again. What a peak should claim across a loop is issue feat(analysis): settle what peak-footprint claims, and make the claim true #146.
  • Tutorial mechanism. Pages are notebooks rendered by executing their own blocks,
    the first being the extraction command. migrate and optimize are new and share
    one example; migrate shows check refusing an authored step, then passing after
    a one-line fix -- the same command both times. index is one figure and
    {{fixture:}} goes. One installed test does what a page tells a reader to do and
    requires each block's output to be the page's own.
  • Plan tooling. Delivered sections must state their change as a diff.

Contract

  • docs/spec/cli.md: naming no command prints that level's --help byte for byte,
    exit 0; a usage error prints usage before the error; --version and the project
    summary come from distribution metadata; --source's first line MUST be the path
    alone with provenance on stderr; an unknown document name is refused uniformly
    across kinds; a page with programs MUST open with its extraction command and a
    rendered page MUST equal executing its own blocks; the page list gains authoring.
  • docs/spec/analysis.md is unchanged, because both analysis changes are
    restorations, not new behaviour: the specification already required that one
    value exceeding a level's capacity raise AnalysisError, and that
    ValueLifetime.bytes project the Type through the splits at or coarser than the
    level's owner. refactor(analysis): unify analysis structure and fix cross-scope and symbolic-offset analysis #126 changed the code, not the spec.

Risk

  • Issue feat(analysis): settle what peak-footprint claims, and make the claim true #146 carries what peak-footprint claims: the largest sum of simultaneously
    live bytes, a lower bound on an allocation peak. Nothing allocates a buffer, so
    the real peak needs a codegen allocation contract. analysis.md:575-577 also
    requires an advisory when an aggregate peak exceeds an implicit cache capacity;
    analysis/ has none.
  • Capacity refusal is per value, so two values that each fit and are live at once
    pass. Stage2_Sharded at ctx_len=1816 is that case; a test pins the cost.

Footprint took the whole tensor's bytes and divided by the topology's unit
count, while traffic asked the local projection what one unit holds; the two
disagreed in the same report by the ratio between a value's shard factor and
that count. Both now read `local_type_of`, and the divisor is gone.

Lifetimes were built on one flat sequence because `replace` handed a loop body
its parent's list, so the nesting the Scope tree already records never reached
the peak. Values are now collected per scope and the peak is composed over that
tree: a scope's own worst point on top of what the enclosing scopes hold where
it sits, sequential scopes taking the greater rather than the sum, and a loop
counted once rather than once per trip.

Refusal returns to one value against its level's capacity, which is what
`docs/spec/analysis.md` states; the working-set sum refuses programs a shared
budget accepts.
The renderer had its own extraction of the programs a page teaches, and the
page carried a second one in a markdown cell that never ran. Nothing proved the
two produced the same bytes, and the filename was typed in three places that
derived from nothing. The page's first block is now the extraction command and
the renderer executes it: rendering happens in two passes, the first laying the
page down with no outputs where that command can read it, and prose and code
blocks are byte-identical across both.

A source block's tag carries the file it belongs to, so one notebook can write
several and a later block can overwrite an earlier one's. The renderer walks
`docs/tutorial/*.ipynb`, so a new page needs no edit here.

The guard is `test_a_reader_reproduces_the_page_from_the_page`: it does what the
page tells a reader to do, in an installation with no checkout to import, and
requires every block's output to be exactly what the page shows, in place. It
replaces an assertion that read the page for a string it never ran.
The two workflow pages were never executed. A reader following `optimize` hit
five errors in a row, and `migrate` spliced a declaration out of a shipped model
through a template mechanism only that one page used. Neither page ever ran
`check`, which is the product's central claim.

Both are notebooks now, rendered by executing their own blocks: one example
carried across them -- RMS-normalize a row, take a blockwise absolute maximum,
quantize to fp8. `migrate` builds the published side from the real
`transformers` class with the epsilon, block width and format the model's own
`config.json` states, authors the step with the scaling on the wrong side of the
bf16 landing, and shows `check` refusing it; a second extraction block writes the
one-line fix over the same file and the same command passes. `optimize` prices
the same step with a `@func` boundary in the middle and without, then checks a
runtime twin against the authored program.

`index` is one figure and three sentences. The `{{fixture:}}` mechanism has no
page left to serve and goes, with the assertions that watched it.
`--source` said where a checkout's file list came from by appending it to the
directory line, so standard output's first line stopped being a path. One line
cannot serve a reader and a caller at once: `sed -n '1p'` took the note along
with the path, and `awk 'NR==1 {print $1}'` breaks the moment the path has a
space in it.

The note moves to standard error, unchanged in condition: a checkout says it, an
installation says nothing. Standard output is now a path and one line per file in
either world, so reading the first line as a path is correct again and the
specification promises that rather than a field layout.
The extraction block took the first whitespace-separated field to get past a
note that is no longer there, and a path with a space in it would have broken
that. `sed -n '1p'` is the honest reading now, with standard error dropped
because a checkout says on it where its file list came from and an installation
says nothing: leaving it captured would put a line in the page that only one of
the two worlds produces.
The optimize page taught making a step fast and then showed a twin written in
torch, which is a second reference rather than a fast implementation. It is one
Triton kernel now, a row per program with the whole step fused into it, and it
holds bit-exactly: `equal` on the fp8 output reports 0 of 14336 mismatched, so
the row reduction lands on the same values the interpreter does.

`step` said nothing, so the kernel is `rms_norm_quant` across both pages, in the
file name, the module entry and the `@func`. The `@func`s gain the outer `cta`
mesh every other placed program in the tree has; it changes no measured number.

The index lists the pages it has and nothing else -- pointing at `spec` and
`check --help` is the tutorial's obligation, and the pages that teach a step are
where it lands, so that assertion moves onto them. The annotation-marker lint now
covers `docs/tutorial/` as well, with the interface-block rules left to
specification pages: a tutorial fence is a program that runs, where a decorator
and a module docstring are correct.
Two independent defects were fixed in one rewrite. The first is the one that
mattered: footprint took the whole tensor's bytes and divided by the topology's
unit count while traffic asked the local projection what one unit holds, so the
same report disagreed with itself by a value's shard factor, and a program that
does not fit was answered instead of refused. That fix is twelve lines, and it
stays.

The second built the lifetimes over the Scope tree so a loop's carried values
stay resident across it. It is correct — a flat sequence ends their intervals
before the loop begins — but it is about a hundred and fifty lines and it moves
two numbers in six programs, by 136 B each: `acc` plus `m` plus `l`, the online
softmax's carried state. Nothing downstream turns on 136 B against a 232 KB
capacity, and a change that size deserves its own title rather than a paragraph
in a release-surface change. It goes to the issue that owns what a peak claims,
where the measurement and the shape of the implementation are recorded.

So the lifetimes are flat again, the specification's three rows say so, and the
two stages with a loop report the smaller peak.
@zhen8838
zhen8838 merged commit c3881ce into tile-ai:main Sep 1, 2026
1 of 2 checks passed
@zhen8838
zhen8838 deleted the fix/cli-release-surface branch September 1, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant