This website is meant to be read and understood quickly by humans, but is only fully parsable, on a technical level, with the aid of an AI system. Read why →
Loop MMT

Loop MMT — Coding Standards v2 · disclosure funnel

Section 1
Readability is the Primary Goal

Code is read far more often than it is written. Every line of code in a Loop MMT constellation will be read by future AI sessions that have never seen it before, by the Integrator verifying it against the spec, by the Operator debugging a production issue at 2am, and potentially by developers (human or AI) maintaining the system years after the original conversations are gone. The code must serve all of these readers. Performance matters. Correctness matters more. But readability matters most — because unreadable code cannot be verified for correctness and cannot be safely optimized for performance.

This is not an abstract preference. It is a structural requirement of the Loop MMT methodology. Each module conversation sees only its own loading pack. The Integrator sees the full spec but reads every module's code to verify it. A future bug-fix session will load this code cold and need to understand it immediately. If the code cannot be understood by a reader with no context beyond the section header and the contracts, the code is not done — regardless of whether it runs correctly.

The Standard
If you put this code in front of a person who is not a programmer, they should be able to read through it and have some idea of what it does. Not every implementation detail — but the shape of it. "This function takes an order, checks if the customer exists, calculates a price, and saves it." That level. If a non-programmer cannot extract that from reading the code, the code is too clever.
Code is Written for People First, Machines Second

Robert C. Martin's Clean Code established the principle that code should be written for human readers first and for the machine second. This principle is absolute in Loop MMT. The machine will execute whatever you give it. The human (or the next AI session) must be able to understand what you gave it and why.

This means: choose clarity over brevity every time. A variable named v_remainingRetryAttempts is better than v_retries. A function named f_calculateTieredDiscountForLoyaltyCustomer is better than f_calcDisc. Yes, the longer names take more characters. Characters are free. Comprehension is expensive.

This also means: do not optimize for performance until you have measured a problem. Readable code that runs in 5ms is better than clever code that runs in 2ms. The 3ms you saved will cost hours when someone needs to understand the clever version. Optimize only when profiling shows a specific bottleneck, and when you do optimize, leave a comment explaining why the readable version was replaced and what the performance constraint was.

Meaningful Names

Names are the primary mechanism by which code communicates intent. A well-chosen name eliminates the need for a comment. A poorly chosen name creates a need for a comment and then fails to satisfy it.

Use intention-revealing names. The name should answer: why does this exist, what does it do, and how is it used. v_elapsedTimeInDays communicates. v_d does not. f_validateOrderHasRequiredFields communicates. f_check does not. The reader should never have to look at the implementation to understand what the function does — the name should tell them.

Avoid disinformation. Do not call a collection v_orderList if it is not a list (it might be a Set or a Map). Do not call a variable v_accountGroup if it is a single account. Names that imply the wrong data structure or cardinality mislead the reader into wrong assumptions.

Make meaningful distinctions. If you have two variables in the same scope, their names must make it clear why both exist. v_order and v_orderData — what is the difference? v_orderFromDatabase and v_orderForPacket — now I know. If you cannot name two things distinctly, they may not need to be two things.

Use pronounceable names. Code is discussed verbally — in the talk phase, in handoffs, in bug reports. v_genymdhms cannot be spoken in conversation. v_generationTimestamp can. If you cannot say the name out loud in a sentence, the name is wrong.

Use searchable names. Single-letter variables and short generic names are impossible to search for in a codebase. MAX_RETRY_ATTEMPTS can be found instantly. 3 buried in a conditional cannot. This matters because the Integrator searches across modules to verify consistency.

Pick one word per concept and stick with it. If Vault Loops use fetch for read operations, all Vault Loops use fetch. Not get in one and retrieve in another and load in a third. If packet types use _CREATED for post-write echoes, they all do. Not _MADE or _ADDED. Consistency across the codebase is more important than the specific word chosen.

Small Functions That Do One Thing

Functions should be small. How small? Small enough that they do exactly one thing and you can describe that thing in a single sentence without using the word "and." If the description requires "and," the function is doing two things and should be split.

