Private
Public Access
0
0
Files
ed 1761aacff3 Docs: Complete detailed SSDL guide via cluster append
This adds SSDL Cluster 2 (Branch Defusing Mechanics) and Cluster 3 (Python-to-SSDL Mapping Rules) to the docs and combines them into guide_ssdl.md.
2026-06-13 16:56:11 -04:00

16 KiB
Raw Permalink Blame History

Guide to Spec/Sketch Description Language (SSDL)

This document provides a comprehensive technical guide and reference specification for the Spec/Sketch Description Language (SSDL). SSDL is a text-side grammar used in the Manual Slop codebase to model computational shapes, trace data flows, analyze control-path branching, and document immediate-mode rendering graphs.


1. Context & Architectural Rationale

SSDL is heavily inspired by modern data-oriented design paradigms:

  1. Taxonomy of Computation Shapes (Ryan Fleury, 2023) — mapping logic structures to visual topologies.
  2. The Big OOPs: A 35-Year Mistake (Casey Muratori, 2025) — prioritizing system data transformations over compile-time entity hierarchies.
  3. Assuming as Much as Possible (Andrew Reece, 2025) — reducing abstraction layers by exploiting specific access patterns.

The Combinatoric Explosion

Every conditional branch (if, switch, try/except) in a code path multiplies the number of potential execution routes. For example, a function containing 5 branches called from 10 different sites generates 2^5 \times 10 = 320 effective codepaths. When state mutations are added, the permutations multiply, making comprehensive testing and reasoning impossible.

SSDL is a design DSL built to combat this explosion. By sketching the computational shapes of functions before writing code, developers can identify branch points, isolate state mutations, and apply architectural defusing techniques (such as nil sentinels and generational handles) to collapse complex branch graphs into flat, linear, easily testable paths.


2. SSDL Syntax Specification

SSDL uses a small, structured ASCII vocabulary to represent control flow, repetition, state interactions, and data propagation.

2.1 The 6 Computation Shapes (Primitives)

Shape SSDL Notation Definition Architectural Context
Instruction [I:name] A single unit of computation. Reads, transforms, or writes data. No internal branching.
Codepath -> A sequential control flow. A linear progression of instructions that terminates.
Wide Codepath => Parallel or branched control flow. Represents concurrent operations, multi-column tables, or tabs.
Codecycle o-> A loop or repetitive path. Iterations over arrays, dictionary scans, or frame render ticks.
Wide Codecycle o=> Parallel repetitive loops. Parallel processing threads or batch worker execution.
Codecycle Graph Boxes + Arrows System-level network. High-level graph showing data nodes and loop interactions.

2.2 SSDL Modifiers and Annotations

Modifiers are applied to instructions to indicate state operations, termination conditions, or architectural invariants:

  • [T] (Terminator): Marks the exit point of a path (e.g., return, exit, break).
  • [B:condition] (Branch): Marks a point where control flow forks (e.g., if, else, match).
  • [M] (Merge): Marks where multiple branch paths reconverge.
  • [S:variable] (State Mutation): Indicates that the instruction writes to persistent/mutable state.
  • [Q:variable] (State Query): Indicates that the instruction reads from persistent/mutable state.
  • [N:sentinel] (Nil Sentinel): Represents a guaranteed-safe fallback object that eliminates invalid pointer checks.
  • ─── (Data Path): Delimits data flow lines, as opposed to control-flow paths (->).
  • [•] (Defused Branch): Represents a conditional branch that has been refactored into a linear sequence.

3. Visual Topology Diagrams

SSDL diagrams trace control and data progression through functions.

3.1 Sequential Execution Path

A linear sequence containing queries and instructions, terminating cleanly:

[Q:active_window] -> [I:SetupWindowFlags] -> [I:BeginRender] -> [T]

3.2 Branching and Reconvergence

Models a conditional fork and subsequent merge back into the main flow:

        [Q:show_preview]
               │
               ▼
        [B:is_enabled?]
                     ╲
      yes             no
                       ╲
     ▼                   ▼
[I:RenderMD]       [I:RenderText]
     │                   │
     ▼                   ▼
     └──────► [M] ◄──────┘
               │
               ▼
        [I:RenderFooter]
               │
               ▼
              [T]

This diagram maps a 2-way conditional execution path, returning to a single straight-line path after the merge.


4. Architectural Defusing Techniques in SSDL

The primary goal of SSDL during spec design is to defuse branches, collapsing N effective codepaths into 1.

4.1 Technique 1: The Nil Sentinel (Branch Elimination)

Null pointer checks (if node is None:) are a major source of branch multiplication. By introducing a self-referential nil sentinel node, we can eliminate null checks entirely.

Before (Branchy Null-Checking)

Every traversal step requires a validation branch, resulting in 2^4 = 16 effective codepaths:

[Q:root]
   │
   ▼
