Monday, 9 February 2026

MVPL AI-first coding beyond the basics

 In some of my previous posts I explained the concepts of the Minimum Viable Product Lines (MVPL) approach to writing software, and I applied the simplest aspects of this approach to a very basic calculator app which I can run in a browser on my iPad. I also touched on the way AI-first coding might be well suited to applying MVPL coding governance to ensure multiple versions of the same product line can be developed over time for different customers with their different paths of development. 


Now I have decided to explore how ongoing development of my basic app might happen over time in a real software project. In particular, I want to look at how the core can gain increasing maturity, to support various kinds of extensions. 


There is a concept in product, or product line, development where the various directions it might go, the various ways to change the product, are seen as its ‘dimensions of variability’.  These can be understood as forming a geometry for the product. Each direction of possible development, past and future, is regarded as an axis. 


In MVPL, every customer can have their own set of axes along which they can be provided with one or more extensions to cater for their version of the product line. They might choose to adopt several existing extensions too. Ideally their extensions can be discarded easily without breaking the product, so that the extensions can be treated experimental. The core usually remains unchanged by any customer’s requirement. Over time the core might actually change, but in ways necessary for the product itself, rather than for individual customers. It can evolve, and this evolution might improve the geometry of the dimensions of variability, supporting more kinds of extension for customers. 


I asked AI to suggest ways the core of my calculator app might mature in stages, to support more axes of the geometry of its dimensions of variability. In this way its possibilities for extension would improve with time. I also emphasised that any new, but differing, requirements of more and more customers must be accommodated over time, and the core code should be able to cater for this without becoming ‘brittle’. (‘Brittle’ code is code that is hard to change without breaking the product for existing requirements.) For this, each customer could be given an extensions manifest, listing their extensions, those enabled and those disabled. AI suggested two initial stages of development of the basic core. Then in a third stage, more advanced management of the extensions could be introduced. 


The first two stages could be coded by AI from a single prompt. This worked well, giving me a working calculator,  able to support future changes in different ways for different customers via a profile manifest for the extensions for each customer. 


The third stage could also be developed using AI, passing in the core code, along with a single prompt. 


The AI note, however, that even the third stage it was proposing had some features too advanced to be implemented this simply. They required more than one prompt and would involve some serious computer science research work. It proposed several further stages too, but even more advanced, and not very feasible for a mere Javascript/HTML single file app. 


Below are two prompts: The first is for basic stages 0 and 1, while the second prompt is for the more advanced stage 2. The prompts were generated by ChatGPT and are designed to be pasted into a ChatGPT chat. Either one chat can be given both prompts in turn, or the second prompt along with the core code can be pasted into a separate chat.



Prompt 1 



You are Codex. Generate a small MVPL teaching project for a browser-based calculator (HTML + JavaScript only, no external libraries) that is friendly to editing on an iPad (e.g., Textastic). The goal: produce a single-file core (index.html) plus two separate extension files (percentage.js and inverse.js). The core must implement Stage 0 (Stable Core Foundation) and Stage 1 (Customer Profiles & Config — solve the second-customer problem early).


IMPORTANT: Return exactly three code blocks and nothing else. Label them exactly as:


--- index.html ---

[full file contents here]


--- percentage.js ---

[full file contents here]


--- inverse.js ---

[full file contents here]


Do NOT include any extra messages, commentary, or explanations outside those three code blocks.


--------------------------------------------------------------------------------

FUNCTIONAL & ARCHITECTURAL REQUIREMENTS (must be implemented and clearly commented)

--------------------------------------------------------------------------------


1) Core (index.html) responsibilities (all inline in index.html):


   CORE CALCULATOR FEATURES (Stage 0 — Stable Core Foundation)

   -----------------------------------------------------------

   • Addition (+)

   • Subtraction (−)

   • Multiplication (×)

   • Division (÷)

   • Decimal number input

   • Equals (=)


   • CANCEL / CLEAR SUPPORT (REQUIRED)

     The calculator must include a clearly visible cancel/clear button.

     The button must behave as a full reset (AC / All Clear), meaning:

       - Clears current display value

       - Clears any stored expression

       - Resets calculator state to initial ready state

     The cancel/clear capability must be implemented in the stable core, NOT as an extension.


   • Stable state management and display rendering.


   • Touch-friendly UI:

       - Large buttons

       - Comfortable spacing for thumb input

       - Works well on iPad in portrait and landscape


   • Keyboard input support (desktop and iPad keyboard).


   • Prevent invalid states:

       - Multiple decimal points in a single number

       - Divide-by-zero handling

       - Invalid operator sequences


   • Minimal EventBus (lightweight lifecycle hooks allowed).


   • Minimal ExtensionRegistry that exposes a safe API for extensions to register.


   • Extension manifest support and customer-scoped enable/disable behavior.


   • Namespaced per-customer extension state storage (each customer must have isolated extension data).


   • A small UI widget for switching the active customer profile.


   • Sample built-in customer profiles for demonstration:

       - customer "alice": enabledExtensions: ["percentage","inverse"]

       - customer "bob": enabledExtensions: ["percentage"]


   • When switching customers:

       - Extension buttons must update immediately

       - Per-customer namespaced extension state must remain isolated


   • Extension loading approach:

       - Include script tags for percentage.js and inverse.js at the bottom of index.html.

       - ExtensionRegistry.register(...) must accept an extension object containing:

           - manifest

           - init(api)

       - Registry must decide whether to initialize extension based on active customer profile.


   • Provide runtime API to enable/disable extensions for the currently active customer.


   • Include clear in-code comments marking:

       - CORE

       - MVPL teaching notes

       - Brief explanation of how customer-scoped extension enable/disable prevents the second-customer problem



