New Architecture Codex — Public Edition

NACODEX

A Framework for Singular Ownership in Complex Software Systems

“A fix made in one place should propagate everywhere it is needed, automatically, without the developer having to know everywhere it is needed.”

— The founding premise of this architecture
12Axioms
8Premises
24Dead Ends
v1.1.2Current Edition
About

Built from real failures

Every rule here exists because something broke without it. Every axiom was earned through production software — not designed in advance.

What it is

An empirical architecture framework

NACODEX describes structural rules for organizing software systems — particularly systems that grow feature by feature, serve multiple components from shared resources, and must be maintainable without losing track of where everything lives.

It was developed through shipping software, encountering failure, identifying the structural cause, and correcting not just the symptom but the condition that made the symptom possible.

Who it’s for

Developers building real systems

The framework applies to any software system where multiple components share access to the same underlying resources, the same logical operation is needed in more than one place, and a single developer or small team must maintain the whole.

The principles are not platform-specific. The architecture originated in Android mobile development. The rules are general.

Current Edition

The Codex — v1.1.2

Integrates the base Codex, Update 1 (ICVU pattern), and Update 2 (Fixed Surface Rule). Expand any section to read.

This document describes an architectural framework developed empirically — through shipping software, encountering failure, identifying its structural cause, and correcting not just the symptom but the condition that made the symptom possible.

The framework applies to any software system where multiple components share access to the same underlying resources; the same logical operation is needed in more than one place; the system grows feature by feature over time; and a single developer or small team must maintain the whole without losing track of it.

This document serves two purposes simultaneously: it is a template for building or refactoring a project to follow this architecture, and it is an academic study of what happens when you take the question “where should this function live?” seriously enough to answer it with permanent, enforceable rules rather than intuition.

Living Document Notice
This document is not final. It is current.

NACODEX is updated whenever an architectural discovery warrants it. Every rule reflects the best understanding at the time of its integration. A later update may refine a rule’s domain, add conditions to its application, or define its boundary with another rule. This is expected and correct behaviour — not a sign that the earlier rule was wrong.

1.1 — The Problem This Solves

Every software project that grows feature by feature eventually encounters the same failure mode: divergent duplication. A function is written in Class A. Later, Class B needs it and copies it with slight adaptations. Two independent copies now exist, maintained independently. A bug fixed in one is not fixed in the other — because the other is not remembered.

This is not a failure of discipline. It is a structural failure. The codebase has no mechanism to prevent duplication and no mechanism to enforce that a fix propagates. The framework in this document solves this structurally by ensuring every function, constant, and piece of logic has exactly one home.

1.2 — Core Philosophy
Singular ownership.
Every function belongs to exactly one class. No other class reproduces it.
Layered dependencies.
A class in layer N depends only on classes in layers below N. Build order is deterministic. Impact of any change is bounded.
Managers, not utilities.
Shared logic lives in a manager class that owns a specific concern end-to-end — its state, its lifecycle, its public interface. Consumers call it; they do not replicate it.
Constants are code.
A string literal that identifies a preference key, a broadcast action, or a configuration slot is logic. All constants live in dedicated files and are referenced by name everywhere else.
1.3 — Premises (P1–P8)

Empirical observations derived from observation rather than theory. The rules that follow are grounded in these premises.

P1
Duplication is the primary source of bugs in growing codebases.
When the same logic exists in multiple places, the copies always diverge over time due to independent modification. The divergence produces bugs disproportionately hard to find because they are invisible at the point of failure.
P2
Any system resource with non-obvious interaction rules must be wrapped.
Every platform has resources that interact in ways not documented or counter-intuitive. Once discovered, the correct usage pattern must be owned by one class, so all consumers inherit the correct behaviour rather than each independently rediscovering the constraint.
P3
String constants are a silent failure point.
A misspelled or misremembered constant string causes a silent default value rather than a crash. No compiler catches it. The bug appears at runtime, often in a path not exercised in development. Constants must be declared once and referenced by name everywhere.
P4
Structured data passed as loose parameters degrades over time.
When a function requires many parameters as separate arguments, callers frequently pass them in the wrong order or diverge in handling edge cases. Grouping related parameters into an immutable data container eliminates this class of bug and makes the function’s contract explicit.
P5
Trivial helpers cause consequential bugs.
A one-line measurement function is often the source of bugs that take multiple sessions to diagnose. When the same measurement is computed differently in two places, the difference only manifests at the edges of the valid range.
P6
Architecture that grows from necessity is reliable but incomplete.
Rules derived from real failures are trustworthy because they are grounded in evidence. But they handle what happened, not what will happen. Explicit extension protocols are required for new categories of concern.
P7
A layered dependency graph prevents circular coupling.
Classes in layer N depend only on classes in layers below N. A circular dependency is always a design error, always fixable by introducing a callback interface or splitting the violating class.
P8
In-memory state and persistent state are not automatically synchronized.
A platform’s persistent storage is not an observable reactive store. An in-memory field and a persistent storage entry representing the same logical value will diverge whenever written by separate code paths. This divergence is silent and must be prevented by construction, not detected after the fact.
Added in Update 1
1.4 — Axioms (AX1–AX12)

