Skip to content

somatize.foreseen

What a graph’s nodes will be called before anything runs, and what an edit did:

from somatize import foreseen
foreseen.names(g) # what each answer will be called
foreseen.unneeded(g, x, store=store) # what would not have to run at all
foreseen.changes(before, after) # what the edit did
foreseen.snapshot(g) # the same, kept for later

A name is a hash of the recipe and not of the data, so only the graph’s input is hashed by content and from there down they are hashes of hashes. The engine already makes this pass before its first node; asking for it on its own turns is my cache still good? into a millisecond instead of a run.

changes answers {node: [finding, ...]} — the shape somatize.health uses, and for the same reason: what happens to a node is more than one fact.

findingwhat it says
CHANGEDits shape moved: another class, other arguments, or who feeds it
RESETTLEDit is frozen at another state — other weights, another version
SALTEDits salt moved
DOWNSTREAMnone of those moved and its name moved anyway
STALEits name did not move and its code did
SUSPECTsomething above it is STALE
ADDED / GONEit is in one graph and not the other
UNVERSIONEDits answer is kept and nobody can say whether its code moved
UNKNOWNit cannot be named on one side or the other

The first three are one question split three ways, because two questions get asked of one answer: does my cache still hold is all three, did the code change is CHANGED alone — weights belong to a version, they are not one.

STALE exists because the fingerprint of the code is deliberately not in the key: editing a forward renames nothing, so a diff that only looked at names would answer nothing changed to the very edit being asked about. Here it is an opinion and not an invalidation, and it reaches down as SUSPECT. UNKNOWN must never be read as unchanged: a .mapped() node is named by items nobody has yet.

Each side is a Graph or a snapshot of one, because two versions of a module do not coexist in an interpreter. Nothing here reads or writes a store.

changes(before: Graph | Snapshot, after: Graph | Snapshot, input: Any | None = None, *, store: Store | str | None = None) -> dict[str, list[str]]

What an edit did, as {node: [finding, ...]}. A node with nothing said about it is not in it. input and store are only used for a side that is still a graph, since a snapshot has been named already.

names(graph: Graph, input: Any | None = None, *, store: Store | str | None = None) -> dict[str, str]

What each node’s answer will be called — {node: name} — with nothing run. A node missing from it cannot be named in advance, which is what a .mapped() node and anything under it are.

snapshot(graph: Graph, input: Any | None = None, *, store: Store | str | None = None) -> Snapshot

Everything changes reads about a graph, as plain JSON, so a version can be compared against one that no longer exists in this process. Two are comparable when taken with the same input, which the default always is.

unneeded(graph: Graph, input: Any | None = None, *, store: Store | str) -> list[str]

The nodes that would not have to run at all, because something below them is already kept. A store and not a temporary one: this is the only question here whose answer depends on what is in it.

  • FINDINGSSTALE, SUSPECT, CHANGED, RESETTLED, SALTED, DOWNSTREAM, ADDED, GONE, UNVERSIONED, UNKNOWN