[B:root != 0?]
   ├─ no ─────► [T:return 0]
   └─ yes
       │
       ▼
     [I:ChildFromValue(root, 1)]
       │
       ▼
     [B:n1 != 0?]
       ├─ no ──► [T:return 0]
       └─ yes
           │
           ▼
         [I:ChildFromValue(n1, 2)] ...

After (Defused with Nil Sentinel [N])

A static nil_node is defined whose child pointers point to itself. Traversal becomes a straight line with 1 effective codepath:

nil_node: [N] = { &nil_node, &nil_node, 0 }

[Q:root]
   │
   ▼
[I:ChildFromValue(root, 1)] -> [I:ChildFromValue(n1, 2)] -> [I:ChildFromValue(n2, 3)]
   │
   ▼
[T:return n3] (if not found, returns nil_node; caller safely queries it without checking)

Sentinel Memory Layout (Linear Defused):

Node A (0x1000) ────► Node B (0x2000) ────► Sentinel Node (0x8000)
                                              │          ▲
                                              └──────────┘ (Self-Loop)

In the sentinel topology, the child/next pointers of the sentinel node point back to 0x8000 (itself). Any query, no matter how deep, resolves safely to the sentinel itself without checking for null.


4.2 Technique 2: Generational Handles (Lifetime Defusing)

Checking if an allocated object has been freed or replaced typically introduces branch complexity. Generational handles defuse this by resolving queries through a validation check that returns a nil sentinel on mismatch.

Main Path:
[Q:entity_handle] ───► [I:ResolveHandle] ───► [B:Generation Matches?]
                                                     ├─ no  ──► [I:return nil_sentinel] ──┐
                                                     └─ yes ──► [I:return entity]        │
                                                                       │                 │
                                                                       ▼                 ▼
                                                            [Q:entity.pos]      [Q:nil.pos] (safe)
                                                                       │                 │
                                                                       └────────► [M] ───┘
                                                                                  │
                                                                                  ▼
                                                                        [I:DrawAtPosition]

The caller's rendering code remains a single straight-line path ([I:DrawAtPosition]), having outsourced the lifetime checks to the validation layer.

Handle Anatomy

A handle is a lightweight, 64-bit value split into two fields:

  • Index (32-bit): The offset in the flat allocation array.
  • Generation (32-bit): A monotonically increasing counter incremented every time the slot at index is reused.
+───────────────────────────────┬───────────────────────────────+
|         Index (32-bit)        |      Generation (32-bit)      |
+───────────────────────────────┴───────────────────────────────+

SSDL Resolution Flow

When resolving a handle to a concrete object, the validation check is encapsulated within the registry, returning the Nil Sentinel on failure:

[Q:handle]
    │
    ▼
[I:LookupRegistry(handle.index)]
    │
    ▼
[B:Slot.Generation == handle.generation?]
         ├── No  ──► [I:Return Nil Sentinel] ──┐
         └── Yes ──► [I:Return Real Object]   │
                                                ▼
                                           [M:Safe Node]
                                                │
                                                ▼
                                        [I:ExecuteAction]

4.3 Technique 3: Immediate-Mode Cache Allocation

Retained-mode API resource management requires tracking allocation, deletion, and reload states (e.g. is_loading, reloading_failed). Immediate-mode caching defuses this by making resource fetching a synchronous key query that handles lazy loading in the background.

RETAINED MODE (Branchy Lifecycle):
[Q:resource] -> [B:is_allocated?]
                   ├─ no  ──► [S:allocate] ──► [B:load_successful?]
                   │                              ├─ no  ──► [T:draw_error]
                   │                              └─ yes ──► [T:draw_resource]
                   └─ yes ──► [B:needs_reload?]
                                 ├─ yes ──► [S:reallocate] ...
                                 └─ no  ──► [T:draw_resource]

IMMEDIATE MODE (Defused Caching):
[Q:resource_key] -> [I:FetchFromCache(key)] -> [I:DrawResource] -> [T]
                           (cache loads in background if missing)

Synchronous Key Querying

The caller requests the resource synchronously every frame using a key:

Caller Path:
[Q:ResourceKey] -> [I:GetCachedResource(Key)] -> [I:DrawResource] -> [T]

Behind the Scenes: Parallel Worker Topology

If the resource is not in memory, the cache returns a placeholder (nil sentinel representation of the resource) and issues an asynchronous load request to a background thread pool.

Foreground (Render Thread):
[I:GetCachedResource] -> [B:In Cache?]
                              ├── Yes ──► [I:Return Resource]
                              └── No  ──► [I:Enqueue Load Task] ──► [I:Return Placeholder]

Background (Worker Thread):
o=> [I:Dequeue Load Task] -> [I:Load Asset] -> [S:Write to Cache] -> [T:Worker Idle]

Once the background worker writes the loaded asset into the cache, the next frame's synchronous call GetCachedResource(Key) naturally hits the cache and returns the fully loaded asset. The control flow in the main rendering loop remains completely linear.


5. Mapping SSDL to Python 3.11 ImGui