2) Extension interface (what each extension file must use):


   Each extension must call:


   ExtensionRegistry.register(extensionObject)


   Where extensionObject has the form:


   {

     manifest: {

       name: "extensionName",

       version: "1.0",

       description: "...",

       dimension: "behavior"

     },

     init(api) { ... }

   }


   The init(api) function must receive a constrained API containing:


       • addButton(config)

           config: { label, className, onPress }


       • getProfile()

           Returns active customer profile object.


       • state(namespace)

           Returns namespaced per-customer extension state.


       • display

           Method or reference allowing extension to update display safely.


   Extensions MUST NOT directly mutate core state or core modules.

   All interaction must occur through the provided API.



3) Required demonstration extensions:


   percentage.js

   -------------

   • Adds "%" button.

   • Divides current displayed value by 100.

   • Uses only extension API.

   • Any stored data must be namespaced per-customer.


   inverse.js

   ----------

   • Adds "1/x" button.

   • Replaces current displayed numeric value with inverse.

   • Must safely handle divide-by-zero and invalid input.

   • Uses only extension API.

   • Any stored data must be namespaced per-customer.



4) Customer profiles & configuration:


   Profiles must support:


   {

     id: "alice",

     label: "Alice",

     enabledExtensions: ["percentage","inverse"],

     extensionConfig: { }

   }


   UI must include:


   • Profile switch dropdown

   • Switching profiles must:

       - Update active extensions

       - Preserve isolated extension state

       - Demonstrate second-customer-safe behavior


   • Provide small UI buttons allowing toggling extensions on/off for the active customer.



5) UI & UX Requirements:


   • Responsive layout suitable for iPad Safari

   • Large touch-friendly buttons

   • Clean readable display area at top

   • Extension buttons visually distinct via .extension CSS class

   • Visible small status area displaying:

       - Active customer

       - Enabled extensions list



6) Code Quality & Teaching Comments:


   Strictly separate and label sections:


   • CORE MODULES

   • EXTENSION INFRASTRUCTURE

   • CUSTOMER PROFILE SYSTEM

   • UI RENDERER

   • BOOTSTRAP


   Each extension file must be clearly labeled:


   • EXTENSION IMPLEMENTATION


   Comments must briefly explain:

   - Why core remains stable in MVPL

   - Why extension manifests exist

   - Why per-customer extension toggling prevents the second-customer problem


   Maintain readability over cleverness.

   No frameworks.



7) Testing Instructions (must be included as comments in index.html only):


   Provide short developer instructions explaining how to:


   • Open file on iPad using Textastic or Safari

   • Switch customer profiles

   • Use cancel/clear button

   • Verify extensions differ per customer

   • Verify extension state isolation



--------------------------------------------------------------------------------

OUTPUT FORMAT (STRICT)

--------------------------------------------------------------------------------


Return exactly three code blocks labeled as shown below (no extra text):


--- index.html ---

[put the entire HTML file contents here]


--- percentage.js ---

[put the single JS extension file contents here]


--- inverse.js ---

[put the single JS extension file contents here]




Prompt 2


You are Codex.


You are given an existing MVPL teaching calculator core implemented in a single index.html file. That file already supports:


• Stage 0 — Stable Core Foundation

• Stage 1 — Customer Profiles & Config

• Extension registry with manifest + init(api)

• Per-customer enable/disable extension support

• Namespaced extension state


Your task is to EXTEND the architecture to implement Stage 2 MVPL capabilities WITHOUT breaking existing behavior.


--------------------------------------------------------------------------------

CRITICAL RULES

--------------------------------------------------------------------------------


• DO NOT rewrite or remove existing Stage 0 or Stage 1 behavior.

• Maintain backward compatibility with existing extensions.

• Only extend registry, manifest schema, and profile config capability.

• UI layout must remain substantially unchanged.

• Extension API must remain backward compatible.

• Keep code lightweight and framework-free.

• Keep everything browser-only HTML + JavaScript.


--------------------------------------------------------------------------------

STAGE 2 FEATURES TO IMPLEMENT

--------------------------------------------------------------------------------


The system must now support:


1) Dependency Detection

2) Conflict Detection

3) Extension Load Ordering

4) Feature Bundles

5) Runtime Validation Warnings