Behavioral rules that all code following this framework obeys. A violation is a defect, not a style difference.

AX1
One class owns each concern. No exceptions.
If a function exists in more than one class, one copy must be eliminated. The only acceptable exception is when two functions are genuinely different operations that happen to share a name — in which case the names should be different.
v1.0
AX2
Consumers call managers. Managers do not call consumers.
Dependency flows downward through the layer graph. A manager may accept a callback interface implemented by a consumer, but it never holds a reference to a concrete consumer class.
v1.0
AX3
Every constant string lives in one place.
Configuration keys, action identifiers, and slot names are declared as named constants in a dedicated file. No string literal that identifies a persistent value or an inter-component message appears in more than one file.
v1.0
AX4
Every inter-component message is built by one sender.
When multiple components send the same type of message to the same recipient, one class is responsible for constructing that message. No component builds the message inline. Any change to the format requires editing exactly one place.
v1.0
AX5
Every managed resource has a designated manager.
Any resource requiring lifecycle management — creation, configuration, and destruction — is owned by a dedicated manager class. No component manages a resource it does not own as its primary concern.
v1.0
AX6
All platform-specific measurement helpers are canonical.
Functions that translate between measurement units, coordinate spaces, or display metrics live in one utility class. No component implements these locally, regardless of how simple they appear.
v1.0
AX7
System interaction constraints are enforced at the point of interaction.
When a platform API has a non-obvious constraint — a call that must not happen during a certain state, an ordering requirement, a flag that interacts with another flag — the enforcement lives in the class that owns the interaction, not in every caller.
v1.0
AX8
Stop operations are always atomic.
Any mechanism that provides zero-latency or pre-emptive stopping of an operation is always triggered through the canonical stop path. It is never set directly by a caller.
v1.0
AX9
New persistent keys are declared before first use.
A new configuration key is added to the constants file in the same delivery as the first code that reads or writes it. It never appears in code before it appears in the constants file.
v1.0
AX10
New display measurements are added to the utility class before first use.
A new measurement function is added to the measurement utility in the same delivery as the first code that calls it.
v1.0
AX11
Every value written by more than one component follows ICVU.
Any value that can be written by more than one component boundary AND must be consistent across all components at all times must have a designated setter that writes both in-memory and persistent storage atomically. No component may write only memory or only storage for such a value.

Domain boundary (Update 2): ICVU governs application-layer value consistency. It does not govern the GPU and compositor pipeline. Values that are simultaneously application-layer data and compositor surface parameters are governed by AX12 in the compositor domain.
Added in Update 1 · Domain boundary added in Update 2
AX12
Overlay window dimensions are fixed at addView() time.
A window manager overlay window whose drawn content changes size during its lifetime must be sized at addView() time to the maximum content it will ever display. After addView(), window dimensions are never changed. updateViewLayout() is called with position changes only. Content size changes are reflected through invalidate() and onDraw(), not through surface reallocation. Rationale: updateViewLayout() with new dimensions triggers surface reallocation in the compositor. The reallocation and the next completed draw are decoupled across frames, producing a positional artifact proportional to the size delta. No application-layer fix can prevent a compositor-layer timing artifact. See Section 2.7 and Dead Ends DE23–DE24.
Added in Update 2
2.1 — Function Classification (A–E)

Every function belongs to exactly one of five categories. The test for each is applied in order; the first that fits is correct.

