Publish and validate the autodiff extension contract - #121
Open
AlekseiChirkovVention wants to merge 6 commits into
Open
Publish and validate the autodiff extension contract#121AlekseiChirkovVention wants to merge 6 commits into
AlekseiChirkovVention wants to merge 6 commits into
Conversation
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.
code-tc
approved these changes
Aug 20, 2026
AlekseiChirkovVention
marked this pull request as ready for review
August 21, 2026 09:53
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.pylowers 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__.pyand 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:
a8b8299is 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
mainbeforefeat/derivative-dependency-analysis,feat/extensible-program-lowering, andfeat/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.