# Application foundation

The application foundation covers DEV-01, DEV-02, DEV-03, DEV-05, DEV-06 and
DEV-07: project/app scaffolding, settings, registration and lifecycle, routing,
management commands, checks and local rebuild/restart. Its public APIs remain
experimental. The separate reusable-package battery (DEV-04), database
inspection/shell tools (DEV-08), and independent release validation retain their
own acceptance criteria in [the inventory](https://cicadel.fauzaanu.com/en/dev/sources/batteries.md).

## Projects and upgrades

`cicadel startproject DIRECTORY example.com/project` creates an ordinary Go module,
`manage.go`, typed project settings, explicit `project/apps.go`, root routes,
compiled components, embedded CSS and request tests. `startapp NAME [DIRECTORY]`
creates model, view, route, migration and test files. Names and module paths are
validated before writing. Existing destinations are never intentionally replaced.
`startpackage` is the independently distributable-app scaffold.

Keep models in `apps/notes/models.go`, split them into multiple files in that Go
package, or move them to `apps/notes/models/` with `package models` and import that
package explicitly. Do not keep duplicate model declarations in both layouts.
There is no runtime discovery. Register owned models with `ModelOf` in the app's `Models()` list. `makemigrations` and `generatequeries` consume that same registry. Handwritten SQL also belongs to recorded history with reviewed historical metadata.
Neither startup nor reload calls AutoMigrate or applies migrations.

For upgrades, pin the intended framework version with `go get`, run `go mod tidy`,
read release notes, and generate a reference project in a new directory. Compare
it with your project and merge deliberate changes. Generation does not rewrite
existing settings, models, components or migration history. Run the matching
`go tool cicadel ui build`, `ui check --json` and project tests before deployment.
See [the upgrade policy](https://cicadel.fauzaanu.com/en/dev/releases/releases/index.md) and [model migrations](https://cicadel.fauzaanu.com/en/dev/topics/migrations/index.md).

## Configuration and registration

Settings precedence is defaults, typed project assignments, then explicit
environment overrides. Every operational field has a documented override in
[operations](https://cicadel.fauzaanu.com/en/dev/howto/operations/index.md). Invalid values fail without printing their contents.
`settings.LoadWith` accepts an injected lookup for tests; it never changes the
process environment. Loaded and constructed applications own their allowed-host
slices. Settings formatting and structured logging redact DatabaseURL; directly
logging that field or serializing arbitrary application secrets bypasses this
protection.

Package settings are ordinary typed constructor options, passed explicitly from
`project/apps.go`. Validate them in the constructor or a registered check; do not
introduce a shared untyped registry or mutate another app's settings. A new factory
call must allocate fresh lifecycle state for each test or application.

App descriptors are copied at construction. Registration rejects duplicate labels,
missing dependencies, cycles, incompatible manifest versions and contribution
collisions. Dependency ordering is stable. Startup runs checks before acquiring
resources. Successful hooks and a failing hook's returned cleanup run in reverse
order on failure. Successful startup is idempotent; closed applications cannot
restart. Close follows HTTP drain and attempts every cleanup even when one fails.
Hooks must honor context deadlines and must not recursively call Start or Close.

Routes use typed `urls.Route` values and standard HTTP handlers. `urls.Include`
applies namespaces and prefixes; reversal validates parameters. App middleware
wraps only that app's routes, in declaration order. See [the application API](https://cicadel.fauzaanu.com/en/dev/ref/api/index.md)
and [installable lifecycle contracts](https://cicadel.fauzaanu.com/en/dev/topics/installable-apps/index.md).

## Management commands

`go run manage.go help` lists built-in, project and namespaced app commands in
sorted order. `help COMMAND` or `COMMAND --help` shows its description without
running checks, startup hooks or the command. Add descriptions through
`App.CommandHelp` or `Manager.CommandHelp`; descriptions must reference registered
commands. Project names are lowercase identifiers with optional hyphens and
underscores; app commands remain `app:command`. Collisions are errors.

Runtime commands execute after checks/startup and receive cleanup on success or
failure. Explicit `App.BuildCommands` run without database validation or runtime
hooks. Constructors and the generated project's Build function still run before
Manager.Run; a project that opens a database during construction needs its
configuration even for help. Offline tools should use a separate construction
path when that distinction matters.

Custom commands receive an unmodified argument slice and context. Use Go's
`flag.NewFlagSet(name, flag.ContinueOnError)`, choose an explicit output writer,
and return `management.UsageError{Err: err}` for invalid flags or arguments.
Handle `flag.ErrHelp` as success. Return ordinary errors for execution failures.
Do not call os.Exit inside commands, because it bypasses lifecycle cleanup.

`management.ExitCode(err)` defines 0 for success/help, 2 for usage errors, 130 for
context cancellation, and 1 for execution/configuration failure. Generated
manage.go and the framework CLI use it. Existing entry points can adopt the same
helper. The exit status of `go run` itself is Go's launcher status; build an
executable to observe the application's exact status directly.

## Structured system and deployment checks

Legacy `App.Checks` continue to return errors. New `App.SystemChecks` register
`SystemCheck{Deployment: bool, Run: func(context.Context) []CheckMessage}`. Each
message has a local ID, severity (`info`, `warning`, `error`), concise Message and
optional Hint. IDs must be lowercase contribution identifiers and unique within
one check invocation. The framework qualifies them as `app:check:id` and rejects
invalid results. Diagnostics are ordered by app dependencies, sorted check names,
and the check's returned message order. Keep messages and hints free of secrets.

Ordinary startup fails on error diagnostics; warnings and information do not
block startup. Deployment-only checks run only on explicit request. Checks must
be read-only, must honor cancellation, and must not start background services.

```
go run manage.go check
go run manage.go check --deploy --fail-level warning
go run manage.go check --deploy --json
```

`--deploy` additionally checks debug mode, secure cookies and explicit allowed
hosts, and runs package deployment checks. `--json` emits a diagnostic array,
including on check failure. `--fail-level` selects the minimum failing severity;
it defaults to error. Database provider requirements are included in management
checks. These checks cannot certify a reverse proxy, external TLS, backups or
production infrastructure; those remain deployment responsibilities.

## Development rebuild and restart

Install the matching CLI and the pinned CSS compiler once, then run:

```
go tool cicadel ui css install
CICADEL_DEBUG=true CICADEL_SECURE_COOKIES=false go tool cicadel dev
```

`dev --dir DIRECTORY --interval 300ms --shutdown-timeout 10s` watches a project
containing go.mod and manage.go. It compiles components and configured CSS, builds
an executable outside the project, and runs it with `runserver`. The same project
configuration and environment apply to every generation. It does not install
build tools implicitly. Applications using another CSS system retain their own
build process when no ui.css.json is configured.

Content hashes detect creation, deletion and same-size changes, including embedded
assets. Two stable observations debounce edits. A source change during a successful
build causes another build before that binary is served. A failed build reports
compiler/source diagnostics and leaves the working server alive. Fixing the source
triggers a new build. The old server receives an interrupt only after a replacement
build succeeds; it is reaped before the replacement starts. Shutdown escalates to
kill after the configured timeout. Interrupt and SIGTERM stop the supervisor and
its child. A server that exits, including on a port conflict, returns an error with
its output instead of repeatedly spawning failing processes.

Hidden files/directories, bin, dist, vendor, node_modules and nested Go modules are
excluded. The project root is resolved to its real path; symlinks within the
source tree are rejected with a diagnostic. Put generated runtime logs and
artifacts in an excluded output directory. Changes to another module or environment
require restarting the supervisor. This is an executable restart workflow; it does
not preserve process memory, inject browser refresh scripts or provide in-process
hot replacement. Production remains `go build` and ordinary process supervision.

## Acceptance evidence

- `settings`: every operational override, malformed-value secrecy, copied host
  configuration, redacted formatted/structured diagnostics and fuzz boundaries.
- Root application tests: route composition, dependency ordering, descriptor
  isolation, partial-start cleanup, cancellation, readiness, HTTP shutdown and
  structured check validation/deployment selection.
- `management`: command collisions, offline/runtime lifecycle, deterministic help,
  check flags, severity thresholds, usage status and cancellation status.
- `internal/devserver`: real executable rebuild, continued HTTP service after a
  compiler failure, recovery after fixing source, content-hash boundaries and
  forced shutdown/reaping of an unresponsive child.
- `internal/scaffold`: generated project/app tests and an outside-source-tree
  executable exercise help, JSON checks, deployment rejection and exact exit codes;
  existing generated-consumer tests verify component and package integration.

The applicable repository workflows verify generated files, PostgreSQL integration,
race checks, vet, fuzz targets, vulnerability scans and builds, including nested
modules. External security review and beta adoption are not claimed by these tests.

Local verification on 2026-11-02 used Go 1.26.10 and PostgreSQL 18.4. The required
commands from `ci.yml`, `accounts.yml`, `formsets.yml` and
`migration-installables.yml` passed, including strict PostgreSQL/race coverage,
all eight configured fuzz runs, generated-source checks, nested-module vet and
vulnerability scans. Release CLI builds passed for Linux and macOS on amd64 and
arm64. The root suite was rerun after the final watcher fixes. These results cover
local checks; they do not claim an external review or a published release.
