Repository navigation
1.i: TopKit Implementation
Non-normative. How the Python reference implementation meets the Specification, and the judgment calls it makes. The Specification wins whenever the two disagree.
One idea per module, nothing over 600 lines.
| Module | Idea |
|---|---|
errors.py |
the failure types |
declarations.py |
the marks (@Action, @Record, Report, Public, @Secret, …), scanning a Tag class once, binding parameters |
geometry.py |
Bases, Shapes, Forms, leaves |
fields.py |
the Field and its sound / defective partitions |
state.py |
the per-Agent state, bound Actions, the runtime type and its descriptors |
overlay.py |
laying one Tag's declarations over a state; materializing Records |
contracts.py |
strict verdicts, binding conditions, Contract
|
transactions.py |
the tagging sequence and the call boundary |
lifecycle.py |
Rip, teardown, Scope, At_Exit
|
access.py |
the hooks on the runtime type, Agent-bound views |
tags.py |
Tag and its metaclass |
queries.py |
Apply, Has, Tags, Outline
|
The hot path is an Agent reading its own attributes and calling its Actions; Agents are built once and play for a long time. So:
-
No
__getattribute__override. A Record is a plain value in the Agent's instance dictionary. An Action is a small bound callable stored in the same dictionary. Python's ordinary lookup finds both at native speed. -
Bound Actions hold the Agent weakly. No reference cycle, so Fields
(which hold Agents weakly) stay honest and finalizers run promptly. A
handle whose Agent died raises
ReferenceError. -
The runtime type is neutral. It is
(Host, Tagged), host first, so every special method of the host keeps working. Its name is the host's name. It carries only what Python requires on a type: special-method Actions, and one descriptor per deleted, secret, or published name, plus__bool__once a Postcondition is visible,__getattr__for views by name, and__del__for deletion. Tags are not in the MRO;isinstanceis answered by the metaclass from the Agent's ever-set. - Runtime types are shared across every Agent whose host and type-level facts match, whatever Tags they carry. Ten thousand Agents of one host normally share one type.
- The composition door is a counter on the Agent's state. Bound protocols raise it while they run; the secret-gate descriptor checks it. Agents without secrets use a plain bound callable and pay nothing.
Measured on Python 3.11 (benchmarks/bench.py), nanoseconds per
operation: plain attribute read 42, Agent host-attribute read 65, Record
read 64, plain method call 86, Action call 295, agent in Tag 291,
bool(agent) with one Post about 1900. Tagging a Record-plus-Post Shape
over a Base costs about 60 µs per Agent; an empty Tag about 22 µs. Peak
memory about 6 KB per Agent with two Tags.
transactions._apply is the call boundary: it snapshots the instance
dictionary, the state, and the class on entry. _gate lays every pending
Tag of the Form over a scratch copy and runs the composed Preconditions
once, so a Shape's gate overrides its Base's and declaration errors
surface before anything changes. _apply_one then lays each Tag over the
live state (no second copy) in the order parts, commit, write; finally
_inspect runs every visible Postcondition once. A TagPreconditionError, TagCompositionError,
TagResolutionError or TagContractError rolls the call back to the entry
snapshot, including Fields. TagImprintError and TagPostconditionError
propagate with everything left in place.
Laying over the live state is safe because nothing reads the new Overlay before commit binds it on the Agent, and the entry snapshot is the only rollback target.
-
invsisinstance.agent in Tagis the is-now check;isinstanceis the has-been check and stays true after Rip. Kept because it is a dependable signal for spotting Rogue Agents. A rolled-back call also rolls the ever-set back. - Records over host descriptors are refused with a Composition Failure rather than silently bypassing a property.
-
The Tag's dotted namespace is the program's. Every Tag-level act is
language syntax on the metaclass:
in,for,~,len,bool,[:],[agent],del Tag[agent],format. The only class attribute TopKit adds is the private_topkit_field.bool(Tag)is "any sound member", like a collection. -
The empty-seat rule on Agents.
__bool__,__format__,__copy__and__deepcopy__are installed on the runtime type only when the host defines none of its own (__bool__only once a Postcondition is visible). Format specs are the display door:f"{Tag:form}",f"{agent:tags}",f"{agent:outline}",f"{agent:contract}". -
Flags own the Agent's
in.__contains__is installed when a@FlagTag lands (a type-level fact, part of the type key; a Flag landing on an Agent that is already tagged rebuilds its runtime type). After the last Flag is Ripped it stays until the type is next rebuilt, answering False. It answers the name, a listed word, or the class of an active Flag and nothing else. The mark is the frozenset of the Tag's words in its own__dict__(empty for bare@Flag); the name is read live from__name__, and the words are never inherited by Shapes. Each Agent keeps its active Flags and their aliases instate.wordsuntil its Tags change or any@Flagis declared (counted indeclarations._flags_declared). A string probe of astrsubclass is read as its plain text, so matching stays exact and never hashes an unhashable subclass.Keyword()is the function form and works on any object. -
One seat, one meaning.
access._host_in_seatreads the host'sinas Python does:__contains__, then__iter__; for each, the first class in the MRO that defines the name decides, andNonefrees the seat (__getitem__alone is ignored). The refusal check (overlay._refuse_a_second_in) and the hook (_hooks_for) both use it, so they cannot disagree. The same check refuses a Flag while a Tag's__contains__/__iter__Action (a published Operation included) is visible, and such an Action while a Flag is active;@Flagrefuses a Tag that declares one. A Form of several Tags is checked as a whole before any of it applies (_refuse_in_collisions_of_the_form), so a Flag Base's Imprint never runs for a Shape that is then refused. No type-level gate (@Delete,@Secret,@Publicof the same name) can overwrite the Flag's hook. Pins are exempt: on a Tag, TOP ownsin. -
Deletion in Layers (STEP-SPEC-18). The runtime type's
__del__is always the kit's finalizer, never a Tag's: a Tag's__del__is an ordinary Action instate.actions, never bound on the Agent, left out of the type-level dunders and of the type key (deleted or not). Soagent.__del__always reads the finalizer. It runs the teardowns (skipped whensys.is_finalizing(): at exit they areAt_Exit's), then the visible__del__: the top Layer, nothing if deleted, else the host's own._host_finalizerfinds the host's own by walking the MRO past the runtime type, as Python does (first definer decides,Nonemeans none, descriptors are bound). The exit path uses no module global and no builtin: what it needs is bound as a default argument, because late in exit both may be gone. The Layer's errors propagate, so Python reports them as unraisable. The first__del__Layer's Underlay is_host_finalizer, or a do-nothing Layer after@Delete. The host's own is found throughtype(agent).__mro__, skipping every class that holds_TOPKIT_HOST_TYPE(a runtime type, even one a user built on); the Agent's__dict__is read withobject.__getattribute__, never through the host's own. An object without TOP state whose class is a runtime type is reset to its host class when its state is attached; if it is never tagged, the finalizer runs its host's__del__. -
A rollback restores the state in place (
_State.Restore), keepingcomposingandchecking: a door or a check opened before the call that is rolled back closes on the same object. (A copy put in its place used to leave the door open for good.) Known limit: in a reference cycle, Python clears weak references before finalizers run, so a finalizer's Tag code cannot call the Agent's bound Actions there (ReferenceError). -
What the kit keeps, and for how long. The Form cache holds a Tag's
Bases only, never the Tag, so a dropped Tag class is freed. A Rip drops
the Tag's view snapshot (a view needs membership). A Field entry is a
slotted weak reference carrying its key, with one callback per Field;
At_Exitnumbers its registrations the same way and drops each one when its Agent dies. A scratch pass (the gate, a Pin's publication) silences the kit's warnings through a context variable, neverwarnings.catch_warnings(), which would reset every warning registry.PERFORMANCE-2026-09-24.mdmeasures all of it. -
A restored name keeps its gate. A name a Tag
@Deleted and a later Layer stored again leavesstate.deletedforstate.restored; the type key gates both. The gate reads the Agent's own value first, so it is harmless for the stored Layer and it keeps a host property of that name hidden. A type rebuilt later for another reason keeps it. -
Reports are builders.
@Report def r(tag[, inherited])is a descriptor that runs its builder once per Tag on first read and caches the value per Tag (weakly).Tag.r += 1replaces the descriptor with a plain value on that class, which is the documented counter pattern. Views snapshot the computed value; a published Report reads live. -
Failures name their check.
TagPreconditionError,TagImprintErrorandTagPostconditionErroruse the_Namedmetaclass:Failure.Named("X")makes, once, the subclassFailure.Xwithname = "X", and attribute access answers only names that were declared. Names are registered inMetaTag.__new__(_name_checks) so the handler is valid as soon as the Tag class exists.Precondition,PostconditionandImprintare_Check_Markobjects: called, they mark; read, they forward to their failure class. Per kind, not per Tag, on purpose: the handler reads the program's own word, and the Tag is already in the message. -
Pins reuse the whole sequence. A pinned Tag is an Agent whose
namespace is its class dictionary.
_namespace_ofhands the kernel a_Class_Namespaceadapter (get, set, pop, keys) over the proxy, so_apply,_materialize,_commit,_snapshotand_rollbackare one code path; the class case restores key by key. The runtime type is a(MetaTag, Tagged)metaclass from the same cache. A landed Action is a_Pinned_Operationdescriptor binding to the Tag it is read from (a Shape inherits it as it inherits a classmethod); a landed Record is a plain class attribute, which is exactly a Report's value._scanskips names the class state manages, so a pinned Tag never projects them onto its Agents. The Tag's own Operations and Reports are host members to a Pin (_pinned_host_function,_pinned_stored); its Agent-scope names and protocols are refused, because a class dictionary is both host and instance namespace and a write there would remove the declaration. No descriptor is ever placed on a metaclass (it would intercept the class-attribute writes the kernel makes): a pinned Tag's@Secretmembers live instate.secret_valuesand answer on the miss path whilecomposingis open;@Publicpinned members are pushed to the Field at commit (_publish_to_field, dry run on copies first) and emitted by the Tag's scan for future Agents, so the scan cache is dropped at pinning._state_ofreads the dictionary directly, which is whyagent in Taggot faster rather than slower. -
Originals for un-patching. When a Pin overlays a Tag's own
Operation, Report or plain value,
_refuse_tag_memberrecords the declared object instate.originals(first patch wins)._call_teardownhands an_Originalsnamespace to a Pin's@Ripteardown that declares a seat beyond its receiver (and Underlay), read through__wrapped__of the composed Action. -
Published members check sound membership at use (STEP-SPEC-10):
the adapter and
_Published.__get__call_require_membership, which raisesTagRogueAccessError(aTagResolutionError, and nothing from the host language: the failure is not anAttributeError, so ahasattrnever swallows it and reports a missing name for a name that exists) when the Agent left, and otherwise runs the Agent's Postconditions through_guardedwithdetailed=True, which raises the named promise and is re-entrancy safe. An Agent without Postconditions pays one dictionary lookup. -
Conditions are sticky (STEP-SPEC-12):
_ripremoves membership and runs the teardown, nothing else. Every bound check carries its origin Tag (_stamp) only so that a Tag re-applied after a Rip replaces its own promise silently.Contract.Delete(agent, *names)pops the named conditions from the Agent's state and raises a Resolution Failure for a name that is not there; it is written to be called from a@Ripprotocol, and works for a pinned Tag as well. -
Field algebra (STEP-SPEC-13):
_Populationinfields.pygives every population the three operators;_Combinedholds two sides and an operator and walks them lazily (|by identity, each once;&and-byinon the right side)._population_ofturns a Tag into its sound partition;MetaTag.__or__falls back totype.__or__when the other side is not a population, soWizard | Nonestays a typing union. No kernel state changes. -
Condition members (STEP-SPEC-14):
_agent_getattranswers a condition by name on the miss path, after Tag views and before the host's own__getattr__, throughcontracts._condition_member, which evaluates one check under the re-entrancy guard and returns a plain bool. Nothing is written to the namespace or the runtime type. The collision rule lives in three places:_refuse_condition_collisionat install (Actions, Records, host class members),_refuse_member_over_conditionwhen an Action or Record is installed over a condition's name, and_refuse_conditions_shadowed_by_the_agentin_apply_onefor a value the Agent's own namespace already holds. - Scope (§0.7) skips a Tag the Agent already carries and records a Tag whose tagging raised a Postcondition failure as applied, so the teardown Rips exactly what the Scope applied.
-
The oracle (
tests/oracle_topkit.py): an independent model of the laws driven by a random walk;tests/test_oracle.pyruns a short walk under the suite. Run it at size with--seeds 50 --steps 1200 --population 18(about 60,000 transitions, under twenty seconds). -
Stacked
@Pre @Postmarks the function's kind"condition"; the scan appends it to both lists and_name_checksregisters both named failures.@Requirementis the same mark written once: it sets the kind"condition"directly, so the two spellings meet in the scan and nowhere else. It carries no failure class of its own, and readingRequirement.Somethingsays which of the two names to catch. -
Assigning a Tag's name on an Agent (
ari.Elf = 1) shadows the view by name; plain Python, not intercepted.Elf[ari]is unaffected. -
Inputs and defaults. A protocol parameter the caller omitted keeps
its declared default; without one it is
None. -
Copying an Agent is refused explicitly (
copy.copy,deepcopy), because the alternative was silently sharing one state between two objects. Cloning is domain work: new Target,Apply(new, *Tags(old)). - Threads. One Agent, one thread. The composing counter and Fields are not synchronized.
- Pickling a tagged Agent fails because its runtime type is synthesized. Serialize the host's data and the list of Tags instead.
- Agent sugar on the mixin (
With,As,|,ApplyTags,__contains__,TagPaths,TagTree): it competed with the host's own methods. The queries live inqueries.pyas functions. -
Tag.NAME,DESCRIPTION,ABSTRACT,Label(),Describe(),Lineage(),Path(): a Tag primitive should not invent application data.Form(Tag)replacesLineage(). -
TagDeletionError(never raised) in favour ofTagDeclarationError. - The implicit
underlay-named-parameter convention.