AFoundation Utilities
Pure computations. No mutable state. No side effects. Always return a value. Trivially testable in isolation.
BData Containers
Immutable value objects. Carry related fields. Include a Builder with validation. No behaviour beyond construction and field access.
CSingle-Concern Managers
Stateful classes that own one concern end-to-end. Accept callback interfaces. Must be replaceable without touching any consumer.
DSystem Bridges
Classes that wrap platform APIs behind a stable, testable interface. The wrapped API must not appear directly in any other class.
EUI Owners
Activities, controllers, and services that own a screen, overlay, or background process. Top of the dependency graph. All non-UI logic delegated to managers.
2.2–2.4 — Grouping, Management & Creation Rules (abridged)
GR1 — Cohesion over proximity.
A function belongs with the functions it is always used alongside, not with the first class that happened to need it.
GR3 — Shared functions leave their original class.
When a function private to Class A is needed by Class B, it is promoted to the appropriate manager before Class B is written.
GR6 — If extraction feels premature, it is not.
Simplicity is not a reason to allow duplication. Extract early.
MR1 — The owner edits. Consumers call.
When a function needs to change, the change is made in the owning class. Consumers are not modified unless the public interface changes.
MR3 — Dead code is deleted immediately.
A function no longer called by any consumer is removed in the same session it becomes unused. Version control preserves history.
MR5 — Callback interfaces are the correct mechanism for consumer-specific behaviour.
The manager holds the interface type, not the consumer type. Callback methods are named from the manager’s perspective, not the consumer’s.
CR1 — Classify before writing.
Before writing a new function, classify it (A–E). This determines where it lives. Do not write the function and decide placement later.
CR7 — High-risk operations require evaluation before code.
Any change involving non-obvious platform interaction requires a written evaluation: what it does, why the approach is correct, what can go wrong.
2.5 — The Layer Graph

Every class belongs to exactly one layer. Dependencies only flow downward. A violation is always a design error, always fixable by introducing a callback interface or splitting the violating class.

LAYER 4
UI Owners (Category E)
Activities, Services, Controllers · May depend on: all layers below
↓ depends on
LAYER 3
Complex Managers (Category C)
Own multi-step lifecycle, accept callbacks · May depend on: layers 1 and 2
↓ depends on
LAYER 2
Logic and Contracts (Categories B, C, D)
Data containers, message builders, bridges, detectors · May depend on: layer 1
↓ depends on
LAYER 1
Foundation (Category A + constants)
Pure utilities, measurement helpers, constant files · No application dependencies
2.6 — The ICVU Pattern

InstantCompactValueUpdate (ICVU) — a write pattern for values that must remain consistent across multiple storage media at all times. Instant: the update happens synchronously. Compact: both media are updated in a single method call. The caller has no knowledge of the two-medium write.

Apply when ALL are true: (1) the value is written by more than one component; (2) read by more than one component; (3) an incorrect read causes a user-visible failure; (4) the failure may occur silently without any error or warning.

// Atomic setter — the only location where both writes occur
private void setValue(float x, float y) {
    fieldX = x; fieldY = y;          // 1. update in-memory
    storage.save(this, x, y);        // 2. update persistent storage
    updateDisplay();                 // 3. optional: refresh derived display
}

// Sync-from-storage — called at component resume points
private void syncFromStorage() {
    fieldX = storage.getX(this, fieldX);
    fieldY = storage.getY(this, fieldY);
    updateDisplay();
}

Domain boundary (Update 2): ICVU governs application-layer value consistency only. Window dimensions live in the compositor domain and are governed by AX12 instead.

2.7 — The Fixed Surface Rule (FSR)

The problem: An overlay window whose content changes size at runtime produces a diagonal positional artifact on every resize call, equal to the size delta per axis. The artifact is in the system compositor — not in application code. No application-layer fix can prevent a compositor-layer timing artifact.

The rule: Size the window once at addView() to the maximum content it will ever display. Only x and y change through updateViewLayout(). Content size changes via invalidate() only.

// Attach — size ONCE at maximum content dimensions
int fixedSize = maxContentSize;
if (fixedSize % 2 == 0) fixedSize += 1;  // force odd → half-integer center

params = buildParams(centerX, centerY, fixedSize);
windowManager.addView(view, params);      // surface allocated once

// Content size change (slider, animation, theme)
content.update(newSize, newRadius);       // sets fields, calls invalidate()
// NO updateViewLayout — surface NOT reallocated

// Position change only
params.x = (int)(targetCenterX - fixedSize / 2f);
params.y = (int)(targetCenterY - fixedSize / 2f);
windowManager.updateViewLayout(view, params);

