_definition_changes() captures function/class source with ast.get_source_segment(source, statement). A FunctionDef, AsyncFunctionDef or ClassDef span begins at the definition, not at its preceding decorator lines, so the captured source omits decorators.
robot-docs/kernel-state.md currently says: “Decorators are included.” The implementation and that contract disagree.
Source checked #
- Initial study:
62226db9790f07f6b3cdfe49cd94a3fc5212b087 - Rechecked against public HEAD on November 10, 2026:
5f90049d9339a02a2c9ce6834ac3ec76d6039ccd; the helper and documentation are unchanged. priv/python/albedo_cells.py,_definition_changes, lines 1083–1122; the relevant call is line 1118.- Source-file SHA256 in both checked revisions:
8e5c6b77db736ed1247950a8bb0f8b38e1e7aca86c301e4eff7f11b889fd6288.
Minimal reproduction #
This executes only the two existing pure capture helpers from a local checkout, not the Albedo module, daemon or kernel:
import ast
from pathlib import Path
source = Path("priv/python/albedo_cells.py").read_text()
tree = ast.parse(source)
helpers = [
node for node in tree.body
if isinstance(node, ast.FunctionDef)
and node.name in {"_bound_names", "_definition_changes"}
]
assert len(helpers) == 2
scope = {"ast": ast}
exec(compile(ast.Module(body=helpers, type_ignores=[]), "capture-helpers", "exec"), scope)
cell = "@decorate\ndef sample():\n return 7\n"
changes = scope["_definition_changes"](cell, ast.parse(cell))
captured = next(row[2] for row in changes if "sample" in row[1])
print(captured)
assert "@decorate" in captured, "decorator source was lost"
Observed output, followed by the failing assertion:
def sample():
return 7
Reproduced using Python 3.12.3 and Python 3.13.16. The same tiny helper probe was repeated against the newer HEAD above.
Expected and possible consequence #
If the documented source-restoration contract is intended, retain the full definition including decorators. Replaying the current captured segment can replace a decorated binding with an undecorated one—for example, dropping a wrapper's behavior or a dataclass transformation—while successful name restoration alone may not expose that semantic change.
Scope: this report directly reproduces source-capture loss. I have not executed an end-to-end snapshot/restore test. It is not a claim that every restoration fails: dill-loaded values, failed replay and deferred-value paths can change particular outcomes.
Suggested regression coverage #
- Decorated synchronous and asynchronous functions, and decorated classes.
- Multiple/multiline decorators and top-level indentation/source-span handling.
- A restore test whose decorated and undecorated bindings behave differently, not merely a check that the name exists.
- Preserve the existing trust/side-effect model of definition replay; do not treat replay as a security sandbox.
A possible implementation direction is to take the source span from the first decorator's line (including its @) through the definition's end, rather than the definition node's default span. If decorator omission is intentional instead, document the limitation and avoid implying full definition fidelity.
fixed in https://tangled.org/okami.mom/albedo/commit/878fbd68!