# Maintaining QuickSite Procedures for changing the engine itself — each one dormant most of the time, and exact when it is not. They live here rather than in `CLAUDE.md` so that the rules governing *every* change stay short enough to actually be read. > **This is not the contributor guide.** How to report a bug, translate, or open > a pull request is in `README.md` under *Contributing*. This file is for someone > already inside the codebase performing one of the procedures below. Read `CLAUDE.md` first — it holds the standards that bind every change (dependency-free, centralize shared logic, HTML-in-JS hygiene, the data-shape rule, and the `docs/` content-hygiene rules). > **Why these checklists are so specific.** Every layer named below was found the > hard way: a command that was routable and completely unusable because it had no > category; a deleted command that survived in an allowlist nobody remembered; a > registration layer in the admin API that mirrors nothing and had to be edited by > hand. The lists are long because the failure modes are silent. ## Contents | Procedure | Consult it when | |---|---| | [Documentation maintenance — the trigger table](#documentation-maintenance--the-trigger-table) | **Every change.** You edited a file and need to know which `docs/` file that obliges you to re-check. | | [Adding a new command](#adding-a-new-command--checklist) | You are adding a `secure/management/command/.php`. | | [Removing a command](#removing-a-command--the-checklist-is-eight-layers-and-it-is-longer-than-adding) | You are deleting one. It is a longer list than adding. | | [Release discipline](#release-discipline) | You are about to cut a git tag. | | [How to write and review a `docs/` file](#how-to-write-and-review-a-docs-file) | You are restructuring or reviewing one of the six canonical docs. | --- ## Documentation maintenance — the trigger table The six canonical docs in `docs/` (`ARCHITECTURE.md`, `ADMIN_PANEL.md`, `COMMAND_API.md`, `WORKFLOW_SYSTEM.md`, `PROJECT_STRUCTURE.md`, `DESIGN_DECISIONS.md`) drift fast. When editing the source files listed below, re-check the corresponding doc. | When you change… | Re-check | |---|---| | `secure/management/routes.php` (command count) | `ARCHITECTURE.md` §3, `COMMAND_API.md` (catalogue + count) | | `secure/management/config/roles.php` (roles or permissions) | `ARCHITECTURE.md` §3 (roles table), and `help.php`'s `authentication.role_system`: one line per role, saying what that role adds and nothing a higher role adds | | `secure/admin/functions/AdminHelper.php` (`getCommandCategories()`) | None — but **REQUIRED** for any new command that should appear on `/admin/command`. See *Adding a new command* checklist step 6. | | `public/admin/assets/js/core/storage-keys.js` | `ADMIN_PANEL.md` §6 (storage key registry) | | `secure/admin/templates/pages/preview/tools.php` — **a tool is one button, in this one row**, and `data-panel-opens` on it says its panel holds its options from the moment it is picked. **The panel's state, `preview-workspace--panel-open`, is set at every width (`preview.js` `setPanelOpen()`) and only the phone block of `responsive.css` gives it a meaning:** no script reads a width, and what asks whether the panel folds away reads the workspace's own box, `display: contents` on a phone (`panelFolds()`). The same goes for `preview-workspace--panel-fills` (the two Save forms, as Add and Edit Params fill the window on a phone) and `preview-page--rows-hidden` (the button beside Reload, a base rule at every width). **On a phone `.preview-sidebar` is always drawn:** the class shows or hides `.preview-sidebar__options`, and the bar under them, `.preview-sidebar__toggle`, stays. **The panel's width is `.preview-sidebar`'s `min(20rem, 40vw)`, its minimum too:** `preview-sidebar-resize.js` reads the minimum from the stylesheet and holds none of its own. **Move and the panel's handle run on pointer events,** so a finger drives them; `.qs-drag-locked` and the handle carry `touch-action: none`, and a change there is read under Responsive Design Mode's touch simulation | `ADMIN_PANEL.md` §8.1 (visual editor modes), §5.1 (*The three views*) | | `secure/src/classes/JsonToHtmlRenderer.php` (node kinds, tag blacklist, QS.* registry) | `ARCHITECTURE.md` §2, §8, §9 | | `secure/src/classes/ApiResponse.php` (envelope shape) | `COMMAND_API.md` (response shape) | | `secure/src/classes/CommandRunner.php` (allowlist) | `COMMAND_API.md` (internals) | | `secure/src/functions/filePolicy.php` (archive limits — `max_entries` / `max_total_bytes` / `max_entry_bytes` / `max_ratio`; import + publish extension allowlists; override merge rules) **and** `secure/management/config/import-policy.php.example` — the two must ALWAYS change together, the `.example` is the shipped documentation of the defaults. The same file's import content check, `qs_import_validate_content()`, and its one exemption (site JSON that is never served) are documented separately | `COMMAND_API.md` (*Archive import limits* — the table of defaults + the override section; its *How an entry's content is checked* for the content check) and, for the content check, the `importProject` `help.php` notes | | `secure/management/command/importProject.php` / `exportProject.php` (what an archive carries, and what an import refuses — one entry, or the whole archive through the archive gate `importFirstStructureFailure()`; and the fixed spellings in `importEntryNameRefusal()`: a file or folder the engine reads by name joins that list, or a case variant of it imports differently on Linux and Windows) | `COMMAND_API.md` — **both** the *Export / Import* catalogue row and *Archive import limits*, which state the same contract twice — and the two commands' `help.php` entries, which state it a third time | | `secure/management/command/exportProject.php` — **a saved export's name**: `_export__