A dead end is a specific approach that is structurally incapable of solving a class of problems, documented to prevent future investigation time from being spent rediscovering that these approaches do not work.

When an approach appears here: this has been investigated fully and eliminated. Do not revisit without a specific new argument that changes the model — not just a different formulation of the same approach.

DE1
Inline coordinate clamping in consumer classes
Each consumer computing its own clamp bounds uses different measurement helpers producing inconsistent results. Correct bounds require a specific platform API call. The fix: one canonical clamping function in the measurement utility, called by all consumers. No consumer clamps locally.
DE2
Hardcoded string literals for preference keys
Silent misspelling produces a silent read-default rather than a crash. No compiler catches it. The fix: one constants file, every key declared by name, all consumers reference by name.
DE3
Duplicating broadcast construction across senders
Multiple senders constructing the same broadcast type independently diverge on field names, field presence, and default values. The fix: one sender class owns construction of each broadcast type.
DE4
Per-consumer window management
Each consumer implementing its own add/remove/update logic rediscovers the non-obvious platform constraints independently. The fix: one window manager owns the lifecycle of all managed overlay windows.
DE5
Reading preference values inline in consumers
Multiple consumers reading the same key with different default values diverge when the key is absent. The fix: one typed accessor per key in the constants file; default value declared once.
DE6
Shared mutable state in static fields
Static fields shared between services and activities produce race conditions and lifecycle coupling impossible to reason about. State crosses component boundaries through intents, callbacks, or persistent storage — never through static fields.
DE7
Recreating windows during active operations
Calling addView() during an active operation triggers platform system events that may stop the operation. Window creation is guarded: created before the operation starts or after it ends, never during.
DE8
Floating point position storage as integers
Storing floating-point coordinates as integer preference values loses sub-pixel precision and introduces rounding drift that accumulates. Store as floats.
DE9
Multiple independent copies of display metrics
Multiple classes computing statusBarHeight, contentHeight, or screenDensity independently produce inconsistent results at boundary conditions. One measurement utility, canonical implementations only.
DE10
Conditional flag toggling across call sites
Adding and removing flags in different places across the codebase produces timing-dependent flag state impossible to audit. Flag state transitions live in the window manager, triggered by callbacks, not by consumers directly.
DE11
Launch-time position as runtime position
A service that captures position at launch and uses it for its entire lifecycle ignores all runtime updates. The fallback path reads from persistent storage, which reflects the most recent position regardless of which component updated it.
DE12
Unconditional save in cleanup methods
A cleanup method that saves state unconditionally saves stale defaults when called on an unattached instance. Attach guard at the top of any method that writes shared state as a side effect.
DE13
Per-mode initialization of shared settings
Applying mode-specific defaults to a shared setting on every mode switch overwrites the user’s choice on every transition. Defaults apply once at first launch. Shared settings have one value, applicable to all modes.
DE14
Dead code that implies an architecture that does not exist
Unused methods that suggest per-mode or per-component storage cause future developers to build on an implied design that has no implementation. Delete dead code immediately. The existence of a method implies it is called.
DE15
Two write paths for the same value
Memory-only write path plus storage-only write path for the same value guarantees divergence. Neither path can be made correct independently. One atomic setter performs both writes; no other code writes either medium for this value.
DE16
Sync triggered by side effects rather than explicit sync calls
Relying on an observer, broadcast, or lifecycle callback to keep value copies in sync produces coupling that breaks silently when the triggering event is missed. Explicit sync calls at defined resume points and after cross-component operations.
DE17
Trusting a component’s in-memory copy after another component may have written
When a background service may have updated persistent storage while the UI was paused, the UI’s in-memory fields are stale. Explicit sync at every resume point.
DE18
Using the same callback interface method for two distinct events
Overloading a callback method with a flag parameter to distinguish two event types causes consumers to implement branching logic that belongs in the manager. Separate callback methods for separate events.
DE19
Using display height instead of content height for Y-axis bounds
Display height includes the status bar. Content height excludes it. An overlay clamped to display height allows positioning behind the status bar, where it is invisible and unreachable. One canonical content height function in the measurement utility.
DE20
Multiple overlay windows for a single logical visual element
Separating logically related visual components into separate overlay windows introduces compositor synchronization problems and makes coordinate management dependent on frame timing. One window, one onDraw() pass, all synchronized content drawn together.
DE21
Injecting gesture events while an overlay window is visible
Gesture injection and overlay window presence interact in non-obvious platform-specific ways. These constraints must be discovered once and owned by the window manager. Code that injects gestures must call the window manager to manage window state, not manage it directly.
DE22
Storing window handles in consumer classes
A consumer storing a direct reference to a window manager window handle has taken ownership of a resource it did not create. If the window manager recreates the window, the consumer’s reference is stale. All window handles are owned by the manager that created them.
DE23
Resizing an overlay window surface during live user interaction
Calling updateViewLayout() with new width or height on an overlay while it is visible produces a diagonal positional artifact equal to |oldHalfSize − newHalfSize| px in both axes, on every resize call. The artifact is in the system compositor, not in application code. Fix attempts that do not resolve this: correcting coordinate calculations, changing call ordering, reading from measured view size, using a single window, applying ICVU to window dimensions. The only resolution: never change window dimensions after addView(). See AX12 and Section 2.7.
Added in Update 2
DE24
Using two overlay windows to display content that must appear synchronized
Maintaining two separate overlay windows and updating both through sequential updateViewLayout() calls does not guarantee they appear synchronized. The system compositor composites each window’s surface independently. A frame can be rendered with one window updated and the other not yet updated. This is structural, not a timing or ordering problem. The only resolution: merge synchronized visual elements into a single window with one onDraw() pass.
Added in Update 2
4.1 — Building a New Project
Step 1
Foundation first.
Before writing any feature code, create the constants file and the measurement utilities. These have no dependencies and never change shape.
Step 2
Identify all inter-component messages.
List every broadcast, notification, event, or callback that crosses a component boundary. For each, create the data container (Category B) and the sender class (Category C).
Step 3
Identify all shared resources.
List every resource that more than one component will access. For each, create or designate a manager (Category C) that owns its lifecycle.
Step 4
Identify all shared values.
List every value that will be written by more than one component. For each, classify it as an ICVU value, define its atomic setter and sync-from-storage method, and deliver them before the second writer is ever written.
Added in Update 1
Step 5
Define callback interfaces.
For each manager, define what consumer-specific behaviour it needs to trigger. Write the interface before writing the manager.
Step 6
Implement managers against their interfaces.
Implement each manager with the callback interface as its only consumer-facing dependency.
Step 7
Wire consumers last.
Implement Category E classes last. They delegate to managers and handle only what is genuinely their own concern.
4.2 — Rules for Adding New Functions (F1–F11)
F1
Start at the bottom of the layer graph. Data containers first, then managers, then consumer wiring. Never write consumer code that depends on a manager that does not yet exist.
F2
One delivery per layer. Foundation utilities first, then logic classes, then managers, then consumer wiring. Every build is green and every layer independently verifiable.
F3
New concern = new manager class. If new functionality requires owning a new resource, a dedicated manager class is created for it.
F4
New inter-component messages go through the sender class. Any new message is added as a method to the sender class before any consumer sends it.
F5
New persistent keys are declared in the constants file before use. The constant and typed accessor are delivered in the same fix as the first code that reads or writes the key.
F6
New window management follows the established manager pattern. New overlay window lifecycle is added to the appropriate window manager class — never implemented inline in a service.
F7
Callback interfaces are extended, not bypassed. New behaviour needed from a consumer is added to the callback interface with a default no-op implementation.
F8
Document before implementing high-risk changes. Any change touching window managers, coordinate space, gesture injection, or platform lifecycle requires a pre-implementation evaluation document.
F9
Verify the layer graph is acyclic after each addition. After adding a new class, confirm it does not introduce a circular dependency.
F10
No manager holds a reference to a UI owner class. Managers accept callbacks for consumer-specific behaviour. They never hold Activity or Service references.
F11
Before introducing a second writer to any value, apply ICVU first. Classify the value as ICVU, define its atomic setter, migrate the existing writer, and deliver all three in the same commit as the new writer. Two write paths that are not coordinated will diverge.
Added in Update 1
5.1 — Testing This Architecture
Layer 1 — Foundation utilities: pure unit tests.
Category A functions have no state and no side effects. Every edge case should be exercised. These tests never require mocking.
Layer 2 — Data containers: construction and validation tests.
Category B containers should be tested for all valid construction paths, all Builder validation failures, and all copy paths.
Layer 3 — Complex managers: behaviour tests with mock callbacks.
Category C managers accept callback interfaces, making them testable with mock implementations. Create the manager, inject a mock callback, exercise the public API, verify both state changes and callback invocations.
Layer 4 — UI owners: integration or manual tests.
Category E classes depend on platform UI rendering. Where automation is available, use it. Where it is not, define a manual verification checklist.