The stepdown rule. Code should read like a narrative, top to bottom, where each function is followed by the functions at the next level of abstraction. The top-level handler reads like prose: "validate the order, hydrate the customer data, calculate the price, persist the record, emit the echo." Each of those steps is a function. Each of those functions contains its own lower-level steps. The reader can stop at whatever level of detail they need.

// ── Top level reads like prose ──
async function h_processOrder(packet) {
  const v_validationResult = f_validateOrderFields(packet.payload);
  if (!v_validationResult.valid) {
    f_emitValidationRejection(v_validationResult.errors);
    return;
  }

  const v_customer = await f_hydrateCustomer(packet.payload.customerId);
  if (!v_customer) {
    f_emitCustomerNotFound(packet.payload.customerId);
    return;
  }

  const v_pricedOrder = f_calculateOrderPrice(packet.payload, v_customer);
  await f_persistOrder(v_pricedOrder);
  f_emitOrderCreated(v_pricedOrder);
}

// Each called function handles one concern at the next level down.
// The reader understands the flow without reading any of them.

One level of abstraction per function. Do not mix high-level business logic with low-level implementation details in the same function. If a function calls f_calculateDiscount (high-level) and also does Math.round(price * 100) / 100 (low-level rounding detail), the rounding belongs in a helper: f_roundToTwoDecimals. The reader should not have to mentally switch between abstraction levels while reading a single function.

Functions should have no side effects. A function named f_validateOrderFields should validate and return a result. It should not also log to the audit trail, modify the packet, or emit a bus event. If it does those things, the name is a lie — the reader expects validation only and gets surprised by hidden behavior. Side effects belong in the calling function where they are visible and explicit.

Comments as a Last Resort

The best comment is code that doesn't need one. A function named f_rejectOrderIfCustomerIsArchived does not need a comment explaining what it does. A variable named v_maximumAllowedWeightInPounds does not need a comment explaining what it holds.

When you feel the need to write a comment, first ask: can I rename the function or variable to make the comment unnecessary? Can I extract a block of code into a well-named function? Can I restructure the logic to be more obvious?

When comments are necessary, they explain why, not what. "Why" comments survive code changes; "what" comments become lies the moment the code is modified and the comment is not updated. Good comments explain: business rules that are not obvious from the code, why a particular approach was chosen over a simpler alternative, what would break if this code were changed, and edge cases that are not apparent from reading the happy path.

// GOOD: explains WHY — the business rule behind the code
// Orders under $10 get free shipping because the processing cost
// exceeds the shipping fee at that price point
if (v_orderTotal < 1000) {
  v_shippingCost = 0;
}

// BAD: explains WHAT — restates the code, adds no information
// Check if order total is less than 1000
if (v_orderTotal < 1000) {
  v_shippingCost = 0;
}

Loop MMT requires a comment above every function (see Section 8). This is the one place where the "last resort" rule is overridden — the function comment serves as a quick-reference entry point for readers scanning the file. But even this comment should explain why the function exists, not what it does line by line.

Formatting as Communication

Code formatting is not cosmetic. It communicates structure. Related code should be close together. Unrelated code should be separated by whitespace. The vertical distance between two pieces of code should reflect their conceptual distance.

Vertical ordering. High-level functions at the top, lower-level helpers below. The reader enters at the top and drills down as needed. This is the newspaper metaphor from Clean Code: the headline is at the top, the details are below, and you stop reading when you have enough information.

Horizontal density. Keep lines short enough to read without scrolling. Long lines force the reader to track two dimensions (down and across). If a function call has six arguments and doesn't fit on one line, it may need restructuring — not line-wrapping.

Consistent formatting across the entire codebase. Every loop factory follows the same internal structure (Section 9). Every handler has the same error-handling shape. Every packet emission has the same field order (type, marker, payload, source). Consistency lets the reader's pattern recognition do the work — they learn the shape once and recognize it everywhere.

The Boy Scout Rule

