# The IS Discipline
## How to build a database-backed web app that stays correct

This is the HOWTO the IS system implies. It is five rules. They are not about PHP --
PHP is just what this implementation emits. The rules are what make a generated app
stay right while people (and AIs) keep changing it.

If you read nothing else: **declare once, derive everything, edit only in the hooks,
gate every release, keep one source of truth.**

---

## 1. Declare the structure once

One file says what your data IS: a table, its columns, each column's type, how each
should be entered. Nothing else in the system gets to have an opinion about it.

In IS that file is `APP/TBL.php` -- the `$is` array. It is a SPEC, not code: a list of
columns, not a set of instructions. That distinction is the whole reason the rest works,
and it is why the spec is the right thing to hand an AI (see HOWTO-AI.md): describing a
table is a job a language model does reliably; writing a whole app by hand, identically,
twice, is not.

**Why it matters:** the moment two files both claim to know your column list, they will
disagree, and the disagreement will surface as a bug in the third file that trusted both.

## 2. Derive every layer mechanically -- and never edit what was derived

From the one spec, generate all of it: the SQL, the server API, the client API, the page.
`make`. Then treat every generated file as *output*: read it, debug through it, never
save an edit into it.

When a generated file is wrong, the template that produced it is wrong. Fix the template
and regenerate -- every app gets the fix at once.

**Why it matters (measured):** in July 2026 this codebase had five latent
crash-on-every-form defects, and the reason they were fixable in an afternoon rather than
a month is that each lived in ONE template. The same week proved the converse: a second,
hand-diverged copy of the templates had kept its own bugs for two years because nobody's
fix reached it.

## 3. Customize only in the hooks

A generated app needs to become YOUR app. So the generator leaves you designated places
it will never overwrite: a stylesheet, a script file, the menu bar. Put every local
decision there.

In IS: `APP.css`, `APP.js`, `lbar.php`. Regeneration does not touch them (their make
targets have no prerequisites, so an existing file is left alone -- verified by md5
across a regeneration).

**Why it matters:** without hooks, customizing means editing derived files, which breaks
rule 2, which costs you rule 2's benefit. The hooks are what make "never edit the output"
a livable rule instead of a pious one. Example: the Phonetic Reductions app's IPA symbol
palette is 120 lines in `reduction.js` and survives every regeneration.

## 4. Gate every release with a check that can fail

Before you ship the generator, have it build a throwaway app and prove things about it:
every file the page asks for exists; the syntax parses; the known failure patterns are
absent; no credential is baked in; no debug popup survived.

Then make releasing DEPEND on that check. In IS: `selftest.sh`, and `make distrib`
requires it.

**A check that cannot fail proves nothing.** Test the test: run it against a known-bad
version and watch it fail. Ours fails the previous release with six findings and passes
the current one. It has since caught real mistakes -- an unguarded loop I had missed in a
second location, and a credential literal in its own fixture.

**Why it matters:** a generator's bugs are multiplied by every app it has generated. The
gate is the only thing standing between one careless template edit and every app breaking
at once.

## 5. One source of truth, and back out before you overwrite

Exactly one copy of the templates is authoritative. Other copies get retired to `old/`
with a pointer, never left to drift. Before overwriting any live file, copy it aside.
Version the distribution; never mutate an already-released artifact.

**Why it matters:** two authoritative copies is the same failure as rule 1's two column
lists, one level up.

---

## The secrets rule (it belongs to rule 1, and it bites hard)

Credentials are NOT part of your information structure. Put the database password in ONE
file OUTSIDE the web-served directories and have the spec call a function for it. Never a
literal in the app directory, never baked into generated output.

The reason is sharper than tidiness. Web servers execute `.php` but serve nearly
everything else as PLAINTEXT. So `MyApp.php` is safe while `MyApp.php~` -- the backup
your editor wrote without asking -- hands the file to anyone who requests it. On this
estate that was not hypothetical: ten public URLs were disclosing a live database
password from editor backups and helper scripts of old apps. Executable files leaked
nothing; their innocent-looking siblings leaked everything.

So: one secrets file, outside the docroot, `chmod 640`, with a guard that 404s a direct
request. Edit it by script, never with an editor -- an editor leaves a backup, and the
backup is the leak. And add the check to your gate (rule 4) so the mistake cannot return.

---

## What this discipline is NOT

It is not a framework, and IS must never become one. The whole system is ~2,900 lines
with no dependency beyond `make`, `php`, and a database. Smallness is the feature: you
can read all of it in an afternoon, which means you can trust it, which means you can fix
it. The day it needs a package manager it has died of success.

It also does not aspire to generality it hasn't earned. IS does single-table (and, with
one spec per table, several tables) SCRUD apps. That's a real, common, useful shape -- a
record of things, searchable, editable. If you need joins, aggregates, and a query
planner, you need a different tool, and saying so plainly is part of the discipline.

## Where this pays off

- **You get an instrument, not a project.** A question you want evidence about ("which
  phonetic reductions do I actually hear?") becomes a working collector in an afternoon,
  on your own host, with your data in your own database. No SaaS, no subscription,
  nobody holding your rows.
- **Your app survives its own maintenance.** Six months later, a fix lands in one place
  and propagates; a release cannot ship broken.
- **An AI can build it FOR you, safely.** Not by writing an app freehand and hoping, but
  by writing a spec that a deterministic generator turns into the same correct app every
  time, with a gate that refuses the plausible-but-broken. Rules 1-4 are exactly the
  properties AI codegen lacks on its own. See HOWTO-AI.md.