To enforce SSDL as a strict design contract, SSDL symbols correspond to specific Python code constructs in src/gui_2.py.

5.1 Basic SSDL-to-Code Transformations

SSDL Code Pattern Python Implementation
[I:label] imgui.text("Label")
[S:app_var] app.app_var = new_value
[Q:app_var] val = app.app_var
[B:show_window] -> [I:panel] if app.show_window: render_panel(app)
o-> [I:row] for item in items: render_row(item)
=> imgui.table_next_column() or imgui.same_line()
[B:popup] => [B:choices] if imgui.begin_popup_modal(...)[0]:

5.2 Python-to-SSDL Mapping Rules & Concrete Examples

All Python code snippets adhere to the manual-slop project's 1-space indentation rule.

Basic Linear Panel

A sequential panel containing labels, inputs, and a button.

def render_simple_panel(app: App) -> None:
 """Renders a basic configuration sidebar panel.
 
 SSDL: [Q:app.config] -> [I:Header] -> [I:TextInput] -> [I:Checkbox] -> [B:SaveButton] -> [S:app.config]
 """
 imgui.text("Configuration Settings")
 changed, app.temp_username = imgui.input_text("Username", app.temp_username)
 if imgui.checkbox("Enable Notifications", app.notifications_enabled)[0]:
  app.notifications_enabled = not app.notifications_enabled
 if imgui.button("Save Changes"):
  app.save_configuration()

Tabbed / Columnar Layouts (Wide Codepath =>)

When layouts expand horizontally (via columns, tables, or tab bars), control flow diverges into concurrent visual segments. SSDL represents this using the => separator.

def render_workspace_tabs(app: App) -> None:
 """Renders workspace tabs.
 
 SSDL: [I:BeginTabBar] -> [B:Tab1] => [B:Tab2] -> [I:EndTabBar]
 """
 if imgui.begin_tab_bar("WorkspaceTabs"):
  if imgui.begin_tab_item("Agent View")[0]:
   imgui.text("Agent Panel Content")
   imgui.end_tab_item()
  if imgui.begin_tab_item("File Editor")[0]:
   imgui.text("File Editor Content")
   imgui.end_tab_item()
  imgui.end_tab_bar()

Loops & Lists (Codecycle o->)

Rendering lists of data requires looping. SSDL represents this using the Codecycle marker o->.

def render_active_agents(app: App) -> None:
 """Renders a list of running agent worker tasks.
 
 SSDL: [Q:app.active_workers] -> o-> [I:WorkerRow] -> [B:KillButton] -> [S:app.active_workers]
 """
 for worker in app.active_workers:
  imgui.text(f"ID: {worker.id} | Status: {worker.status}")
  imgui.same_line()
  if imgui.button(f"Terminate##{worker.id}"):
   app.controller.kill_worker(worker.id)

Popups & Modal Windows

Modals interrupt the parent window flow and create temporary sub-graphs.

def render_error_modal(app: App) -> None:
 """Renders a modal popup dialog when an error occurs.
 
 SSDL: [Q:app.error_msg] -> [B:open_trigger] -> [I:draw_modal] -> [B:close_btn] -> [S:app.error_msg]
 """
 if app.pending_error:
  imgui.open_popup("Error Alert")
  app.pending_error = False
 if imgui.begin_popup_modal("Error Alert", True, imgui.WindowFlags_.always_auto_resize)[0]:
  imgui.text(f"Operation Failed: {app.error_message}")
  if imgui.button("Acknowledge"):
   app.error_message = ""
   imgui.close_current_popup()
  imgui.end_popup()

Comprehensive Code Mapping Example

This example demonstrates a complete ImGui popup modal implementation and its corresponding SSDL annotation.

def render_confirm_action_modal(app: App) -> None:
 """Renders the confirm action popup modal.
 
 SSDL: `[Q:show_confirm] -> [B:open_popup] -> [I:message] => [S:pending_action | B:cancel_button]`
 """
 if app.show_confirm_modal:
  imgui.open_popup("Confirm Action")
  app.show_confirm_modal = False
 
 if imgui.begin_popup_modal("Confirm Action", True, imgui.WindowFlags_.always_auto_resize)[0]:
  imgui.text("Are you sure you want to proceed?")
  
  if imgui.button("Confirm", imgui.ImVec2(120, 0)):
   app.controller.execute_pending_action()
   imgui.close_current_popup()
  
  imgui.same_line()
  if imgui.button("Cancel", imgui.ImVec2(120, 0)):
   imgui.close_current_popup()
  
  imgui.end_popup()
SSDL Trace Breakdown:
  1. [Q:show_confirm]: The function checks app.show_confirm_modal state.
  2. -> [B:open_popup]: If true, it opens the modal dialog.
  3. -> [I:message]: Draws the warning text "Are you sure you want to proceed?".
  4. =>: Branches horizontally to draw action options side-by-side.
  5. [S:pending_action]: Confirm button modifies state via controller.
  6. [B:cancel_button]: Cancel button closes the modal.