Leave the code cleaner than you found it. If a bug-fix session touches a function with a misleading name, rename it. If a module conversation notices a magic number while working nearby, extract it to a named constant. Small, continuous improvements prevent the gradual accumulation of unreadable code.

This rule has a boundary in Loop MMT: do not refactor outside your scope without flagging it to the Operator. The improvement must be within the module you are working on. Cross-module cleanups go through the Coordinator and the Integrator.

The Readability Test

Before delivering code, apply this test: could a new AI session, loaded only with this module's loading pack and these coding standards, read this code and understand what it does, why it does it, and how it connects to the rest of the constellation? If the answer is no, the code is not done. The tests may pass. The contracts may match. But if the next reader cannot understand it, it will eventually be changed incorrectly, and the tests and contracts will stop protecting it.

A second test, more demanding: could a person who does not write code — but who understands the business domain — read the top-level handler functions and recognize the business process? "It validates the order, looks up the customer, calculates the price, saves it, and sends a confirmation." If the code is clean enough, a business-literate non-programmer can follow that narrative in the code itself, not just in a comment above it.

The Clean Code Principles Applied to Loop MMT
Meaningful names → the prefix taxonomy and full-word naming convention. Small functions → the stepdown rule within every handler. Single Responsibility → one loop type per concern, enforced by the closure wall. No side effectsf_ functions are pure; side effects are explicit bus emissions in handlers. Don't Repeat Yourself → patterns are named and reusable; config tables drive behavior generically. Comments explain why → every function gets a why-comment, not a what-comment. Consistent formatting → every factory follows the same six-part internal structure. These aren't aspirations. They are requirements. Code that violates them is not ready for delivery.
Section 2
The Rule

Full words, no abbreviations. camelCase for variables and functions. UPPER_SNAKE_CASE for constants. Every function has a comment above it explaining why it exists. Error case before happy path. Magic numbers become named constants. No unrequested features. Build exactly what was discussed in the talk phase.

Consistency Over Cleverness
If a pattern for what you're building already exists in the codebase, follow it exactly. If no pattern exists, establish one consistent with existing patterns. The goal is that code written in five separate conversations by five different AI sessions looks like it was written by one person. The naming conventions, structure patterns, and error handling below exist to make this possible.
Section 3
Prefix Taxonomy

Every variable, function, constant, and factory uses a prefix that identifies its role at a glance. This is not optional.

PrefixMeaningExamplesNotes
bus_Bus instancebus_data, bus_vault, bus_signalThe bus objects passed to loop factories.
loop_Loop factory functionloop_vaultOrders, loop_computePricingNever used for instances — only the factory.
pkt_Packet type constantpkt_ORDER_CREATED, pkt_FETCH_CUSTOMERUPPER_SNAKE after prefix. Matches contract registry.
wf_Workflow pipeline specwf_cancelOrder, wf_batchNotifyThe pipeline spec object, not a function.
gate_Dependency gate instancegate_supabase, gate_stripeOne per adopted dependency.
v_State variablev_retryCount, v_connected, v_bufferAny mutable state within a loop. Scratch state uses this prefix.
f_Pure function / helperf_calculatePrice, f_validateSchemaTakes inputs, returns output. No side effects. No bus access.
e_DOM element referencee_intakeForm, e_statusDisplayPresentation Loop only. Cached at init, not queried per render.
h_Event handlerh_onSubmitOrder, h_onStatusClickPresentation Loop only. Bound to UI events.
c_Contract definitionc_orderCreated, c_fetchCustomerThe contract object in the registry.
When in Doubt
Is it a value that changes? v_. Is it a function with no side effects? f_. Does it respond to a UI event? h_. Does it talk to the bus? It's a handler inside a subscription, not a standalone function — it doesn't get a prefix, it lives inside the bus.on() callback or is named as a handler within the factory.
Section 6
Error Handling

Every operation that can fail gets a try/catch. The catch follows the Loop Recovery Protocol: do not crash, alert the bus, preserve state, continue operating.

// ── The standard error handling pattern ──

