GOLDEN_RULES_addition.md

Standard App Structure — v2 (validated against real folder tree, piloted on `defapp`)



`defapp` was the old standard. This version corrects it against what's actually
in production across `register`, `aiwizard`, and `aicode`, and is being tested
by updating `defapp` itself before rolling out further.

Mandatory per-app skeleton



```
{appname}/
├── {appname}.index.php # entry point, dot-segment naming
├── public/ # web-servable root ONLY — everything else stays outside webroot
├── admin/ # admin views/routes for this app
├── pages/ # app pages/routes
├── templates/ # shared layout templates (NOT "tmpl" — corrected from v1 draft)
├── config/
│ └── templates/ # config file templates
├── css/
│ └── themes/ # theme overrides (integrates with system-tier theme-manager)
├── js/
├── img/
├── php/ # app-specific PHP includes/logic
├── data/
│ ├── config/
│ ├── fonts/
│ ├── logs/ # logs live HERE, not a top-level logs/ — corrected from v1 draft
│ └── sql/
├── docs/
│ ├── adr/ # architecture decision records (from aiwizard)
│ └── runbooks/ # from aiwizard
├── ai/
│ ├── prompts/
│ └── output/
├── audit/ # app-specific audit checks (declared mandatory per app now)
├── tests/ # NOT "test" — corrected from v1 draft
├── scripts/ # one-off/maintenance scripts for THIS app
├── tools/ # maintained reusable CLI for THIS app
├── tmp/ # scratch/ephemeral, gitignored
└── var/
└── sessions/ # runtime state (PHP sessions, from register)
```

Correction from v1: `scripts/` and `tools/` are mandatory per-app (not
system-only as originally proposed) — real apps already keep app-specific
scripts/tools alongside system-wide ones. Both tiers get both folders; the
placement rule (below) decides which specific file goes where.

Optional: `src/{Controllers,Helpers,Parser}/` (or app-specific namespaces
like `aicode`'s `Database/Diagnostics/Logging/Models`) — only for apps that use
OOP/namespaced classes rather than the procedural style. Not every app needs this.

Optional declared extension zones (per app, audited only if declared)



```
{appname}/
├── api/ # REST API layer (family-tree)
├── python/ # non-PHP runtime — venv itself is gitignored, never committed
├── assets/ # uploads, photos, exports (family-tree)
└── themes/ # only if beyond the standard css/themes/ override
```

System tier — same mandatory-skeleton logic, system-wide scope



```
system/
├── .env # golden rule: single source of truth for system-wide config
├── config/
├── data/
│ └── project.registry.json
├── logs/
├── audit/ # cross-app compliance checks — mandatory at this tier
├── tests/ # tests the shared tooling itself — mandatory at this tier
├── tools/
│ └── theme-manager/ # shared across apps — moved here, not a peer app
├── scripts/
├── docs/
└── php/
└── aicode/ # shared library code — moved here, not a peer app
```

The single placement rule (unchanged, still the test for every file)



> If a folder's contents apply to more than one app, or govern the tooling
> system itself, it belongs under `system/`, declared as a system extension.
> If a folder's contents are specific to one app's own behavior or
> requirements, it belongs under that app, declared as an app extension.

Open item — not yet resolved by the tree alone



Where the app-level `.env` actually lives is unclear from directory names
alone (hidden files don't show in a tree listing). Confirm before the pilot:
does each app have its own `.env` under `config/`, or do all apps read only
from the system-tier `.env`? This decides whether "golden rule" is one file
or one-per-app.

Pilot: what changes in `defapp` to test this



`defapp` currently has: `admin, ai/{output,prompts}, audit, config, css/themes,
data/{config,fonts,logs,sql}, docs, img, js, pages, php, scripts, sql, src/
{Controllers,Helpers,Parser}, templates, tests, tmp, tools`.

To bring it in line with v2, add:
- [ ] `public/` — carve out the actual web-servable files from wherever they
currently sit (likely `pages/` or app root); everything else stays private
- [ ] `var/sessions/` — if defapp handles its own sessions rather than a shared
mechanism
- [ ] `docs/adr/` and `docs/runbooks/` — currently `docs/` is flat
- [ ] `config/templates/` — currently `config/` is flat
- [ ] Resolve the redundant `sql/` vs `data/sql/` — pick one (recommend
`data/sql/` only, drop the top-level `sql/`)
- [ ] Decide whether `src/` (OOP) or `php/` (procedural) is defapp's actual
pattern — right now it has both, which suggests a style that hasn't
fully committed either way

Nothing here needs `api/`, `python/`, or `assets/` — defapp has no declared
extensions, which is correct for a default/reference app.

Config scope, by cardinality (not by who edits it)



The right storage mechanism is decided by how many instances of a setting can
exist — not by who makes the change. An admin editing something doesn't make
it "admin data"; it's still scoped by who the setting affects.

| Scope | Cardinality | Storage | Example |
|---|---|---|---|
| System-wide | One instance, applies to every app | `system/data/config/` | Shared auth settings, global feature flags |
| App-wide | One instance, applies to every user of that app | `{appname}/data/config/` | `forms.json` — admin edits it once, every user sees the change from then on |
| Per-user | One instance per user (N grows with user base) | Database (`user_preferences` table), never the filesystem | A user's saved preferences, dashboard layout, notification settings |
| Per-session | One instance per active session, ephemeral | `system/data/tmp/sessions/` | Login state, CSRF token, in-progress form draft |

Rule: file location reflects who's affected by a setting, never who
made the edit. Per-user scope breaks the flat-file convention because
cardinality grows with the user base — that's a query problem, not a
folder-layout problem, so it goes in the database against the existing
`user_groups` schema (`users`, `groups`, `user_groups`, `login_logs`), not in
`data/config/`.

`user_preferences` table



Extends the existing multi-group auth schema. Key-value rows (not a fixed
column per setting) so new preference types don't require a schema migration,
and `appname` nullable so a preference can be system-wide (applies across all
apps for that user) or scoped to one app.

```sql
CREATE TABLE user_preferences (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
appname VARCHAR(64) NULL, -- NULL = system-wide for this user; else scoped to one app
pref_key VARCHAR(128) NOT NULL,
pref_value JSON NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uq_user_app_key (user_id, appname, pref_key),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
```

Lives at `system/data/sql/user_preferences.sql` since it's shared schema
across all apps, alongside the rest of the auth schema — not duplicated
per-app.