The test gap indicator: If a function cannot be tested without instantiating a Category E class, the function is in the wrong place. Extract it to a lower layer.

5.3 — On the Evolution of Rules
An architectural rule, when applied to a domain for which it was not designed, may produce correct behaviour in the domain it governs and incorrect behaviour in the adjacent domain it does not govern. The evidence of this situation is: following the rule correctly makes something worse.

When this occurs, the correct response is: (1) do not abandon the rule; (2) define the domain boundary of the rule explicitly; (3) identify the adjacent domain and find or create the governing rule for it; (4) document both the rule and its boundary in the Codex.

This Codex has now accumulated two updates. The ICVU rule (Update 1) was correct and applied broadly. Applied to overlay window dimensions, it produced correct values but worsened visual behaviour through increased compositor write frequency. Update 2 defines the domain boundary of ICVU and adds AX12 to govern the compositor domain. Both rules — ICVU and the Fixed Surface Rule — are now in the Codex with their domains defined. Neither one alone is sufficient. Both are correct.

5.5 — A Note on Emergent Architecture

The rules in this document were not designed in advance. They were extracted from working software after observing what broke and why. What good architecture actually requires is systematic observation and extraction. The developer who watches which functions get copied, which constants get misspelled, which interface boundaries collapse under load — and then responds by extracting, formalising, and enforcing — will produce better architecture than the developer who designs the perfect system in advance and struggles to fit reality into it.