async function h_processOrder(packet) {
  try {
    // Error case checks first (NASA rule)
    if (!packet.payload.orderId) {
      bus.data.emit({
        type: 'VALIDATION_REJECTED',
        payload: { reason: 'missing_orderId', source: packet },
        source: 'vault:orders',
      });
      return; // Early return — do not process
    }

    // Happy path
    const result = await db.from('orders').insert(packet.payload);
    // ... emit echo ...

  } catch (err) {
    // Alert the bus — specific error context
    bus.data.emit({
      type: 'LOOP_ERROR',
      payload: {
        loop: 'vault:orders',
        operation: 'CREATE_ORDER',
        error: err.message,
        // Include enough context to diagnose without replay
        packetType: packet.type,
        payloadSummary: { orderId: packet.payload?.orderId },
      },
      source: 'vault:orders',
    });
    // Do not re-throw. The loop continues to operate.
    // State was not modified (the insert failed).
  }
}

Error case before happy path. Check for invalid inputs, missing required fields, and precondition failures at the top of every handler. Return early on failure. The happy path should be the unindented code, not nested inside validation checks.

LOOP_ERROR payload. Always include: which loop (loop), which operation (operation), the error message (error), and enough context to diagnose without needing to replay the exact packet. Do not include the full packet payload in the error — it may contain PII. Include identifiers only.

Section 7
Naming Conventions
Functions

camelCase, full English words, reads like prose. Factory functions use create prefix: createVaultOrders, createFilterOrderValidation, createComputePricing. The factory name encodes the loop type and the business domain.

Handler functions inside factories use descriptive names: h_createOrder, h_fetchCustomer, h_onSubmitIntake. Helper functions use f_ prefix: f_calculateDiscount, f_validateOrderSchema, f_generateId.

Packet Types

UPPER_SNAKE_CASE. Verb-noun structure. The verb describes what happened or what is requested:

PatternExamplesUsed For
CREATE_*CREATE_ORDERCommands — requesting a write
*_CREATEDORDER_CREATEDEchoes — confirming a write happened
FETCH_*FETCH_CUSTOMERPull requests — requesting data
*_FETCHEDCUSTOMER_FETCHEDPull responses
*_UPDATEDORDER_UPDATEDEchoes for updates
*_ARCHIVEDORDER_ARCHIVEDEchoes for soft deletes
*_VALIDATEDORDER_INTAKE_VALIDATEDFilter pass results
*_REJECTEDVALIDATION_REJECTEDFilter rejection results
COMPUTE_*COMPUTE_PRICERequesting a computation
*_CALCULATEDPRICE_CALCULATEDComputation results
Loop Addresses

Format: type:businessDomain. The type is one of: working, compute, vault, filter, signal, presentation, workflow. The business domain is camelCase. Examples: vault:orders, compute:pricing, filter:orderValidation, signal:customerNotify, workflow:cancel.

Pipeline Specs

Pipeline spec variable names use wf_ prefix with descriptive name: wf_createOrder, wf_cancelOrder, wf_batchSendNotifications. The name field inside the spec uses snake_case: name: 'create_order'. Step names within the pipeline also use snake_case: name: 'validate_payment'.

Database Tables and Columns

snake_case for database tables and columns: orders, customer_id, total_price, created_at. camelCase for JavaScript/packet fields: customerId, totalPrice, createdAt. The Vault Loop translates between the two conventions at the database boundary. This translation is part of the Vault Loop's responsibility and is never done by other loop types.

Section 8
Comments

Every function gets a // comment on the line above it. The comment explains why the function exists, not what it does line-by-line. If the function name and signature don't make the "what" obvious, the function is named poorly — fix the name, don't compensate with comments.

// Translate database snake_case to packet camelCase for the order contract
function f_mapOrderRow(row) { ... }

// Reject orders with missing required fields before they enter the pipeline
function f_validateOrderSchema(payload) { ... }

// The pricing engine uses tiered rates — this prevents a single-item
// order from paying the same base rate as a 50-item bulk order
function f_calculateTieredDiscount(items, customerTier) { ... }

Section dividers within a factory use the single-bar format:

// ── Subscriptions ──
// ── Handlers ──
// ── Helpers ──

