|
1 | 1 | # backend — durable stack orchestration |
2 | 2 |
|
3 | 3 | `backend/` is the stack-level orchestration plane. It exists because repository |
4 | | -automation must remain inspectable and recoverable when a hosted executor such as |
5 | | -GitHub Actions is delayed, unavailable, or unreliable. |
| 4 | +automation must remain inspectable and recoverable when a hosted executor such |
| 5 | +as GitHub Actions is delayed, unavailable, or unreliable. |
6 | 6 |
|
7 | 7 | The first vertical slice is MSDMD regeneration. |
8 | 8 |
|
9 | | -## Contract |
10 | | - |
11 | 9 | ```text |
12 | 10 | request |
13 | | - -> durable SQLite job |
| 11 | + -> PostgreSQL job on the VM |
14 | 12 | -> exact source + generator identities |
15 | | - -> selected executor |
16 | | - -> repo-local generation |
17 | | - -> artifact verification |
18 | | - -> SHA-256 receipt |
| 13 | + -> leased worker attempt |
| 14 | + -> bounded repo-local generation |
| 15 | + -> source/generator re-verification |
| 16 | + -> atomic artifact replacement |
| 17 | + -> PostgreSQL + JSON SHA-256 receipt |
19 | 18 | -> explicit retry / hmmm |
20 | 19 | ``` |
21 | 20 |
|
22 | 21 | GitHub Actions is not the job database and is not required by this slice. The |
23 | | -implemented executor is `local`; VM and GitHub-hosted executors remain `hmmm` |
24 | | -until they implement the same job contract. |
| 22 | +implemented executor is the VM-local worker; a future GitHub-hosted adapter may |
| 23 | +claim the same durable contract without becoming the state owner. |
| 24 | + |
| 25 | +## Authority boundary |
| 26 | + |
| 27 | +PostgreSQL owns orchestration state only: |
| 28 | + |
| 29 | +- requested jobs and idempotent identities; |
| 30 | +- worker leases and attempts; |
| 31 | +- receipts; |
| 32 | +- dependency ordering; |
| 33 | +- unresolved `hmmm`. |
| 34 | + |
| 35 | +Repositories continue to own their source, canon, and generated artifacts. The |
| 36 | +worker coordinates one target at an exact commit using an exact skill-lib |
| 37 | +collector digest; it acquires no semantic authority from that access. |
| 38 | + |
| 39 | +## Runtime requirements |
| 40 | + |
| 41 | +- Linux VM with systemd. |
| 42 | +- PostgreSQL 14+. |
| 43 | +- Python 3.11+. |
| 44 | +- `git`. |
| 45 | +- target checkouts directly under `STACK_REPO_ROOT`. |
| 46 | +- a pinned skill-lib checkout at `STACK_SKILL_LIB_ROOT`. |
| 47 | +- an independent mounted filesystem/device for the verified backup mirror. |
| 48 | + |
| 49 | +The production worker is intended to use local PostgreSQL Unix-socket/peer |
| 50 | +authentication and no outbound network access. |
| 51 | + |
| 52 | +## Install |
| 53 | + |
| 54 | +After observing the actual VM, follow [`deploy/VM_SETUP.md`](deploy/VM_SETUP.md). |
| 55 | +The application portion is: |
| 56 | + |
| 57 | +```bash |
| 58 | +cd /srv/stack |
| 59 | +python3 -m venv .venv |
| 60 | +.venv/bin/pip install --upgrade pip |
| 61 | +.venv/bin/pip install -r backend/requirements.txt |
| 62 | +``` |
| 63 | + |
| 64 | +Configure `/etc/stack-orchestrator.env` from |
| 65 | +`deploy/stack-orchestrator.env.example`. Keep it mode `0600`. |
25 | 66 |
|
26 | | -Repository authority stays with the target repository. `backend/` coordinates a |
27 | | -regeneration against an explicit checkout and a pinned skill-lib collector; it |
28 | | -does not become metadata authority. |
| 67 | +Initialize PostgreSQL: |
| 68 | + |
| 69 | +```bash |
| 70 | +set -a |
| 71 | +. /etc/stack-orchestrator.env |
| 72 | +set +a |
| 73 | +/srv/stack/.venv/bin/python -m frontend.cli.stackctl db migrate |
| 74 | +``` |
29 | 75 |
|
30 | | -Operational state defaults to `.stack/state/jobs.sqlite3` and is intentionally |
31 | | -untracked. |
| 76 | +`SQLite` is no longer a production fallback. One VM PostgreSQL service owns the |
| 77 | +orchestration state so leases, concurrent claims, retries, and recovery have one |
| 78 | +transactional boundary. |
32 | 79 |
|
33 | | -## Usage Guidance |
| 80 | +## MSDMD usage |
34 | 81 |
|
35 | | -From the stack repository root: |
| 82 | +Moving branch names are not execution identities. A queued job records the full |
| 83 | +source commit plus SHA-256 of `msdmd/collect.py`. |
36 | 84 |
|
37 | 85 | ```bash |
38 | | -python -m frontend.cli.stackctl msdmd refresh ucns --root ../ucns |
| 86 | +python -m frontend.cli.stackctl msdmd refresh ucns \ |
| 87 | + --root /srv/stack-repos/ucns \ |
| 88 | + --source-sha <40-hex-commit> |
| 89 | + |
39 | 90 | python -m frontend.cli.stackctl msdmd status |
40 | 91 | python -m frontend.cli.stackctl msdmd explain <job-id> |
41 | 92 | python -m frontend.cli.stackctl msdmd retry <job-id> |
42 | 93 | ``` |
43 | 94 |
|
44 | | -Queue without executing: |
| 95 | +Queue without running immediately: |
| 96 | + |
| 97 | +```bash |
| 98 | +python -m frontend.cli.stackctl msdmd refresh ucns \ |
| 99 | + --root /srv/stack-repos/ucns \ |
| 100 | + --source-sha <40-hex-commit> \ |
| 101 | + --queue-only |
| 102 | +``` |
| 103 | + |
| 104 | +The persistent worker claims queued jobs with `FOR UPDATE SKIP LOCKED` and a |
| 105 | +lease: |
45 | 106 |
|
46 | 107 | ```bash |
47 | | -python -m frontend.cli.stackctl msdmd refresh ucns --root ../ucns --queue-only |
48 | | -python -m frontend.cli.stackctl msdmd run <job-id> |
| 108 | +python -m frontend.cli.stackctl worker once |
| 109 | +python -m frontend.cli.stackctl worker run |
49 | 110 | ``` |
50 | 111 |
|
51 | | -Run the verification suite: |
| 112 | +A regeneration becomes `hmmm`, not guessed-through success, when the worker |
| 113 | +finds an operator-resolvable boundary such as: |
| 114 | + |
| 115 | +- target HEAD differs from the requested commit; |
| 116 | +- target HEAD changes during generation; |
| 117 | +- skill-lib collector digest differs from the queued identity; |
| 118 | +- unrelated worktree changes are present; |
| 119 | +- target is outside `STACK_REPO_ROOT` / `STACK_ALLOWED_REPOS`; |
| 120 | +- an expired worker lease is recovered. |
| 121 | + |
| 122 | +A collector non-zero exit is `failed`. Successful output is written to a temp |
| 123 | +file, re-verified, hashed, and atomically replaced at the repository root. |
| 124 | + |
| 125 | +## Backups |
| 126 | + |
| 127 | +`ops/backup_postgres.sh`: |
| 128 | + |
| 129 | +1. runs custom-format `pg_dump`; |
| 130 | +2. validates the dump with `pg_restore --list`; |
| 131 | +3. records SHA-256; |
| 132 | +4. retains a local recovery copy; |
| 133 | +5. requires an independent mounted backup root; |
| 134 | +6. refuses a same-filesystem mirror; |
| 135 | +7. copies, re-hashes, and re-validates the independent copy; |
| 136 | +8. applies local and mirror retention windows. |
| 137 | + |
| 138 | +If the independent mount is absent, the script leaves the validated local dump |
| 139 | +in place but exits non-zero with `hmmm`; that is recovery material, not a |
| 140 | +complete backup. |
| 141 | + |
| 142 | +The systemd timer runs daily with a randomized delay. Install it only after one |
| 143 | +manual backup and one restore drill succeed. |
| 144 | + |
| 145 | +Restore drill: |
| 146 | + |
| 147 | +```bash |
| 148 | +set -a |
| 149 | +. /etc/stack-orchestrator.env |
| 150 | +set +a |
| 151 | +backend/ops/restore_test.sh |
| 152 | +``` |
| 153 | + |
| 154 | +The script refuses to restore into the production DSN. |
| 155 | + |
| 156 | +## Tests |
| 157 | + |
| 158 | +Local unit tests require no PostgreSQL installation: |
52 | 159 |
|
53 | 160 | ```bash |
54 | 161 | python -m unittest backend.tests.test_orchestrator |
55 | 162 | ``` |
56 | 163 |
|
| 164 | +A PostgreSQL integration test is enabled only when a disposable database is |
| 165 | +explicitly supplied: |
| 166 | + |
| 167 | +```bash |
| 168 | +STACK_TEST_DATABASE_URL='postgresql:///stack_orchestrator_test?host=/var/run/postgresql' \ |
| 169 | + python -m unittest backend.tests.test_orchestrator.PostgresIntegrationTests |
| 170 | +``` |
| 171 | + |
| 172 | +Shell syntax: |
| 173 | + |
| 174 | +```bash |
| 175 | +bash -n backend/ops/backup_postgres.sh backend/ops/restore_test.sh |
| 176 | +``` |
| 177 | + |
57 | 178 | ## hmmm |
58 | 179 |
|
59 | | -- VM executor and credential boundary. |
60 | | -- GitHub Actions executor as an optional worker, never the durable state owner. |
61 | | -- Organization-level affected-repository discovery and dependency ordering. |
62 | | -- Persistent service/daemon; the first slice is intentionally operator-driven. |
| 180 | +- The actual VM distribution, PostgreSQL state, service account, and filesystem |
| 181 | + ownership are not observable from this chat and must be inspected before |
| 182 | + installation. |
| 183 | +- The independent backup device/remote-backed mount is not yet identified from |
| 184 | + this environment. |
| 185 | +- GitHub Actions executor adapter remains deliberately unimplemented; VM-local |
| 186 | + execution is the resilience baseline. |
| 187 | +- Organization-level affected-repository discovery and automatic dependency |
| 188 | + scheduling are represented as a next layer, not silently inferred. |
| 189 | +- Automatic commit/PR materialization of regenerated collection points remains |
| 190 | + separate from regeneration and receipt verification. |
0 commit comments