--------------------------------------------------------------------------------

EXTENSION MANIFEST ENHANCEMENTS

--------------------------------------------------------------------------------


Enhance extension manifests to support optional metadata:


manifest: {

  name: string (required)

  version: string

  description: string

  dimension: string


  requires?: string[]

  conflicts?: string[]

  priority?: number

  bundleTags?: string[]

}


Rules:


• requires lists extension names that must load first

• conflicts lists extensions that cannot coexist

• priority is a numeric load ordering hint (higher loads later)

• bundleTags identifies bundles that include this extension


Backwards compatibility:

• Existing extensions without these fields must still work


--------------------------------------------------------------------------------

REGISTRY BEHAVIOR EXTENSIONS

--------------------------------------------------------------------------------


Modify ExtensionRegistry to support:


### 1 — Dependency Graph Resolution


Before initializing extensions:


• Build dependency graph of enabled extensions

• Detect missing dependencies

• Detect circular dependencies

• Prevent initialization of invalid extensions

• Emit console warnings explaining problems


### 2 — Load Ordering


Extensions must initialize in this order:


1. Dependency order (topological sort)

2. Then priority ascending


### 3 — Conflict Detection


If extension A conflicts with extension B:


• Prevent A from loading

• Log warning explaining conflict


### 4 — Safe Partial Loading


If one extension fails validation:


• Other valid extensions must still load


--------------------------------------------------------------------------------

FEATURE BUNDLE SYSTEM

--------------------------------------------------------------------------------


Add bundle support to core.


Core must define:


const FeatureBundles = {

  "scientific": ["inverse"],

  "finance": ["percentage"],

  "advanced": ["percentage", "inverse"]

};


Profiles may now include:


enabledBundles: string[]


Runtime must:


• Expand bundles into enabledExtensions

• Avoid duplicates

• Allow extensions to also be enabled individually


--------------------------------------------------------------------------------

PROFILE SYSTEM CHANGES

--------------------------------------------------------------------------------


Profiles must support:


{

  id,

  label,

  enabledExtensions,

  enabledBundles

}


Switching profiles must:


• Recalculate extension set

• Revalidate dependencies/conflicts

• Reload extensions safely


--------------------------------------------------------------------------------

EXTENSION API CHANGES

--------------------------------------------------------------------------------


Extend API object passed to extensions with:


api.getEnabledExtensions()

api.isExtensionEnabled(name)


These must reflect final resolved extension state.


--------------------------------------------------------------------------------

RUNTIME UI FEEDBACK

--------------------------------------------------------------------------------


Add a small developer status area showing:


• Active extension load order

• Any dependency or conflict warnings


Keep UI minimal and unobtrusive.


--------------------------------------------------------------------------------

ALGORITHM REQUIREMENTS

--------------------------------------------------------------------------------


Implement:


• Topological dependency sort

• Circular dependency detection

• Conflict filtering

• Bundle expansion logic


Keep algorithms readable and well commented for teaching purposes.


--------------------------------------------------------------------------------

EXAMPLE EXTENSIONS (REQUIRED OUTPUT)

--------------------------------------------------------------------------------


You must create two extension files demonstrating Stage 2.


------------------------------------------------------------

percentage.js

------------------------------------------------------------


Must include:


manifest: {

  name: "percentage",

  version: "1.1",

  dimension: "behavior",

  bundleTags: ["finance","advanced"],

  priority: 10

}


Behavior:

Adds "%" button dividing current display by 100.


------------------------------------------------------------

inverse.js

------------------------------------------------------------


Must include:


manifest: {

  name: "inverse",

  version: "1.1",

  dimension: "behavior",

  requires: ["percentage"],

  bundleTags: ["scientific","advanced"],

  priority: 20

}


Behavior:

Adds "1/x" button with divide-by-zero protection.


This demonstrates dependency ordering.


--------------------------------------------------------------------------------

TEACHING COMMENT REQUIREMENTS

--------------------------------------------------------------------------------


Code must include short comments explaining:


• Why dependency graphs protect MVPL core stability

• How conflict detection prevents unsafe feature combinations

• Why bundles support product-line configuration

• How Stage 2 helps prevent second-customer problems at scale


--------------------------------------------------------------------------------

OUTPUT FORMAT

--------------------------------------------------------------------------------


Return EXACTLY three code blocks and nothing else:


--- index.html ---

(Full updated file)


--- percentage.js ---

(Full extension file)


--- inverse.js ---

(Full extension file)


No explanations outside code blocks.


--------------------------------------------------------------------------------

SUCCESS CRITERIA

--------------------------------------------------------------------------------


After implementation:


• Switching profiles must change enabled extensions

• Bundles must activate correct extensions

• Dependency ordering must work

• Conflict detection must block incompatible extensions

• Existing Stage 1 extensions must remain compatible

• Calculator arithmetic must still function


--------------------------------------------------------------------------------

END OF TASK

--------------------------------------------------------------------------------

No comments:

Post a Comment