Theory produces elegant architecture. Practice produces correct architecture. The gap between them is smaller than it appears, but it only closes through observation — not through reasoning alone.

Appendix A — Where Does This Function Live?
Is it a pure computation with no state?
  → Category A. Lives in the foundation utilities file.

Is it a group of related fields that travel together?
  → Category B. Lives in a dedicated data container class with a Builder.

Does it own a resource lifecycle, needed by multiple consumers?
  → Category C. Lives in a dedicated manager class.

Does it wrap a platform API to hide its complexity?
  → Category D. Lives in a dedicated bridge class.

Does it respond to user input or own a visible surface?
  → Category E. Lives in an activity, fragment, or service.

Does it identify a persistent value or inter-component message?
  → It is a constant. Lives in the constants file. Always.

Is it a value written by more than one component?
  → It is an ICVU value. It gets an atomic setter and sync-from-storage method.
    [Added in Update 1]

Does this code call updateViewLayout() with new width or height while visible?
  → It violates AX12. Size the window at addView() time to the maximum content
    size. Only pass position changes to updateViewLayout() after that.
    [Added in Update 2]
Appendix B — Is This a Violation?
A function appears in more than one class?
  → Violation of AX1. Extract to the appropriate category.

A manager imports a concrete consumer class?
  → Violation of AX2. Introduce a callback interface.

A string literal identifies a key or action in more than one file?
  → Violation of AX3. Declare in constants file and reference by name.

A consumer builds its own inter-component message inline?
  → Violation of AX4. Add a method to the appropriate sender class.

A value is written in one place without updating persistent storage?
  → ICVU anti-pattern 1. The value requires an atomic setter. [Update 1]

A value is written to storage without updating the in-memory field?
  → ICVU anti-pattern 2. The value requires an atomic setter. [Update 1]

updateViewLayout() is called with new width or height while visible?
  → Violation of AX12. Resize only at addView(). Use invalidate()
    for content changes. [Update 2]

Two overlay windows kept in sync through sequential updateViewLayout calls?
  → Dead End DE24. Merge into a single window. [Update 2]
