Skip to content

Publish and validate the autodiff extension contract - #121

Open
AlekseiChirkovVention wants to merge 6 commits into
feat/traced-parameter-updatesfrom
feat/autodiff-extension-contract
Open

Publish and validate the autodiff extension contract#121
AlekseiChirkovVention wants to merge 6 commits into
feat/traced-parameter-updatesfrom
feat/autodiff-extension-contract

Conversation

@AlekseiChirkovVention

Copy link
Copy Markdown
Collaborator

Goal

Publish, document, and validate the generic client contract added by the three branches beneath this one, so a downstream consumer can adopt it without private graph access.

What changed

Exports. The structured dependency analysis, the program lowering seam, and traced parameter updates are now reachable from tinychain.autodiff. Resolution is lazy through the module-level attribute hook the package already uses for its artifact, route, callsite, and compiler surfaces, so importing the package does not pull in the new modules. No production module beneath this branch was modified.

Documentation. A new README section describes the boundary a consumer builds against: what the framework owns in analysis and traversal, what a consumer supplies as handlers and fusion policy, the ordering and provenance guarantees, the error categories and when each is raised, and how a parameter update is authored as ordinary Tensor code. The surface is marked experimental and carries no post-0.x stability guarantee.

A generic consumer. test_autodiff_fake_consumer.py lowers two concrete operators and one supported fusion into its own representation, reaching everything through public names and never a private submodule, with no target-specific concept anywhere in it.

A static dependency guard. A test asserts that no module in the autodiff package imports a target-specific dependency.

What the guards do and do not catch

This is stated here because it is stated in the code and the documentation, and the three should agree.

The check that updates are authored only from public Tensor operations catches literal construction and any name bound in the module, including an import alias. It does not catch construction living in another module reached at call time, nor a function-local aliased import. It is aimed at a careless direct construction, not at deliberate circumvention by someone with commit access.

The consumer test proves that a consumer with no private access and no target-specific concept can use the seam through public names. It is a capability demonstration, not a regression guard: it would not detect the framework later growing consumer-specific behaviour on the lowering path.

The dependency guard reads import statements, so it rejects both static forms of a target-specific import and would miss one resolved at run time.

Each of those limits was found by testing the guard against the evasion rather than by reading it, and the documentation now predicts every outcome.

Compatibility

Additive. Across the whole feature range this branch completes, the only source files touched are the package's __init__.py and the four new or extended autodiff modules. No existing serialized payload gained a field, and the derivative program's serialized form and the execution scheduler are untouched.

Test evidence

Test level: unit.

Tests were written first: a8b8299 is a test-only commit whose export tests failed on the missing package-root names and whose consumer module failed at collection — the missing exports being precisely what was under test. Three tests passed on arrival and are recorded as pinning existing behaviour rather than driving the change.

Focused: 21 passed. Full non-integration suite: 738 passed, 1 skipped, with the 4 failures that pre-date this work unchanged — none vanished, no new one.

Stack position

Stack position 4 of 4 — based on feat/traced-parameter-updates.

Cannot merge into main before feat/derivative-dependency-analysis, feat/extensible-program-lowering, and feat/traced-parameter-updates.

Notes for review

Three small documentation-accuracy improvements are known and deliberately not folded in here: the lowering paragraph lists two of the error categories lowering can surface rather than all of them, the compatibility paragraph does not mention that the error-category constant gained five members, and one test docstring cites a specification section by number. They are tracked for the task-level pass rather than reopening a reviewed branch.

Assert that the dependency analysis, program lowering, and traced update
surfaces are reachable from the package root, and that an unknown
attribute still fails rather than resolving.

Add a consumer that lowers two concrete operators and one supported
fusion into its own representation, reaching everything it needs through
the package root and never a private submodule, so the seam is shown to
be generic rather than merely available.

Add a static guard asserting no module in the autodiff package imports a
target-specific dependency.

Strengthen the check that updates are authored only from public Tensor
operations. Scanning source text for constructor calls is evadable by an
import alias or by moving construction into a helper reached through
attribute access; neither can avoid binding the name in the module
namespace, so the namespace is now checked directly.
The check asserted that every from-import in the consumer module named
the public autodiff package, which also caught future statements and
standard library imports and so asserted more than it meant. Restrict it
to imports of this project and keep the exact-match rule, so reaching
into a private submodule still fails it.

Also drop internal planning identifiers from the traced-update test
module docstring, naming the analysis and lowering seam directly. A
reader has no access to the artifacts those identifiers referred to.
Publish the structured dependency analysis, the program lowering seam,
and traced parameter updates from the package root, so a consumer reaches
the whole contract without importing a private submodule.

The names resolve lazily through the module-level attribute hook already
used for the artifact, route, callsite, and compiler surfaces, keeping
the import cost of the package unchanged for callers that do not use
them.
Describe the boundary a consumer builds against: what the framework owns
in dependency analysis and traversal, what a consumer supplies as
handlers and fusion policy, the ordering and provenance guarantees, the
error categories and when each is raised, and how a parameter update is
authored as ordinary Tensor code.

State the compatibility position plainly -- the surface is additive and
changes no existing payload or serialized form -- and mark it
experimental rather than promising stability it has not earned yet.

Say what the dependency guard does and does not check: it rejects a
target-specific import mechanically, while the absence of an encrypted
layout assumption remains a review claim, since a generic encrypted
encoding concept legitimately exists elsewhere in the client and a
keyword ban would fail on it.
Three guards claimed more than they deliver, and a consumer would have
built against those claims.

The check that updates are authored only from public Tensor operations
catches literal construction and any name bound in the module, including
an import alias. It does not catch construction living in another module
reached at call time, nor a function-local aliased import, because
neither binds the type in the module namespace. Say so, and say that the
guard is aimed at a careless direct construction rather than at
deliberate circumvention.

The consumer test proves that a consumer with no private access and no
target-specific concept can lower two concrete operators and one fusion
into its own representation through public names. It cannot detect the
framework later growing consumer-specific behaviour on the lowering
path, which it claimed. Drop that claim.

The dependency guard reads import statements, so it rejects both static
forms of a target-specific import and misses one resolved at run time.

Behaviour is unchanged; only the descriptions are.
The traced-update section still said the guard catches a helper reached
through attribute access, which it does not, and which the section
describing that boundary already said plainly. A reader looking the guard
up arrives at this paragraph first, so it was the more likely of the two
to be believed.

State only that an import alias is caught, and point at the boundary
section rather than restating it, so the two cannot disagree again.
@AlekseiChirkovVention
AlekseiChirkovVention marked this pull request as ready for review August 21, 2026 09:53
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.

3 participants