Do not write comments that restate the code. // increment counter above count++ is noise. Write comments that explain why: decisions, business rules, edge cases, things a future reader would wonder about.

Section 9
Code Organization Within a Loop

Every factory follows the same internal structure:

  1. Section header — the quick-reference card (type, capabilities, emits, listens, patterns).
  2. State declarations — any v_ variables the loop maintains (scratch state, bridges, counters).
  3. Subscriptions — all bus.on() calls grouped together at the top of the factory body.
  4. Handlers — the functions that process each subscribed packet type, in the same order as the subscriptions.
  5. Helpers — pure f_ functions used by the handlers.
  6. Initialization — any setup that runs when the factory is called (startup sweeps, bridge creation, timer setup).

This order makes every loop readable in the same way. A reader looking for "what does this loop do when it receives ORDER_CREATED?" goes to the subscriptions section, finds the handler name, and jumps to it.

Section 11
Delivery Checklist

Run through this for every deliverable. Every time.

  1. Factory signature matches capability table. A Working Loop receives { bus } and nothing else. A Vault Loop receives { bus, db } and nothing else. No extra capabilities.
  2. Section header is accurate. Type, capabilities, emits, listens, patterns — all match the actual code.
  3. Emitted packets match contracts. Every emitted packet type exists in the contract registry. Every required field is present. Field names are character-for-character matches.
  4. Subscriptions match routing table. Every bus.on() call subscribes to a type that has a routing entry with this loop as a destination.
  5. Markers are explicit. Every emitted packet includes marker: 'live', 'empty', or 'tombstone'. No omissions.
  6. Error handling follows recovery protocol. Every async operation has try/catch. Every catch emits LOOP_ERROR with loop name, operation, and error message. No re-throws. No silent catches.
  7. Naming follows taxonomy. Prefix on every variable, function, and constant. Full words. camelCase.
  8. Comments explain why. Every function has a comment. No "what" comments that restate the code.
  9. No unrequested features. The deliverable contains only what was agreed on in the talk phase.
  10. Tests pass. If this module has tests, they are green. If not, the code has been manually verified against the contracts.
Section 12
Anti-Patterns
Anti-PatternWhyInstead
Ghost state in closure variablesInvisible to other loops, not on bus, not in VaultState goes on the bus (emit it) or in a Vault (persist it). Only scratch state is allowed in closures, and it must be cleared per-transaction.
Capability leakageA loop accesses something not in its factory signatureIf you need it, it belongs in the capability injection. If it's not in the table for your loop type, your design is wrong.
Guessing at field namesCreates the #1 integration errorCheck the contract registry. Character-for-character match. If the contract says customerId, do not write customer_id.
Silent error swallowingFailures become invisibleEvery catch emits LOOP_ERROR. No empty catch blocks. No catch (e) { /* ignore */ }.
Omitting the markerAmbiguous responses — "no data" vs "lookup failed" becomes indistinguishableEvery emitted packet includes an explicit marker. Every pull response includes a marker.
Cross-loop communication outside the busBypasses contracts, routing table, and observabilityAll inter-loop communication goes through the bus. No shared variables, no direct function calls, no events outside the bus system.
Database access outside Vault LoopsViolates the closure wall — only Vault Loops receive dbNeed data? Pull it from a Vault via the bus. Need to write? Emit a command to a Vault.
Business logic in Presentation LoopsUI should render state, not compute itComputation goes in a Compute Loop. The Presentation Loop subscribes to the result.
Unrequested featuresScope creep, untested code, undiscussed design decisionsBuild exactly what was discussed. If you see something useful to add, suggest it — don't implement it.
Verbal contracts"I think the field was called X" introduces mismatchesCheck the document. If it's not in the loading pack, ask for it.
Returning from factory functionsThe factory sets up subscriptions; it doesn't return an APILoops communicate through the bus, not through return values. (Exception: the bridge helper, which is internal to the loop.)
Happy path firstError cases get buried, forgotten, or half-implementedError case before happy path. Validate inputs at the top. Return early on failure.