Appendix C — Glossary (key terms)
Atomic setter
A method that updates both in-memory and persistent storage for a given ICVU value in a single call. The only place in the codebase where both writes occur together.
Compositor-layer artifact
A visual defect caused by the system compositor’s handling of a surface reallocation event, not by application code. Cannot be diagnosed or fixed at the application layer.
Divergent duplication
The failure mode where copies of the same function diverge over time due to independent modification, eventually producing inconsistent behaviour across consumers.
Fixed Surface Rule (FSR)
The pattern for window manager overlay windows whose content changes size at runtime. The window is sized at addView() to the maximum content dimensions and never resized after that. [Added in Update 2]
ICVU value
A value written by more than one component that must remain consistent across in-memory and persistent storage at all times. Governed by the ICVU pattern and AX11. Domain: application-layer values only. [Added in Update 1]
Multi-copy state divergence
A bug class where the same logical value exists as independent copies in multiple storage media without a protocol to keep them synchronized. The bug appears to “move” between code paths because each fix corrects one copy without addressing the others. [Added in Update 1]
Singular ownership
The property of an architecture where each piece of logic, each constant, and each resource is owned by exactly one class.
Surface reallocation
The process by which the system compositor responds to a window dimension change by allocating a new backing buffer at the new size. The reallocation and the next completed draw are decoupled across frames, producing transient positioning artifacts proportional to the size delta. [Added in Update 2]
Live Updates

Pending Updates

Updates submitted by contributors that have not yet been integrated into the main Codex body. Visually distinct from the integrated content above.

No pending updates

All submitted updates have been integrated into v1.1.2.
New submissions will appear here before integration.

Version History

Past editions

A full record of every edition and integrated update. Older versions are not discarded — rules survive recodification unless explicitly retired with a documented rationale.

v1.1.2 CURRENT 2026-03-26
Full recodification — Update 2 integrated
Added AX12 (Overlay window dimensions fixed at addView() time) and the Fixed Surface Rule pattern (Section 2.7). Defined the ICVU domain boundary. Added Dead Ends DE23 and DE24. Added Section 5.3 “On the Evolution of Rules” — the first documented instance of rule-scoping in the Codex, naming the Update 1 → Update 2 pattern as expected Codex behaviour.
AX12 FSR pattern ICVU domain boundary DE23 DE24 Section 5.3
v1.1.1 2026-03-25
Update 1 integrated — ICVU pattern
Added Premise P8 (in-memory and persistent state are not automatically synchronized). Added AX11 (every value written by more than one component follows ICVU). Added Rule F11 (apply ICVU before introducing a second writer). Documented the ICVU pattern in full (Section 2.6) and the multi-copy state divergence bug class.
P8 AX11 F11 ICVU pattern Section 2.6
v1.0 2026-03-25
Base Codex — initial public edition
Function classification (Categories A–E), layer graph, axioms AX1–AX10, premises P1–P7, grouping rules, management rules, creation rules, rules for new function creation (F1–F10), recognised exceptions E1–E5, and the Dead End Registry DE1–DE22. Framework extracted from production software development.
AX1–AX10 P1–P7 DE1–DE22 Layer graph Categories A–E

Versioning scheme: major.recodification.updates_in_cycle — the third digit increments with each integrated update; the second digit increments and the third resets to zero when updates are consolidated into a full recodification.

Contribute

Submit a discovery

NACODEX is a living document. If you’ve found something that belongs here — a pattern, a dead end, a scope boundary — submit it. The Codex grows through production experience, not theory.

1

Check whether your discovery qualifies

A NACODEX update documents a structural discovery — a finding about how software systems behave architecturally, not just a bug fix. It must be generalisable: someone who wasn’t there when you found it must be able to apply it.

Pattern

A reusable solution structure to a class of problems. Has a name, a defined application condition, and a defined implementation form.

Dead End

An approach that is structurally incapable of solving the problem. You can explain why it cannot work — not just that it failed.

Axiom / Premise

A factual observation about software systems of this type that, if violated, reliably produces a specific class of failure.

Scope Boundary

A condition under which a previously documented rule produces incorrect results because it was applied outside its domain.

If you are unsure whether your discovery qualifies: err on the side of submitting. Low-confidence suggestions are marked as such. The review process handles filtering.

2

Run the NACODEX closing routine

Download the Contribution Guide below and upload it at the start of your next AI-assisted development session. When your session ends, send the message: “Run NACODEX closing routine.”

Your AI assistant will conduct a structured interview (4–6 questions) and produce a formatted file: NACODEX_Update_Suggestion.md. The guide contains the full interview structure, output template, and an instruction block addressed directly to the AI assistant.

3

Send the file by email

Attach NACODEX_Update_Suggestion.md and send to nacodex@pukapasoft.xyz. Subject line format: NACODEX Suggestion — [Type] — [One-line description]

Suggestions are reviewed against the current public edition. Integrated suggestions are acknowledged by reply with a reference to the update number. There is no timeline commitment for review — this is a small-scale, best-effort project.