The generated reference says what each class does. This page records the things it deliberately does not do, and the bounds that are fixed in code rather than configuration.
`lib/main.php` checks for `config.php` before the framework is allowed to exist. If it is missing and `install/` is present, the request is handed to the installer; if the installer is gone too, the request ends there.
The practical consequence: the application cannot boot in a degraded or read-only mode for inspection. There is no “load what you can” path. A deployment either has a complete `config.php` or does not run.
The whitelist is literally `en, ar, zh, fr, ru, es, id`, and it appears five times in the tree:
| Location | Form |
| — | — |
| `lib/main.php:129` | `$validLocales = ['en', 'ar', …]` |
| `lib/controller/TranslationController.php:40` | `in_array($_GET['lang'], ['en', 'ar', …])` |
| `lib/controller/LocaleController.php:28` | fallback when `available_locales()` is absent |
| `lib/dao/LanguageDao.php:13` | documented in the class docblock |
| `install/include/setup.php:811` | `$langOrder` for the installer's seed data |
Adding a language therefore means five coordinated edits, and the installer's seed order can drift from the runtime whitelist.
Any locale with a region subtag is rejected. The sanitiser in `lib/main.php` is `preg_replace('/[^a-z]{2}/', '', strtolower($code))`, which only removes *runs of two or more* non-`a-z` characters. A single separator is left alone, so the value reaches the whitelist unchanged and fails:
| Input | After sanitiser | Result |
| — | — | — |
| `en` | `en` | accepted |
| `EN` | `en` | accepted |
| `pt-br` | `pt-br` | rejected — `-` is a run of one |
| `pt_BR` | `pt_br` | rejected |
| `zh-Hans` | `zh-hans` | rejected |
| `en-us` | `en-us` | rejected |
So `pt-BR` cannot be expressed, and adding it would need a real normalisation step rather than a longer whitelist.
`Dispatcher::dispatch()` branches on one configuration value, `rewrite_status`:
Both styles are always present in the code; only one is reachable per configuration. A new page type must be added to both the route table in `Bootstrap::defineRoutingRules()` and the query-parameter list in `lib/main.php`, or it will work in one URL style and 404 in the other.
`?download=` is the documented exception: it is handled before the SEO branch specifically so downloads survive either permalink setting.
There is no environment-variable fallback for the database credentials. `.env` is loaded through Dotenv when present, but the application's own settings live in `config.php`, which is derived from `config.sample.php`. See Requirements.
This is the most consequential non-behaviour in the codebase, and it is the facade's own documented decision.
`Scriptlog\Core\FrontHelper` is a deprecated facade over `Scriptlog\Service\FrontService`. Every data-access method delegates to the `FrontService` instance registered via `FrontHelper::setFrontService()`. When no service is registered, the methods return `null` or an empty array.
The inline SQL that used to back those methods was removed because it drifted from `FrontService`, used string interpolation, and one path was mysqli-only and broke on PDO. The fix was correct, but the chosen failure mode is silence. A theme calling `FrontHelper` before `Bootstrap` has run gets an empty result and renders a blank region with no error anywhere.
83 types have a test whose name suggests it covers them. 113 do not:
| Layer | Types without a matching test name |
| — | — |
| core | 75 |
| controller | 17 |
| dao | 9 |
| model | 9 |
| service | 4 |
This is a name-based heuristic, not proof of a coverage gap: a test may cover a class under a different name, or through an integration test. But the shape is informative — `service` is the best-covered business layer by name (only 4 unmatched) while `core` is by far the largest gap. With 2,649 methods in the suite, the project clearly tests heavily; what it does not do is maintain a one-test-per-class correspondence.
There are zero `@todo` annotations in first-party code. The one hit in an earlier pass was inside a vendored HTMLPurifier shim. Either the backlog is tracked outside the source, or it is tracked in issues; it is not in the code.