Skip to content

TaskTonic Lifecycle Architecture

TaskTonic Philosophy

This document details the lifecycle management, hierarchy, and initialization protocols of the TaskTonic framework. At the core of this architecture is ttLiquid, the base class for all active components.

The framework utilizes an explicit state machine (ttLC - TaskTonic Life Cycle) to guarantee deterministic initialization, thread-safe service resolution, and secure memory cleanup, even when dealing with complex Multiple Inheritance (MRO) and C++ bindings like PySide.


1. Core Parameters & Hierarchy

Every TaskTonic component is automatically embedded into the TaskTonic hierarchy by the ttLiquid class and receives three fundamental parameters upon creation:

  • id (Integer): A unique identifier guaranteed by the central ttLedger.
  • name (String): A human-readable identifier. If not explicitly provided, the framework generates one based on the id and class name (e.g., 01.MyComponent). Names are reserved in the ttLedger.
  • life_cycle (ttLC Enum): The current state of the object (e.g., NEW, CREATE, STATIC, FINISHED).

The Hierarchy (Bases and Infusions)

TaskTonic operates on a contextual hierarchy. When a liquid is created, it automatically detects its calling context via the ttSparkleStack.

  • base: The parent liquid that instantiated this component.
  • infusions: A list of child components (subjects) created by this liquid. When a base finishes, it cascades the finish command to all its infusions.

2. Configuration Flags (_tt_...)

Developers can control the initialization behavior of a ttLiquid declaratively by setting class-level flags. The framework strongly prefers these explicit flags over dynamic parameter injection via super().__init__().

Service & Hierarchy Flags

  • _tt_is_service (bool | str): Marks the class as a Singleton Service. If True, a generic service is created (requires name upon instantiation). If a string is provided, the service is rigidly bound to that name.
  • _tt_base_liquid (bool): If True, this component refuses to attach to a base (parent context) and acts as an independent root liquid in the hierarchy.

Forcing Attributes (Fail-Fast Configuration)

  • _tt_force_name (str): Hardcodes the name of the component. If a developer attempts to pass a different name during instantiation, the framework raises a RuntimeError.
  • _tt_force_log_mode (ttLog): Hardcodes the logging level. Bypasses inherited log modes and prevents modifications via kwargs.
  • _tt_force_stealth_logging (bool): A convenience flag to permanently lock the logger to STEALTH mode (no output), overriding all other settings.

3. Phase 1: Creation (NEW -> CREATE -> STATIC)

The creation phase is highly secured by the __ttLiquidMeta metaclass and the _tt_route_init router. This ensures that the framework infrastructure is fully operational before any user code is executed.

  1. Metaclass Interception (NEW): The metaclass captures the instantiation and forces the execution through _tt_route_init.
  2. Bootstrap (CREATE): The router executes _bootstrap(). This is a one-time operation that:
  3. Claims the name and id from the ledger.
  4. Binds the object to its base.
  5. Initializes the ttLogger based on the provided parameters or _tt_ flags.
  6. Inherits service dependencies.
  7. The __init__ Chain: The router triggers the Python super() chain.
  8. Security: To protect against C++ MRO quirks (like PySide executing __init__ multiple times), the router tracks executed initializations in a _tt_executed_inits set.
  9. Immutability: The router monitors kwargs (name and log_mode). If a subclass attempts to alter these core parameters dynamically via a nested super() call, a RuntimeError is raised.
  10. Finalization (STATIC): The router cleans up temporary tracking variables, logs the creation phase, and transitions the state to STATIC.

4. Phase 2: Services (STATIC & SPARKLING)

When a developer requests a component flagged with _tt_is_service, the ttLedger checks if it already exists.

If it exists, the __new__ method intercepts the creation and returns the existing instance. The router detects that the object is already STATIC (or active) and executes _tt_handle_existing_service() instead of rebooting the object.

  • The existing service registers the calling liquid as a new contextual base (added to service_bases).
  • It fires the _tt_init_service_base hook, allowing the service to react to the new context.

5. Phase 3 & 4: Finish and Detachment

(Note: The full cascading finish logic for asynchronous tonics is currently under development. The following applies to the core synchronous liquid destruction).

When a liquid is done, it calls finish().

  1. Cascading: The liquid commands all its infusions (children) to finish.
  2. Unregistering: Once all infusions are completed, the liquid detaches from its base and unregisters its id from the ttLedger.
  3. The Shell (ttFinishedLiquid): To prevent dangling references and memory leaks (especially critical for large data or UI components), the object undergoes a forced mutation.

The ttFinishedLiquid Security Shell

Upon completion, the framework replaces the object's class with ttFinishedLiquid and clears its internal __dict__. The object transitions to the DETACHED state.

This has strict consequences:

  • Memory Cleared: All internal variables (dataframes, widget references) are instantly garbage-collected.
  • Fail-Fast Data Access: Any attempt to read or write attributes (e.g., my_liquid.some_data = 5) raises a hard AttributeError.
  • Safe Sparkle Blocking: If an external system (like an async timer or a UI event) attempts to fire a method registered in the sparkles whitelist, the shell intercepts it. Instead of crashing the application, it:
  • Returns a harmless _noop (No Operation).
  • Logs a WARNING.
  • Safely triggers the ttse__on_system_error hook in the calling tonic to gracefully handle the stale event.
  • Idempotency: Requesting the id of a finished liquid will safely and permanently return -1.