diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 7d371bd..6570916 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -41,7 +41,6 @@ jobs: shellcheck --exclude=SC2034,SC2015,SC2164 core/*.sh shellcheck --exclude=SC2034,SC2015,SC2164 modules/host/*.sh shellcheck --exclude=SC2034,SC2015,SC2164,SC2119,SC2120 modules/db/*.sh - shellcheck --exclude=SC2034,SC2015,SC2164 modules/preflight/*.sh shellcheck --exclude=SC2034,SC2015,SC2164 cron/*.sh shellcheck --exclude=SC2034,SC2015,SC2164,SC2043,SC2012,SC1090,SC1091 cli/actools shellcheck --exclude=SC2034,SC2015,SC2164,SC2043,SC2012,SC1090,SC1091 cli/commands/*.sh @@ -53,8 +52,6 @@ jobs: shellcheck --severity=warning --exclude=SC2034,SC2015,SC2164,SC1090,SC1091 modules/audit/lib/*.sh shellcheck --exclude=SC2034,SC2015,SC2164,SC2119,SC2120 modules/preview/*.sh shellcheck --exclude=SC2034,SC2015,SC2164 modules/stack/*.sh - shellcheck --exclude=SC2034,SC2015,SC2164 modules/storage/*.sh - shellcheck --exclude=SC2034,SC2015,SC2164 modules/worker/*.sh trivy: runs-on: ubuntu-latest steps: diff --git a/docs/architecture/runtime-authority-map.md b/docs/architecture/runtime-authority-map.md index 1951765..393d34d 100644 --- a/docs/architecture/runtime-authority-map.md +++ b/docs/architecture/runtime-authority-map.md @@ -40,7 +40,7 @@ A row may carry a compound status (e.g. `current (monolithic) → target via dis | **DB access layer (root exec / dump / backup user / readiness wait / credential probe)** | `modules/db/core.sh` — **live module (P0-M)**, sourced by `actools.sh` at the exact spot the inline blocks (`:450-530`) occupied; `db_exec_root`, `db_exec_root_stdin`, `db_dump_container`, `setup_backup_db_user`, `wait_db`, `check_db_creds` extracted **verbatim** (per-function byte-identity verified; the `tests/db/` contract/mock suite pinned the commands and SQL against the inline code first and stayed green across the move). ONE intentional post-extraction change (isolated, e2e-gated commit): `wait_db`'s readiness probe no longer passes the DB **root** password on argv — it uses a umask-077 `--defaults-extra-file` temp file inside the container, fed over stdin by the printf builtin (the backup-cron pattern); probe SQL (`mysql.actools_write_check` write-check), 50×3s bounds and outcome unchanged. SECURE SHAPE CI-locked: `tests/guards/wait_db_security_guard_test.bats` (non-vacuous) | none — the **stale v9.2 twins are deleted (P0-M)**: `modules/db/{backup_user,credentials,wait}.sh` were unwired orphans whose content (a divergent `check_db_creds` error message; the argv-password `wait_db`) did **not** survive (grep-proof: no references on any `.sh`/`.yml`/`.bats` surface). The duplicate-function guard now covers the six DB names (closure exactly-once + wired-twin + unconditional `modules/db` twin-ban arms, all non-vacuous) | The DB access layer as a live module produced from the inline v14 code, with the orphan twins purged and the argv-password probe retired (P0-M spec) | **module = live (P0-M landed)** — six functions exactly-once on the live path; `wait_db` argv exposure (Entry-017 known risk, `:510`) **CLOSED — e2e-confirmed** (run #75 reached `MariaDB ready.`; stamped at the P0-N ratification of Entry 018); golden drift 6/6 + cron fixture (no fixture modified). **(P0-N) single authority across BOTH runtimes:** the live CLI (`doctor`) now also resolves the DB layer from this module — `doctor.sh`'s byte-identical local `db_exec_root` copy is deleted and the live-CLI-path guard bans any redefinition (see the Doctor row) | | **DB provisioning** | `actools.sh::install_env()` — inline `db_exec_root` SQL creating DB/user/grants (the *callers* stay inline; the called DB-layer functions are the P0-M module above) | none — the former `modules/db/*` v9.2 twins are **deleted (P0-M)**; `modules/db/core.sh` (the live DB access layer) is what `lint.yml`'s `modules/db/*.sh` shellcheck glob now matches | `modules/db/*` (or `db` stage handler) via dispatcher | **current (monolithic)** in `install_env`. P0-D wired the `db` stage as a documented **no-op** handler (DB creation stays inside the `install_env` loop, which runs at the `drupal` stage). **P0-M moved only the DB access layer** (the six functions); the genuine db/drupal split + `install_env` extraction is **later scope** (the post-closure renumbering made P0-N the live-CLI DB convergence — see the Doctor row) | | **Drupal provisioning** | `modules/drupal/provision.sh` — **sourced LIVE** at `actools.sh:183`; `drupal_provision "$env"` called from `install_env` (`:~1152`) | none known | `modules/drupal/provision.sh` via dispatcher | **live (module)** — the *one* module sourced on the live path; the cleanest precedent for the extraction pattern | -| **Worker provisioning** | worker **image** built by `modules/stack/images.sh::build_worker_image` (**live**, P0-G); worker **service** rendered by `modules/stack/compose.sh::generate_compose`; worker CLI ops are **inline in `cli/actools`** (`worker-logs` :103) — the dead `cli/commands/worker.sh` twin was **deleted in P0-O** | `modules/worker/*` exists (shellchecked `lint.yml:41`) but is still **not** on the live path; the worker **runtime** stays folded in the compose generator | `modules/worker/*` (or `worker` stage handler) via dispatcher | **partial (P0-G)** — the worker **image build** moved to `modules/stack/images.sh`; the worker **runtime**/service stays folded and the `worker` stage remains a no-op (genuine worker-stage extraction is later scope) | +| **Worker provisioning** | worker **image** built by `modules/stack/images.sh::build_worker_image` (**live**, P0-G); worker **service** rendered by `modules/stack/compose.sh::generate_compose`; worker CLI ops are **inline in `cli/actools`** (`worker-logs` :103) — the dead `cli/commands/worker.sh` twin was **deleted in P0-O** | `modules/worker/*` was **deleted in C2** (orphan — never on the live path; its `lint.yml` shellcheck line was removed with the dir); the worker **runtime** stays folded in the compose generator | `modules/worker/*` (or `worker` stage handler) via dispatcher | **partial (P0-G)** — the worker **image build** moved to `modules/stack/images.sh`; the worker **runtime**/service stays folded and the `worker` stage remains a no-op (genuine worker-stage extraction is later scope) | | **Backup-cron generator** | `modules/backup/cron.sh::setup_backup_cron` — **live module (P0-L)**, sourced by `actools.sh` at the exact spot the inline block (`:584-661`) occupied; the function body extracted **verbatim** (per-function byte-identity verified; the generated `/etc/cron.daily/actools-backup` is **byte-identical** — golden capture `tests/fixtures/golden/backup-cron`, sha `bdfaa0c6…`). SECURE SHAPE, now CI-locked: the cron writes `[mariadb-dump]`/user/password into a **umask-077 temp file inside the DB container** and runs `mariadb-dump --defaults-extra-file="$t"`; the password is read from `.actools-state.json` at cron **runtime** — never on argv, never baked into the script (`tests/guards/cron_security_shape_guard_test.bats`, non-vacuous) | none — the **insecure orphan `cron/backup.sh` is deleted (P0-L)**: its `-ubackup -p"${BACKUP_PASS}"` put the DB password on argv (visible in `ps`); it was unwired and its form did **not** survive (grep-proof: no references on any `.sh`/`.yml`/`.bats` surface). The remaining `modules/backup/*` files are untouched P0-O-audit orphans | The backup-cron generator as a live module produced from the secure inline shape, with the insecure orphan purged (P0-L spec) | **module = live (P0-L landed)** — generated cron byte-identical (`tests/generated/backup_cron_drift_test.bats`); security shape guarded in CI; golden drift 6/6 (no compose fixture modified) | | **CLI install** | `actools.sh::setup_cli()` (`:1247-1262`) — **install-by-copy** (P0-F): `install -m 0755 "${INSTALL_DIR}/cli/actools" /usr/local/bin/actools`; then `chmod +x` and persist `ACTOOLS_HOME=${INSTALL_DIR}` to `/etc/environment`. The old `cat > /usr/local/bin/actools < set. **Any phase that changes the live-module set must update BOTH** this > table and that guard's `EXPECTED_LIVE_MODULES` list. Baseline `82ba206`. -`modules/` holds **18** directories. **6 are LIVE** — reached by the live +`modules/` holds **13** directories. **6 are LIVE** — reached by the live install path (sourced from `actools.sh`, or referenced by the installer / -operator CLI). **12 are orphan** — no live reference anywhere in `actools.sh`, +operator CLI). **7 are orphan** — no live reference anywhere in `actools.sh`, `installer/`, or `cli/` (verified by per-module grep, recorded in -`PHASE0_LEDGER` Entry 021). The orphans split into **dead-twins** (duplicate -live inline/module logic; C2 deletes them) and **4.5-seeds** (committed 4.5 -design; C3 quarantines them into `experimental/`, not deleted). **C1 acts on -none of them** — it only classifies and guards, so C2/C3 are safe. - -**Totals: 6 live · 12 orphan (7 dead-twin + 5 4.5-seed) · 18 total.** (The -plan-of-record §2's "12 of 19" is an off-by-one; the verified figure is -**12 of 18**, recorded here authoritatively.) +`PHASE0_LEDGER` Entry 021). The 12 original orphans split into **dead-twins** (duplicated +live inline/module logic) and **4.5-seeds** (committed 4.5 design, +quarantined into `experimental/` in C3, not deleted). **C2 removed the 5 +dead-twins** and reclassified `ai` + `preview` (previously dead-twin) **as +4.5-seeds** — their dirs stay in place for C3 — so the **7 that remain are +all 4.5-seeds**. C1 had acted on none of them — it only classified and +guarded, which is what made C2 safe. + +**Totals: 6 live · 7 orphan (all 4.5-seed, C3-quarantine-bound) · 13 total.** +5 dead-twins (`health, migrate, preflight, storage, worker`) removed in C2; +`ai` + `preview` reclassified as 4.5-seeds (C3 quarantine). (At C1 the figure +was **12 of 18** orphan — correcting the plan-of-record §2's "12 of 19" +off-by-one; C2 then removed 5, leaving **7 of 13**.) | module | status | evidence | disposition | |---|---|---|---| @@ -122,13 +127,8 @@ plan-of-record §2's "12 of 19" is an off-by-one; the verified figure is | `drupal` | LIVE | sourced on the live path — `actools.sh:181` (`modules/drupal/provision.sh`) | — | | `host` | LIVE | sourced on the live path — `actools.sh:193` loop over `modules/host/*.sh` | — | | `stack` | LIVE | sourced on the live path — `actools.sh:204` loop over `modules/stack/*.sh` | — | -| `ai` | orphan · dead-twin | no live reference | C2: delete | -| `health` | orphan · dead-twin | no live reference | C2: delete | -| `migrate` | orphan · dead-twin | no live reference (the **module** dir; the separate inline `migrate` CLI text-guide is **not** this dir and **stays**) | C2: delete (module only) | -| `preflight` | orphan · dead-twin | no live reference | C2: delete | -| `preview` | orphan · dead-twin | no live reference | C2: delete | -| `storage` | orphan · dead-twin | no live reference | C2: delete | -| `worker` | orphan · dead-twin | no live reference | C2: delete | +| `ai` | orphan · 4.5-seed (reclassified C2) | no live reference | C3: quarantine → `experimental/` | +| `preview` | orphan · 4.5-seed (reclassified C2) | no live reference | C3: quarantine → `experimental/` | | `compliance` | orphan · 4.5-seed | no live reference | C3: quarantine → `experimental/` | | `dr` | orphan · 4.5-seed | no live reference | C3: quarantine → `experimental/` | | `network` | orphan · 4.5-seed | no live reference | C3: quarantine → `experimental/` | @@ -142,8 +142,8 @@ from the tree — the source-closure of `actools.sh` (the `CLOSURE` engine from `actools.sh` / `installer/` / `cli/actools` — and fails CI if it diverges from this list in **either** direction (an undocumented new live module, or a documented-live module that stops being sourced). `audit` is the one live -module reached without an `${INSTALL_DIR}` source line: it is invoked from -`cli/actools` (the copied operator-CLI surface), which is why the union with +module reached without an `${INSTALL_DIR}` source line from `actools.sh`: it is +invoked from `cli/actools` (the copied operator-CLI surface), which is why the union with the entry-point grep, not the closure alone, is required. ## Update rule diff --git a/docs/runbooks/PHASE0_LEDGER.md b/docs/runbooks/PHASE0_LEDGER.md index 4fe500e..d3fac07 100644 --- a/docs/runbooks/PHASE0_LEDGER.md +++ b/docs/runbooks/PHASE0_LEDGER.md @@ -86,11 +86,180 @@ Approved / Needs revision / Blocked ### Forbidden next scope ```` +## Entry 022 — C2 · Delete Dead-Twin Modules (5) + Reclassify ai/preview + +Date: 2026-06-14 +Branch: `phaseC/C2-delete-dead-twins` (operator records the applied branch + `main` SHA on merge) +Commit SHA: one implementation commit on top of baseline `ce35813` + this docs entry (sandbox; operator stamps the squash/merge SHA on apply) +Actor / Claude session (model): Coding Window (Opus) — three isolated Opus windows per WORKFLOW-PACKAGE §0 (coder ≠ reviewer ≠ doc-checker); this window does **not** self-approve +Phase: C2 — Delete Dead-Twin Modules + Reclassify ai/preview (Track C, Layer 2) +Task prompt source: `SPEC-C2-delete-dead-twins.md` + project instructions + `WORKFLOW-PACKAGE.md` + +### Objective + +Delete the **5 dead-twin** orphan modules (`health, migrate, preflight, storage, worker`) — dead code that physically ships to prod via the in-place install + `chown -R`, never reached on the live path. Per the operator's revised decision, **`ai` and `preview` are NOT deleted** (Entry 021 had slated all 7 dead-twins for deletion); they are **reclassified dead-twin → 4.5-seed** and left in place for C3 to quarantine into `experimental/`. C2 also removes the 3 now-dangling `lint.yml` shellcheck lines, updates the C1 inventory in `runtime-authority-map.md` to the post-deletion truth, aligns the audit-wording in the map + guard comment, and ratifies Entry 021 (stamping the C1 merge `ce35813`/#50). The orphan-inventory guard **stays green** throughout — C1 built it to survive exactly this. **This phase changes what ships to prod (deletes files), so a branch e2e green is a required pre-merge gate.** + +### What changed + +**1. Deleted 5 dead-twin dirs** (`git rm -r modules/{health,migrate,preflight,storage,worker}`) — **9 files, 492 sh-lines**: + +```text +146 modules/health/checks.sh +187 modules/migrate/migrate.sh + 15 modules/preflight/disk.sh + 13 modules/preflight/dns.sh + 17 modules/preflight/ram.sh + 19 modules/storage/s3fs.sh + 42 modules/storage/settings_inject.sh + 19 modules/worker/queue.sh + 34 modules/worker/xelatex.sh +492 total +``` + +**Per-module live-path grep-proof** (re-verified at the C2 working tree; surface = the live entry points `actools.sh installer/ cli/ profiles/ cron/`): + +```text +$ grep -rn "modules/health/" actools.sh installer/ cli/ profiles/ cron/ → 0 hits +$ grep -rn "modules/migrate/" actools.sh installer/ cli/ profiles/ cron/ → 0 hits +$ grep -rn "modules/preflight/" actools.sh installer/ cli/ profiles/ cron/ → 0 hits +$ grep -rn "modules/storage/" actools.sh installer/ cli/ profiles/ cron/ → 0 hits +$ grep -rn "modules/worker/" actools.sh installer/ cli/ profiles/ cron/ → 0 hits +``` + +Broadened bare-name (no-trailing-slash) sweep over the same surface: `health/migrate/preflight/storage` = 0 module-mentioning lines; `worker` = 2, both **comments referencing LIVE modules**, not the deleted dir — `actools.sh:418` ("Container images: … worker (`modules/stack/images.sh`)") and `installer/dispatch.sh:298` ("… worker-runtime decomposition … `modules/host/*`"). The worker **runtime** stays folded in the live `modules/stack/` compose+image generators; only the orphan `modules/worker/` copy is gone. Deletion is transparent to the install path. + +The inline `migrate` CLI text-guide is **separate and stays**: `cli/actools:282-283` (`migrate)` case → `echo "=== XeLaTeX Migration Guide ==="`) and `:350` (help text). It references no `modules/migrate/`, and `cli/` is forbidden scope anyway. + +**2. `.github/workflows/lint.yml`** — removed the **3** now-dangling shellcheck command lines (`modules/preflight/*.sh`, `modules/storage/*.sh`, `modules/worker/*.sh`); a glob matching a deleted dir would error and fail the lint job. The **15 remaining** shellcheck commands all resolve to ≥1 existing file and pass (verbatim run below); the `preview`, `dr`, `observability` lines are intact (preview is not deleted in C2). YAML parses. + +**3. `docs/architecture/runtime-authority-map.md`** — inventory updated to the post-C2 truth: +- "Standalone modules" intro counts `18 → 13` dirs, `12 → 7` orphan. +- The orphan-split prose rewritten: the 12 original orphans split into dead-twins + 4.5-seeds; **C2 removed the 5 dead-twins and reclassified `ai` + `preview` as 4.5-seeds** (dirs stay for C3), so the 7 remaining are all 4.5-seeds. +- Totals line `6 live · 12 orphan (7 dead-twin + 5 4.5-seed) · 18 total` → **`6 live · 7 orphan (all 4.5-seed, C3-quarantine-bound) · 13 total`**, with the dictated prose line; the reconciling parenthetical now reads "(At C1 the figure was **12 of 18** orphan … C2 then removed 5, leaving **7 of 13**.)". +- Table: the 5 deleted rows (`health, migrate, preflight, storage, worker`) removed; the `ai` and `preview` rows **reclassified** to status `orphan · 4.5-seed (reclassified C2)`, disposition `C3: quarantine → experimental/`. Table now lists exactly the 13 remaining dirs (6 LIVE + 7 4.5-seed). +- Audit-wording aligned to Entry 021's precise form: "`audit` … reached without an `${INSTALL_DIR}` source line **from `actools.sh`**" (the `cli/actools:317` source line *is* an `${INSTALL_DIR}` source for a helper lib; the precise claim is that `audit.sh` is bash-executed, not in the `actools.sh` source-closure). +- "Current-state map" **Worker provisioning** row: the orphan-copy cell now records `modules/worker/*` was **deleted in C2** (its `lint.yml` line removed with it); the worker runtime stays folded in the compose generator. + +**4. `tests/guards/orphan_inventory_guard_test.bats`** — **comment only**: the audit header comment now reads "… without a `${INSTALL_DIR}` source line from actools.sh — e.g. audit, …" (the fix matches this file's existing no-backtick comment style for `actools.sh`, and preserves the file's article "a"). `EXPECTED_LIVE_MODULES` (line 51) and the `derive_live_modules` body are **byte-identical** to `ce35813` (verified by extraction-diff; only the comment line differs). + +**5. `docs/runbooks/PHASE0_LEDGER.md`** — this Entry 022 added; **Entry 021 ratified** (Pending → APPROVED, C1 merge `ce35813`/#50 stamped, original text preserved). + +### Files changed + +- `modules/health/`, `modules/migrate/`, `modules/preflight/`, `modules/storage/`, `modules/worker/` — **DELETED** (9 files, 492 lines). +- `.github/workflows/lint.yml` — 3 shellcheck lines removed. +- `docs/architecture/runtime-authority-map.md` — inventory + worker row + audit wording (~40 lines changed). +- `tests/guards/orphan_inventory_guard_test.bats` — audit comment only (guard logic + `EXPECTED_LIVE_MODULES` byte-identical). +- `docs/runbooks/PHASE0_LEDGER.md` — Entry 022 added + Entry 021 ratified. + +### Files intentionally not changed + +- **`modules/ai/` and `modules/preview/`** — kept in place for C3 (only their inventory **classification** changed). Byte-identical to baseline. +- **The 5 4.5-seed dirs** (`compliance, dr, network, observability, security`) — C3 scope; byte-identical to baseline. +- **`docs/advanced.md` / `docs/privacy.md`** — **left intact**, and correctly so: their pointers reference `modules/preview/branch.sh` (`advanced.md:88`), `modules/ai/assistant.sh` (`advanced.md:122`), and `modules/ai/` (`privacy.md:5`) — **all still present after C2**, so the "Experimental — not wired" design-reference text is still accurate. The pointer fix rides with **C3**, when ai/preview actually move to `experimental/`. (These live at `docs/`, not `docs/operator/`.) +- `actools.sh`, `installer/`, `cli/`, `profiles/`, `cron/`, `core/` — no code touched; byte-identical to baseline (`actools.sh` sha `44de0635…`). +- The guard's **logic + `EXPECTED_LIVE_MODULES`** — unchanged (comment only). +- No historical record rewritten (`CHANGELOG.md`, `docs/releases/*`, `docs/tests/*`, `HANDOFF-*`, ledger entries < 021); no golden/generated fixture touched. + +### Runtime authority changes + +| Concern | Before | After | +|---|---|---| +| (none) | — | — | + +No authority moved. The 5 deleted modules were **never on the live path** (per-module grep-proof = 0); the live set is unchanged at the 6 `EXPECTED_LIVE_MODULES`. C2 is deletion of dead code + doc/inventory truth-up. + +### Generated-file impact + +| File | Unchanged / Changed intentionally / Not touched | Evidence | +|---|---|---| +| docker-compose.yml | Not touched | generated drift 9/9 green (incl. all 5 variants) | +| Caddyfile | Not touched | generated drift 9/9 green | +| my.cnf | Not touched | generated drift 9/9 green | +| Dockerfiles | Not touched | generated drift 9/9 green | +| CLI | Not touched | `cli/actools` byte-identical to baseline | +| backup cron | Not touched | backup-cron drift 3/3 green | + +### Tests run + +```sh +# 1. guard stays green +bats tests/guards/orphan_inventory_guard_test.bats # 2/2 PASS + +# 2. non-vacuity (ai still exists): inject -> FAIL -> revert -> PASS +sha256sum actools.sh # 44de0635… (== baseline) +sed -i '457a source "${INSTALL_DIR}/modules/ai/assistant.sh" || error "…"' actools.sh +bats tests/guards/orphan_inventory_guard_test.bats # arm 2 FAILS (derived gains `ai`: "> ai") +git checkout -- actools.sh # sha 44de0635… restored byte-for-byte +bats tests/guards/orphan_inventory_guard_test.bats # 2/2 PASS again + +# 3. full guards (no guard added/removed) +bats -r tests/guards/ # 23/23 PASS + +# 4. generated/golden (no drift) +bats -r tests/generated/ # 9/9 PASS + +# 5. dirs gone +ls -d modules/*/ # 13 dirs; health/migrate/preflight/storage/worker absent + +# 6. lint sanity — all 15 remaining shellcheck commands, verbatim +# (every glob resolves to >=1 file; aggregate exit 0) +# actools.sh / core/*.sh / modules/host/*.sh / modules/db/*.sh / cron/*.sh / +# cli/actools / cli/commands/*.sh / installer/*.sh / modules/audit/*.sh / modules/dr/*.sh / +# modules/observability/*.sh / modules/drupal/*.sh / modules/audit/lib/*.sh / +# modules/preview/*.sh / modules/stack/*.sh # 15/15 PASS, exit 0 + +# 7. branch e2e — NOT runnable in sandbox (no docker daemon, no cloud creds); operator-gated. +``` + +### Test result + +PASS (all sandbox-runnable suites) — guard 2/2; non-vacuity inject→FAIL→revert→PASS (actools.sh restored to `44de0635…`); full guards 23/23 (unchanged count: 21 prior + 2 C1); generated/drift 9/9; 13 dirs with the 5 deleted absent; lint 15/15 shellcheck commands exit 0, no missing-glob; YAML parses. Verbatim transcript in `HANDOFF-C2.md`. + +**Branch e2e: NOT run here (sandbox has no docker daemon and no cloud/provisioning credentials) — operator-gated, see Blockers.** Not fabricated. + +### Documentation updated + +- [x] Runtime authority map (inventory 13 dirs; ai/preview reclassified; worker row; audit wording) +- [ ] Generated-file contract (not applicable — no generated output changed) +- [ ] CLI authority contract (not applicable) +- [ ] Operator target docs (intentionally **not** changed — `advanced.md`/`privacy.md` pointers still accurate until C3) +- [x] Test plan (the guard's header comment; this entry's transcript) + +### Changelog / release notes + +- [ ] CHANGELOG.md updated (not in C2's allowed-file set; Review Gate may add a release note on merge) +- [ ] Release note added (out of allowed-file scope) +- [ ] Test report added (the HANDOFF carries the verbatim transcript) +- [ ] Review notes added (Review window appends its verdict) + +### Known risks + +- **Behavior-changing (deletes shipped files).** The grep-proof shows the 5 dirs are unreferenced on the live path, so the install is expected byte-identical — but because the install copies the whole tree and `chown -R`s it, the authoritative confirmation is a **branch e2e** reaching `MariaDB ready.` (signal from the untouched live `modules/db/core.sh:122`). Until that green, **merge is blocked**. +- The map's line-number citations are exact at the relevant baselines; the **guard** keys off derivation (not line numbers), so CI still protects the set if a later phase shifts lines. C3 must refresh the table again when ai/preview/seeds move. +- The `ai`/`preview` reclassification is an inventory/label change only; their dirs and files are byte-identical to baseline, so the C1 non-vacuity injection still bites unchanged (demonstrated above). + +### Blockers + +- **Branch e2e green is a required pre-merge gate** and cannot be produced in this sandbox (no docker daemon, no Hetzner/cloud credentials). The operator must dispatch `e2e.yml` (`workflow_dispatch`) on `phaseC/C2-delete-dead-twins` and confirm the run reaches `MariaDB ready.` (+ `actools doctor` healthy). An SSH-timeout is infra → re-run. No merge until then. + +### Review Gate decision + +**Pending** — the Review Gate ratifies on merge (this coding window does not self-approve). Verify in order: scope (`diff` vs `ce35813` = exactly the 5 dirs deleted + the 4 files edited; `ai`/`preview` + the 5 seeds untouched) → deletion safety (re-grep the 5 names over the live path → 0; inline `migrate` guide intact at `cli/actools:282-291`) → guard integrity (`EXPECTED_LIVE_MODULES` + `derive_live_modules` byte-identical to `ce35813`; re-run 2/2; re-inject non-vacuity) → inventory truth (table matches the 13 dirs; ai/preview reclassified; totals correct; audit wording says "from `actools.sh`") → `lint.yml` (remaining shellcheck lines resolve; YAML parses) → no regression (full guards + generated green) → **branch e2e green (`MariaDB ready.`)** → patch reproduces the tree; author `actools-pl ` → then DOC-CHECK (`advanced.md`/`privacy.md` still accurate because ai/preview still exist). + +### Next safe task + +**C3** — quarantine the **7** 4.5-seeds (`compliance, dr, network, observability, security, ai, preview`) into `experimental/` (move, not delete); fix the `docs/advanced.md` / `docs/privacy.md` pointers to the new `experimental/` paths; remove the `preview` `lint.yml` shellcheck line (and add any needed `experimental/**` lint coverage); update the inventory + guard expectations to match. Branch e2e green required (behavior-adjacent file moves). + +### Forbidden next scope + +No feature wiring (Track E); no edits to `live_closure.bash` or the guard's derivation logic; no golden re-capture; no deletion of the 7 seeds (C3 **moves** them); no rewriting of historical ledger entries (< 022). + ## Entry 021 — C1 · Orphan-Module Inventory + Live-Set Guard Date: 2026-06-14 Branch: `phaseC/C1-orphan-inventory` (operator records the applied branch + `main` SHA on merge) -Commit SHA: one implementation commit on top of baseline `82ba206` + this docs entry (sandbox; operator stamps the squash/merge SHA on apply) +Commit SHA: one implementation commit on top of baseline `82ba206` + this docs entry (sandbox; operator stamps the squash/merge SHA on apply). **Merged to `main` as `ce35813` (#50)** — *stamped at C2 ratification, 2026-06-14.* Actor / Claude session (model): Coding Window (Opus) — cross-model review withdrawn; three isolated Opus windows per WORKFLOW-PACKAGE §0 Phase: C1 — Orphan-Module Inventory + Live-Set Guard (Track C, Layer 1) Task prompt source: `SPEC-C1-orphan-inventory.md` + project instructions + `WORKFLOW-PACKAGE.md` @@ -241,7 +410,7 @@ None. ### Review Gate decision -**Pending** — the Review Gate ratifies on merge (the coding window does not self-approve). Verify in order: scope (3 files only) → inventory truth (re-derive 18 + the 6/12 split + dead-twin/4.5-seed sub-split) → guard correctness (expected == doc == derived; closure-sanity present) → non-vacuity (re-run inject→fail→revert) → no regression (guards + generated green) → patch reproduces tree → author `actools-pl ` → then DOC-CHECK. +**APPROVED — ratified (2026-06-14): C1 merged to `main` as `ce35813` (#50).** C1 was the **no-behavior-change** inventory + live-set-guard phase (docs + one new guard; `actools.sh` byte-identical to `44de0635…`), so it carried **no e2e gate**; `ce35813` is the **verified baseline** of C2, and this ratification rides with the C2 patch — which re-runs the guard green (2/2, non-vacuous) and depends on it. Stamped at C2 ratification per `SPEC-C2` §5, preserving the original entry text. *(Original pending text, for the record:)* **Pending** — the Review Gate ratifies on merge (the coding window does not self-approve). Verify in order: scope (3 files only) → inventory truth (re-derive 18 + the 6/12 split + dead-twin/4.5-seed sub-split) → guard correctness (expected == doc == derived; closure-sanity present) → non-vacuity (re-run inject→fail→revert) → no regression (guards + generated green) → patch reproduces tree → author `actools-pl ` → then DOC-CHECK. ### Next safe task diff --git a/modules/health/checks.sh b/modules/health/checks.sh deleted file mode 100644 index 19f00a7..0000000 --- a/modules/health/checks.sh +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/health/checks.sh — Phase 2: Semantic Health Checks -# ============================================================================= - -# Run a root mariadb command inside the db container with no password on the host argv. -# The container already holds MARIADB_ROOT_PASSWORD; we expose it to the client as MYSQL_PWD -# *inside* the container. Caller passes mariadb args ($@), e.g. -e "..." or a heredoc on stdin. -db_exec_root() { - docker exec -i actools_db sh -c 'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb -uroot "$@"' _ "$@" -} - - - -health_check_all() { - local issues=0 - - echo "" - echo "=== Actools Health Check ===" - echo "$(date '+%F %T')" - echo "" - - # --- Container status --- - echo "── Containers ──────────────────────────" - local containers=("actools_caddy" "actools_db" "actools_php_prod" "actools_redis" "actools_worker_prod") - for c in "${containers[@]}"; do - local status health - health=$(docker inspect "$c" --format="{{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}" 2>/dev/null || echo "none") - status=$(docker inspect "$c" --format='{{.State.Status}}' 2>/dev/null || echo "missing") - - - if [[ "$status" == "running" ]]; then - if [[ "$health" == "healthy" || "$health" == "none" || "$health" == "" ]]; then - echo " ✓ ${c} — ${status}" - else - echo " ✗ ${c} — ${status} (health: ${health})" - (( issues++ )) || true - fi - else - echo " ✗ ${c} — ${status}" - (( issues++ )) || true - fi - done - - # --- Memory pressure --- - echo "" - echo "── Memory Pressure ─────────────────────" - local containers_with_limits=("actools_php_prod:512" "actools_db:2048" "actools_redis:256") - for entry in "${containers_with_limits[@]}"; do - local name="${entry%%:*}" - local limit_mib="${entry##*:}" - local mem_raw - mem_raw=$(docker stats --no-stream --format "{{.MemUsage}}" "$name" 2>/dev/null \ - | grep -oP '^[\d.]+(?=MiB)' || echo "0") - if [[ -n "$mem_raw" && "$mem_raw" != "0" ]]; then - local mem_int="${mem_raw%.*}" - local pct=$(( mem_int * 100 / limit_mib )) - if (( pct > 85 )); then - echo " ✗ ${name}: ${mem_raw}MiB / ${limit_mib}MiB (${pct}%) — CRITICAL" - (( issues++ )) || true - elif (( pct > 70 )); then - echo " ! ${name}: ${mem_raw}MiB / ${limit_mib}MiB (${pct}%) — WARNING" - else - echo " ✓ ${name}: ${mem_raw}MiB / ${limit_mib}MiB (${pct}%)" - fi - fi - done - - # --- TLS certificate expiry --- - echo "" - echo "── TLS Certificate ─────────────────────" - local expiry expiry_epoch now_epoch days_left - expiry=$(echo | openssl s_client -connect "${BASE_DOMAIN}:443" \ - -servername "${BASE_DOMAIN}" 2>/dev/null \ - | openssl x509 -noout -enddate 2>/dev/null \ - | cut -d= -f2) - if [[ -n "$expiry" ]]; then - expiry_epoch=$(date -d "$expiry" +%s 2>/dev/null) - now_epoch=$(date +%s) - days_left=$(( (expiry_epoch - now_epoch) / 86400 )) - if (( days_left < 14 )); then - echo " ✗ ${BASE_DOMAIN} — expires in ${days_left} days — RENEW NOW" - (( issues++ )) || true - else - echo " ✓ ${BASE_DOMAIN} — expires in ${days_left} days" - fi - else - echo " ! TLS check unavailable" - fi - - # --- Disk space --- - echo "" - echo "── Disk Space ──────────────────────────" - local disk_pct disk_free - disk_pct=$(df / | awk 'NR==2 {print $5}' | tr -d '%') - disk_free=$(df -h / | awk 'NR==2 {print $4}') - if (( disk_pct > 85 )); then - echo " ✗ Disk: ${disk_pct}% used (${disk_free} free) — CRITICAL" - (( issues++ )) || true - elif (( disk_pct > 70 )); then - echo " ! Disk: ${disk_pct}% used (${disk_free} free) — WARNING" - else - echo " ✓ Disk: ${disk_pct}% used (${disk_free} free)" - fi - - # --- MariaDB slow queries --- - echo "" - echo "── MariaDB ─────────────────────────────" - local slow_queries - slow_queries=$(db_exec_root -sN \ - -e "SHOW GLOBAL STATUS LIKE 'Slow_queries';" 2>/dev/null | awk '{print $2}') - if [[ -n "$slow_queries" ]]; then - if (( slow_queries > 100 )); then - echo " ! Slow queries: ${slow_queries} — check slow query log" - else - echo " ✓ Slow queries: ${slow_queries}" - fi - fi - - # --- Redis eviction --- - echo "" - echo "── Redis ───────────────────────────────" - local evicted - evicted=$(docker exec actools_redis redis-cli info stats 2>/dev/null \ - | grep evicted_keys | cut -d: -f2 | tr -d '[:space:]') - if [[ -n "$evicted" && "$evicted" -gt 0 ]]; then - echo " ! Redis evicted keys: ${evicted} — consider increasing REDIS_MEMORY_LIMIT" - else - echo " ✓ Redis evictions: 0" - fi - - # --- Summary --- - echo "" - echo "────────────────────────────────────────" - if (( issues == 0 )); then - echo " ✓ All checks passed — system healthy" - else - echo " ✗ ${issues} issue(s) found — review above" - [[ -n "${NOTIFY_WEBHOOK:-}" ]] && \ - curl -fsS -X POST "${NOTIFY_WEBHOOK}" \ - -H "Content-Type: application/json" \ - -d "{\"text\":\"Actools health: ${issues} issue(s) on ${BASE_DOMAIN}\"}" \ - --max-time 10 &>/dev/null || true - fi - echo "" -} diff --git a/modules/migrate/migrate.sh b/modules/migrate/migrate.sh deleted file mode 100644 index cbc9610..0000000 --- a/modules/migrate/migrate.sh +++ /dev/null @@ -1,187 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/migrate/migrate.sh — Phase 3: Zero-Downtime DB Migrations -# Uses gh-ost for large tables (>100k rows), drush updb for smaller ones -# ============================================================================= - -# Run a root mariadb command inside the db container with no password on the host argv. -# The container already holds MARIADB_ROOT_PASSWORD; we expose it to the client as MYSQL_PWD -# *inside* the container. Caller passes mariadb args ($@), e.g. -e "..." or a heredoc on stdin. -db_exec_root() { - docker exec -i actools_db sh -c 'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb -uroot "$@"' _ "$@" -} - -# Pipe-fed root mariadb (e.g. restore): stdin is the piped SQL; $1 is the target database. -# Password from container env (MYSQL_PWD); db name passed positionally. No password on host argv. -db_exec_root_stdin() { - docker exec -i actools_db sh -c 'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb -uroot "$1"' _ "$1" -} - -# Container-exec mariadb-dump AS ROOT, password from the container's own env (no argv exposure). -# Use ONLY where the dump must capture objects the backup user cannot read (migration snapshot, prod clone). -# $@ = dump args (db name, --single-transaction, etc.). Caller pipes stdout to gzip. -db_dump_root() { - docker exec -i actools_db sh -c 'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb-dump -uroot "$@"' _ "$@" -} - - - -INSTALL_DIR="${INSTALL_DIR:-/home/actools}" -MIGRATE_LOG="${INSTALL_DIR}/logs/migrate" - -migrate_plan() { - local env="${1:-prod}" - local db_name="actools_${env}" - - echo "" - echo "=== Migration Plan: ${env} ===" - echo "Database: ${db_name}" - echo "" - - # Check pending Drupal updates - echo "── Pending Drupal Updates ──────────────────" - cd "$INSTALL_DIR" - local pending - pending=$(docker compose exec -T "php_${env}" bash -c \ - "cd /var/www/html/${env} && ./vendor/bin/drush updatedb:status 2>/dev/null" \ - 2>/dev/null || echo "Could not check pending updates") - - if echo "$pending" | grep -q "No database updates"; then - echo " ✓ No pending database updates" - else - echo "$pending" | head -20 - fi - - echo "" - echo "── Large Tables (>100k rows — will use gh-ost) ──" - db_exec_root -sN </dev/null -SELECT - table_name, - table_rows, - ROUND(data_length/1024/1024, 2) AS size_mb -FROM information_schema.tables -WHERE table_schema = '${db_name}' - AND table_rows > 100000 -ORDER BY table_rows DESC; -SQL - - echo "" - echo "── All Tables ──────────────────────────────" - db_exec_root -sN </dev/null -SELECT - table_name, - table_rows, - ROUND(data_length/1024/1024, 2) AS size_mb -FROM information_schema.tables -WHERE table_schema = '${db_name}' -ORDER BY table_rows DESC -LIMIT 20; -SQL - - echo "" - echo "Run 'actools migrate --apply ${env}' to apply pending updates" - echo "Run 'actools migrate --rollback ${env}' to rollback last migration" -} - -migrate_apply() { - local env="${1:-prod}" - local db_name="actools_${env}" - - mkdir -p "$MIGRATE_LOG" - local log_file="${MIGRATE_LOG}/migrate_${env}_$(date +%F_%H%M%S).log" - - echo "" - echo "=== Applying Migrations: ${env} ===" - echo "Log: ${log_file}" - echo "" - - cd "$INSTALL_DIR" - - # Step 1: Pre-migration backup - echo "Step 1/4: Pre-migration backup..." - local snap="${INSTALL_DIR}/backups/pre_migrate_${env}_$(date +%F_%H%M%S).sql.gz" - db_dump_root --single-transaction --quick "${db_name}" | gzip > "$snap" - echo " ✓ Snapshot: ${snap}" - - # Step 2: Check for large tables needing gh-ost - echo "" - echo "Step 2/4: Checking table sizes..." - local large_tables - large_tables=$(db_exec_root -sN </dev/null -SELECT table_name FROM information_schema.tables -WHERE table_schema = '${db_name}' AND table_rows > 100000; -SQL -) - - if [[ -n "$large_tables" ]]; then - echo " ! Large tables detected — gh-ost will be used for schema changes:" - echo "$large_tables" | while read -r t; do echo " - $t"; done - else - echo " ✓ No large tables — standard drush updb will be used" - fi - - # Step 3: Run drush updb - echo "" - echo "Step 3/4: Running drush updatedb..." - docker compose exec -T "php_${env}" bash -c " - cd /var/www/html/${env} - ./vendor/bin/drush updatedb --yes 2>&1 - ./vendor/bin/drush cr 2>&1 - " | tee -a "$log_file" - - # Step 4: Post-migration health check - echo "" - echo "Step 4/4: Post-migration health check..." - local status - status=$(curl -sso /dev/null -w "%{http_code}" --max-time 15 \ - "https://${BASE_DOMAIN}" 2>/dev/null || echo "ERR") - - if [[ "$status" == "200" ]]; then - echo " ✓ Site responding: HTTP ${status}" - echo "" - echo "=== Migration complete ===" - echo " Backup: ${snap}" - echo " Log: ${log_file}" - else - echo " ✗ Site not responding: HTTP ${status}" - echo "" - echo " ROLLBACK: actools migrate --rollback ${env}" - echo " Backup available: ${snap}" - exit 1 - fi -} - -migrate_rollback() { - local env="${1:-prod}" - local db_name="actools_${env}" - - echo "" - echo "=== Rollback: ${env} ===" - - # Find latest pre-migrate backup - local latest - latest=$(ls -t "${INSTALL_DIR}/backups/pre_migrate_${env}_"*.sql.gz 2>/dev/null | head -1) - - if [[ -z "$latest" ]]; then - echo "No pre-migration backup found." - echo "Available backups:" - ls -lht "${INSTALL_DIR}/backups/"*.sql.gz 2>/dev/null | head -5 - exit 1 - fi - - echo "Latest pre-migration backup: ${latest}" - read -rp "Restore ${db_name} from this backup? [y/N] " reply - [[ "$reply" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 0; } - - echo "Restoring..." - cd "$INSTALL_DIR" - db_exec_root \ - -e "DROP DATABASE IF EXISTS \`${db_name}\`; CREATE DATABASE \`${db_name}\` CHARACTER SET utf8mb4;" - gunzip -c "$latest" | db_exec_root_stdin "${db_name}" - - docker compose exec -T "php_${env}" bash -c \ - "cd /var/www/html/${env} && ./vendor/bin/drush cr" - - echo " ✓ Rollback complete" - echo " Run: actools health to verify" -} diff --git a/modules/preflight/disk.sh b/modules/preflight/disk.sh deleted file mode 100644 index 8152cdc..0000000 --- a/modules/preflight/disk.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/preflight/disk.sh — Disk Space Checks -# ============================================================================= - -check_disk() { - section "Disk Check" - AVAILABLE_KB=$(df / | awk 'NR==2 {print $4}') - (( AVAILABLE_KB < 20971520 )) && \ - error "Only $(( AVAILABLE_KB / 1048576 ))GB free. At least 20GB required." - log "Disk OK -- $(( AVAILABLE_KB / 1048576 ))GB free." - - DISK_USE=$(df / | awk 'NR==2 {print $5}' | tr -d '%') - (( DISK_USE > 80 )) && warn "Disk ${DISK_USE}% full -- risk of failure during install." -} diff --git a/modules/preflight/dns.sh b/modules/preflight/dns.sh deleted file mode 100644 index 235b40e..0000000 --- a/modules/preflight/dns.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/preflight/dns.sh — DNS Resolution Checks -# ============================================================================= - -check_dns() { - section "DNS Check" - for subdomain in "${BASE_DOMAIN}" "stg.${BASE_DOMAIN}" "dev.${BASE_DOMAIN}"; do - getent hosts "$subdomain" >/dev/null 2>&1 \ - && log "DNS OK -- ${subdomain}" \ - || warn "DNS MISSING -- ${subdomain}. Let's Encrypt will fail until DNS propagates." - done -} diff --git a/modules/preflight/ram.sh b/modules/preflight/ram.sh deleted file mode 100644 index 0c96fcf..0000000 --- a/modules/preflight/ram.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/preflight/ram.sh — RAM Check + Parallel Install Guard -# ============================================================================= - -check_ram() { - section "RAM Check" - TOTAL_RAM=$(free -m | awk '/Mem:/ {print $2}') - log "Total RAM: ${TOTAL_RAM}MB" - - if [[ "${PARALLEL_INSTALL:-false}" == "true" ]] && (( TOTAL_RAM < 6000 )); then - warn "Only ${TOTAL_RAM}MB RAM -- forcing sequential install (need 6GB+ for parallel)." - PARALLEL_INSTALL=false - fi - - (( TOTAL_RAM < 1024 )) && warn "Less than 1GB RAM -- install may fail on low-memory server." -} diff --git a/modules/storage/s3fs.sh b/modules/storage/s3fs.sh deleted file mode 100644 index 48739ba..0000000 --- a/modules/storage/s3fs.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/storage/s3fs.sh — S3FS Module Installation -# Extracted from actools.sh v9.2 during Phase 1 modular refactor -# ============================================================================= - -install_s3fs() { - local env="$1" - - if [[ "${ENABLE_S3_STORAGE:-false}" == "true" ]]; then - log "Installing S3FS module for ${env}..." - docker compose exec -T "php_${env}" bash -c " - cd /var/www/html/${env} - composer require drupal/s3fs --no-interaction - ./vendor/bin/drush en s3fs --yes 2>/dev/null || true - " 2>/dev/null || warn "S3FS installation failed -- configure manually after install" - log "S3FS installed for ${env}." - fi -} diff --git a/modules/storage/settings_inject.sh b/modules/storage/settings_inject.sh deleted file mode 100644 index 1d13c05..0000000 --- a/modules/storage/settings_inject.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/storage/settings_inject.sh — S3 settings.php Injection -# Extracted from actools.sh v9.2 during Phase 1 modular refactor -# NOTE (Phase 4.5): This module is currently UNWIRED — sourced nowhere, its -# functions have no callers. Live Drupal settings injection is in -# modules/drupal/provision.sh. Paths here are intentionally NOT maintained. -# Wire-in (modular injection) vs delete-as-superseded (inline) is a Phase 5 decision. -# ============================================================================= - -inject_s3_settings() { - local env="$1" - - log "Injecting S3 credentials into settings.php for ${env}..." - docker compose exec -T "php_${env}" bash -c " - CONFIG_FILE=/var/www/html/${env}/sites/default/settings.php - cat >> \"\$CONFIG_FILE\" <<'SETTINGS' - -// S3FS configuration -- injected by actools installer (v9.2) -// Credentials read from container env vars. Never in config export. -\$config['s3fs.settings']['access_key'] = getenv('AWS_ACCESS_KEY_ID') ?: ''; -\$config['s3fs.settings']['secret_key'] = getenv('AWS_SECRET_ACCESS_KEY') ?: ''; -\$config['s3fs.settings']['bucket'] = getenv('S3_BUCKET') ?: ''; -\$config['s3fs.settings']['region'] = getenv('AWS_REGION') ?: 'us-east-1'; -\$config['s3fs.settings']['use_s3_for_public'] = TRUE; -\$config['s3fs.settings']['use_s3_for_private'] = TRUE; - -\$_s3_endpoint = getenv('S3_ENDPOINT_URL'); -if (!empty(\$_s3_endpoint)) { - \$config['s3fs.settings']['use_customhost'] = TRUE; - \$config['s3fs.settings']['hostname'] = \$_s3_endpoint; -} - -\$_cdn_host = getenv('ASSET_CDN_HOST'); -if (!empty(\$_cdn_host)) { - \$config['s3fs.settings']['use_cname'] = TRUE; - \$config['s3fs.settings']['domain'] = \$_cdn_host; -} -SETTINGS - " 2>/dev/null || warn "S3 settings.php injection failed for ${env}" - log "S3 settings injected for ${env} (provider: ${STORAGE_PROVIDER})." -} diff --git a/modules/worker/queue.sh b/modules/worker/queue.sh deleted file mode 100644 index 925c8dd..0000000 --- a/modules/worker/queue.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/worker/queue.sh — Queue Worker Configuration -# Extracted from actools.sh v9.2 during Phase 1 modular refactor -# ============================================================================= - -worker_status() { - cd "$INSTALL_DIR" - docker compose exec worker_prod bash -c \ - "cd /var/www/html/prod && ./vendor/bin/drush queue:list" \ - 2>/dev/null || warn "Worker container not ready or drush not installed yet." -} - -worker_run() { - cd "$INSTALL_DIR" - log "Running queue worker manually on prod..." - docker compose exec worker_prod bash -c \ - "cd /var/www/html/prod && ./vendor/bin/drush queue:run actools_document_export" -} diff --git a/modules/worker/xelatex.sh b/modules/worker/xelatex.sh deleted file mode 100644 index 5813458..0000000 --- a/modules/worker/xelatex.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -# ============================================================================= -# modules/worker/xelatex.sh — XeLaTeX Worker Image -# Extracted from actools.sh v9.2 during Phase 1 modular refactor -# ============================================================================= - -build_worker_image() { - cat > "$INSTALL_DIR/Dockerfile.worker" < references found in the live entry points # (actools.sh, installer/, cli/actools). The union is required because some -# live modules are reached without a ${INSTALL_DIR} source line — e.g. audit, -# copied in and invoked from cli/actools — and because the closure engine +# live modules are reached without a ${INSTALL_DIR} source line from actools.sh +# — e.g. audit, copied in and invoked from cli/actools — and because the closure engine # skips a source target whose file does not exist on disk, while a text grep # of the entry points still sees a freshly-wired reference. #