IR-20261112: fix(sbx-lab,sbx-gits): point the sandbox npm cache at the work volume #952

Merged
pi-landing merged 1 commit from plane/IR-20261112-npmcache into live 2026-11-12 04:20:22 +00:00
Collaborator

What

One environment variable on the sandbox container of two StatefulSets:

app container env
sbx-lab sandbox npm_config_cache=/home/ld/work/.npm-cache
sbx-gits sandbox npm_config_cache=/home/ld/work/.npm-cache

Why — the failure mode this closes

Both sandbox containers run with readOnlyRootFilesystem: true and
HOME=/home/ld, and /home/ld is on that read-only root. npm derives its cache
directory from HOME ($HOME/.npm), so it cannot create it, and any
npm install — a pi package install included — fails on the cache path before
it ever reaches the network. It is not a network, proxy or registry problem, and
it reproduces with no package name at all.

The work volume is writable and already carries the other build caches
(GOCACHE, GOMODCACHE on sbx-lab; the same plus TMPDIR on sbx-gits), so the
npm cache goes next to them. No new mount, no widened permission, no new volume:
the path is inside a mount both pods already have.

Scope

  • core/manifests/apps/sbx-lab/statefulset.yaml — 1 env var + its comment.
  • core/manifests/apps/sbx-gits/statefulset.yaml — 1 env var + its comment.
  • scripts/manifests/tests/test_sbx_npm_cache.py — new offline check (below).
  • .forgejo/workflows/ci.yaml — one step in runner-manifest-test that runs it.

Nothing else moves: no CPU/memory, no mount, no image, no other container.

The check

test_sbx_npm_cache.py renders both app dirs with kubectl kustomize (offline,
no cluster) and asserts on the rendered template:

  • A the render succeeds and yields exactly one StatefulSet per app;
  • B the sandbox container declares npm_config_cache=/home/ld/work/.npm-cache;
  • C the premise still holds — HOME=/home/ld on readOnlyRootFilesystem: true
    (if either moves, the comment this env carries is describing a world that no
    longer exists, so the check must fail rather than stay green);
  • D that path is under a writable mount (a read-only cache moves the
    failure instead of fixing it);
  • E no other container of the pod carries the variable (scope).

--self-check pushes six deliberately broken copies through the same
assertions (env dropped, npm's own $HOME/.npm-cache path, HOME moved, rootfs
made writable, work mount read-only, env leaked to a sidecar) and fails if any
one of them stops being rejected.

Class determination

Dangerous class: YES — and only because of the fourth file.

  • The two manifest paths (core/manifests/apps/sbx-*/statefulset.yaml) and the
    test (scripts/manifests/tests/*.py) match no entry of
    DEFAULT_LANDING_POLICY.dangerousPaths (working-files/.../landing-8c666fbe.ts
    @ 8c666fbe, list at :2913-2962): they are not RBAC, secrets, network policy,
    Terraform, config, migrations or an oversight path.
  • .forgejo/** is on that list (:2917), and it matches
    .forgejo/workflows/ci.yaml (pathMatches :3203-3220,
    classifyChangedPaths :3274-3289). CI is an oversight path: a change to it
    changes what every PR is allowed to merge through.

So class=dangerous here is a choice, stated rather than hidden: the two-line
fix needs no second session, but a check nothing runs is not a check. If the
.forgejo step is dropped, this PR becomes non-dangerous and the test stays as a
hand-run script. Flagging it so the call is explicit.

Merge effect: nothing restarts

Both StatefulSets are updateStrategy: OnDelete. An env var is read only when a
container is created, and OnDelete restarts nothing on its own, so merging
this changes no running pod
: each sandbox picks the variable up at its next
natural recreate. Nobody should bounce a pod to pick this up — sbx-lab and
sbx-gits are shared session sandboxes.

Verification

  • kubectl kustomize core/manifests/apps/sbx-lab → rc 0 (16984 lines rendered);
    .../sbx-gits → rc 0 (22769 lines).
  • python3 scripts/manifests/tests/test_sbx_npm_cache.py → RESULT: PASS (both apps).
  • python3 scripts/manifests/tests/test_sbx_npm_cache.py --self-check →
    self-check: PASS (12 rejections: 6 mutations × 2 apps).
  • CI at head 9c6ad6947680fd321795a85842085ab26a18435f (read from the API): run #3638 [pass] —
    helm-validate, runner-manifest-test (12s: the job that now carries this check), shellcheck,
    yaml-lint, terraform-validate, dispatcher-test, receiver-test, cockpit-test, cockpit-e2e,
    record-sql-test, alert-receiver-test — and run #3639 [pass] (shellcheck,
    terraform-validate, yaml-lint).
## What One environment variable on the `sandbox` container of two StatefulSets: | app | container | env | |---|---|---| | `sbx-lab` | `sandbox` | `npm_config_cache=/home/ld/work/.npm-cache` | | `sbx-gits` | `sandbox` | `npm_config_cache=/home/ld/work/.npm-cache` | ## Why — the failure mode this closes Both sandbox containers run with `readOnlyRootFilesystem: true` **and** `HOME=/home/ld`, and `/home/ld` is on that read-only root. npm derives its cache directory from `HOME` (`$HOME/.npm`), so it cannot create it, and any `npm install` — a pi package install included — fails on the cache path before it ever reaches the network. It is not a network, proxy or registry problem, and it reproduces with no package name at all. The work volume *is* writable and already carries the other build caches (`GOCACHE`, `GOMODCACHE` on sbx-lab; the same plus `TMPDIR` on sbx-gits), so the npm cache goes next to them. No new mount, no widened permission, no new volume: the path is inside a mount both pods already have. ## Scope - `core/manifests/apps/sbx-lab/statefulset.yaml` — 1 env var + its comment. - `core/manifests/apps/sbx-gits/statefulset.yaml` — 1 env var + its comment. - `scripts/manifests/tests/test_sbx_npm_cache.py` — new offline check (below). - `.forgejo/workflows/ci.yaml` — one step in `runner-manifest-test` that runs it. Nothing else moves: no CPU/memory, no mount, no image, no other container. ## The check `test_sbx_npm_cache.py` renders both app dirs with `kubectl kustomize` (offline, no cluster) and asserts on the rendered template: - **A** the render succeeds and yields exactly one StatefulSet per app; - **B** the `sandbox` container declares `npm_config_cache=/home/ld/work/.npm-cache`; - **C** the premise still holds — `HOME=/home/ld` on `readOnlyRootFilesystem: true` (if either moves, the comment this env carries is describing a world that no longer exists, so the check must fail rather than stay green); - **D** that path is under a **writable** mount (a read-only cache moves the failure instead of fixing it); - **E** no other container of the pod carries the variable (scope). `--self-check` pushes six deliberately broken copies through the *same* assertions (env dropped, npm's own `$HOME/.npm-cache` path, HOME moved, rootfs made writable, work mount read-only, env leaked to a sidecar) and fails if any one of them stops being rejected. ## Class determination **Dangerous class: YES — and only because of the fourth file.** - The two manifest paths (`core/manifests/apps/sbx-*/statefulset.yaml`) and the test (`scripts/manifests/tests/*.py`) match **no** entry of `DEFAULT_LANDING_POLICY.dangerousPaths` (`working-files/.../landing-8c666fbe.ts` @ 8c666fbe, list at `:2913-2962`): they are not RBAC, secrets, network policy, Terraform, config, migrations or an oversight path. - `.forgejo/**` *is* on that list (`:2917`), and it matches `.forgejo/workflows/ci.yaml` (`pathMatches` `:3203-3220`, `classifyChangedPaths` `:3274-3289`). CI is an oversight path: a change to it changes what every PR is allowed to merge through. So `class=dangerous` here is a **choice, stated rather than hidden**: the two-line fix needs no second session, but a check nothing runs is not a check. If the `.forgejo` step is dropped, this PR becomes non-dangerous and the test stays as a hand-run script. Flagging it so the call is explicit. ## Merge effect: nothing restarts Both StatefulSets are `updateStrategy: OnDelete`. An env var is read only when a container is created, and `OnDelete` restarts nothing on its own, so **merging this changes no running pod**: each sandbox picks the variable up at its next natural recreate. Nobody should bounce a pod to pick this up — sbx-lab and sbx-gits are shared session sandboxes. ## Verification - `kubectl kustomize core/manifests/apps/sbx-lab` → rc 0 (16984 lines rendered); `.../sbx-gits` → rc 0 (22769 lines). - `python3 scripts/manifests/tests/test_sbx_npm_cache.py` → `RESULT: PASS` (both apps). - `python3 scripts/manifests/tests/test_sbx_npm_cache.py --self-check` → `self-check: PASS` (12 rejections: 6 mutations × 2 apps). - CI at head `9c6ad6947680fd321795a85842085ab26a18435f` (read from the API): run **#3638** `[pass]` — helm-validate, **runner-manifest-test (12s: the job that now carries this check)**, shellcheck, yaml-lint, terraform-validate, dispatcher-test, receiver-test, cockpit-test, cockpit-e2e, record-sql-test, alert-receiver-test — and run **#3639** `[pass]` (shellcheck, terraform-validate, yaml-lint).
IR-20261112: fix(sbx-lab,sbx-gits): point the sandbox npm cache at the work volume
All checks were successful
CI Review / helm-validate (pull_request) Successful in 10s
CI Review / receiver-test (pull_request) Successful in 4s
CI Review / runner-manifest-test (pull_request) Successful in 12s
CI Review / alert-receiver-test (pull_request) Successful in 20s
Lint & Validate / shellcheck (pull_request) Successful in 6s
Lint & Validate / yaml-lint (pull_request) Successful in 27s
CI Review / record-sql-test (pull_request) Successful in 1m23s
Lint & Validate / terraform-validate (pull_request) Successful in 2m36s
CI Review / cockpit-e2e (pull_request) Successful in 3m46s
CI Review / dispatcher-test (pull_request) Successful in 3m52s
CI Review / cockpit-test (pull_request) Successful in 4m5s
9c6ad69476
Both sandbox StatefulSets run the `sandbox` container with
readOnlyRootFilesystem: true and HOME=/home/ld, and /home/ld lives on that
read-only root. npm derives its cache from HOME ($HOME/.npm), cannot create it,
and fails before it ever reaches the network — so every pi/package install in
the container dies on the cache directory.

The work volume IS writable, so the cache goes there, the same treatment
GOCACHE/GOMODCACHE already get:

  sbx-lab/sandbox : npm_config_cache=/home/ld/work/.npm-cache
  sbx-gits/sandbox: npm_config_cache=/home/ld/work/.npm-cache

Nothing else moves: no limits, no mounts, no other container, and sbx-sendrast
is untouched (its HOME is a writable volume mount, so it has no such gap).

updateStrategy is OnDelete on both, so this merge restarts NOTHING: an env var
is only read when the container is created, and both pods pick it up at their
next natural recreate.

scripts/manifests/tests/test_sbx_npm_cache.py asserts, on the `kubectl
kustomize` render of both app dirs, that the env is on the sandbox container
with that value, on a WRITABLE mount, that the premise (HOME + read-only root)
still holds, and that no other container grew the variable; --self-check feeds
six deliberately broken copies through the same assertions. It runs in
ci.yaml's runner-manifest-test job.

Lab-Run: IR-20261112-npmcache
Collaborator

APPROVE - ld-reviewer - head 9c6ad69476

Reviewed the PR body, the full diff, the two manifests and the workflow as they stand at the pinned head, and CI on that head. Item by item:

  1. Scope of the diff — exactly what is claimed, nothing else. Four files, pure additions, zero deleted lines: .forgejo/workflows/ci.yaml (+16), core/manifests/apps/sbx-lab/statefulset.yaml (+9), core/manifests/apps/sbx-gits/statefulset.yaml (+9), scripts/manifests/tests/test_sbx_npm_cache.py (+206, new). No CPU/memory requests or limits, no volume or volumeMount added or altered, no image, no other container, no other app. sbx-sendrast is untouched, and its absence is not a silent omission: the test states why (its HOME is a writable volume mount, so the default cache works there).

  2. The variable lands on the sandbox container only, in both apps (read from each file at the pinned sha). sbx-lab: sandbox opens at :528, npm_config_cache sits at :731-732 inside that container's own env list next to GOCACHE/GOMODCACHE (:720/:722); the pod's other container (skill-sync, :867) has no such env. sbx-gits: sandbox at :747, the variable at :802-803; neither emu-health (:1128) nor emulator (:1205) grows it. Each file carries exactly one occurrence of the name. Value is /home/ld/work/.npm-cache in both, and /home/ld/work is mounted on the sandbox with no readOnly: true, so the cache path is genuinely writable.

  3. The CI step is a real gate, not a no-op. It is a new step in runner-manifest-test (ci.yaml :139-153) that runs the script twice — straight, then --self-check. The straight run renders both app dirs with kubectl kustomize (a non-zero render exits FATAL, so a broken render cannot pass) and asserts on the rendered template that the sandbox container declares npm_config_cache=/home/ld/work/.npm-cache, that HOME=/home/ld and readOnlyRootFilesystem: true still hold, that the path lies under a mount that is not read-only, and that no other container carries the variable. With the env dropped, assertion B fails on None != CACHE_PATH — that is precisely the regression it catches. --self-check re-feeds six mutations (env dropped, npm's own $HOME path, HOME moved, rootfs writable, work mount read-only, env leaked to a sidecar) through the same problems() and fails if any one stops being rejected. A run block that exits non-zero fails the job, so this cannot rot into a green no-op.

  4. No secrets or unexpected content. The only two added value: lines are paths. A scan of the diff for private keys, passwords, tokens, secrets, API keys and base64 blobs returned zero hits; the rest of the added text is explanatory comments and the test's own logic. No credential is read, widened or copied.

  5. CI on the exact head 9c6ad6947680fd321795a85842085ab26a18435f — read from GET /repos/Homelab/infra-core/commits/<sha>/status: combined state success, 11/11 contexts green — helm-validate, runner-manifest-test, receiver-test, dispatcher-test, alert-receiver-test, cockpit-test, cockpit-e2e, record-sql-test, shellcheck, yaml-lint, terraform-validate. runner-manifest-test is the job that carries the new step, so the gate ran at this head. The /statuses list also shows the enqueue-time pending rows from 00:14:42Z; each context's later success supersedes its own placeholder, and the combined status I read is success.

  6. The dangerous-class claim is stated, not hidden. The body has an explicit "Class determination" section declaring dangerous YES and naming the single reason: .forgejo/workflows/ci.yaml matches .forgejo/** on the dangerous-path list, i.e. an oversight path. It also states that the two manifests and the test match no dangerous entry, and that dropping the CI step would make the PR non-dangerous. The policy constants themselves are not in this repo, so I cannot re-derive the list from here; what I can confirm is that the classification is declared up front and its reasoning matches the oversight-path rule. On that basis this is a dangerous-class change and needs two approvals from independent review sessions, not one.

Beyond the checklist: the diagnosis holds — readOnlyRootFilesystem: true with HOME=/home/ld on that root does make $HOME/.npm impossible, so pointing the cache at a mount the pod already has fixes it without a new volume or widened permissions. updateStrategy: OnDelete means merging restarts nothing, which matches the body. I found nothing that must change.

model: omniroute/deepseek-flash
session: IR-20261112-npmcache-seatA-20261112T0232Z

**APPROVE - ld-reviewer - head 9c6ad6947680fd321795a85842085ab26a18435f** Reviewed the PR body, the full diff, the two manifests and the workflow as they stand at the pinned head, and CI on that head. Item by item: 1. **Scope of the diff — exactly what is claimed, nothing else.** Four files, pure additions, zero deleted lines: `.forgejo/workflows/ci.yaml` (+16), `core/manifests/apps/sbx-lab/statefulset.yaml` (+9), `core/manifests/apps/sbx-gits/statefulset.yaml` (+9), `scripts/manifests/tests/test_sbx_npm_cache.py` (+206, new). No CPU/memory requests or limits, no volume or `volumeMount` added or altered, no image, no other container, no other app. `sbx-sendrast` is untouched, and its absence is not a silent omission: the test states why (its HOME is a writable volume mount, so the default cache works there). 2. **The variable lands on the `sandbox` container only, in both apps** (read from each file at the pinned sha). `sbx-lab`: `sandbox` opens at :528, `npm_config_cache` sits at :731-732 inside that container's own env list next to GOCACHE/GOMODCACHE (:720/:722); the pod's other container (`skill-sync`, :867) has no such env. `sbx-gits`: `sandbox` at :747, the variable at :802-803; neither `emu-health` (:1128) nor `emulator` (:1205) grows it. Each file carries exactly one occurrence of the name. Value is `/home/ld/work/.npm-cache` in both, and `/home/ld/work` is mounted on the sandbox with no `readOnly: true`, so the cache path is genuinely writable. 3. **The CI step is a real gate, not a no-op.** It is a new step in `runner-manifest-test` (`ci.yaml` :139-153) that runs the script twice — straight, then `--self-check`. The straight run renders both app dirs with `kubectl kustomize` (a non-zero render exits FATAL, so a broken render cannot pass) and asserts on the *rendered* template that the `sandbox` container declares `npm_config_cache=/home/ld/work/.npm-cache`, that `HOME=/home/ld` and `readOnlyRootFilesystem: true` still hold, that the path lies under a mount that is not read-only, and that no other container carries the variable. With the env dropped, assertion B fails on `None != CACHE_PATH` — that is precisely the regression it catches. `--self-check` re-feeds six mutations (env dropped, npm's own `$HOME` path, HOME moved, rootfs writable, work mount read-only, env leaked to a sidecar) through the same `problems()` and fails if any one stops being rejected. A `run` block that exits non-zero fails the job, so this cannot rot into a green no-op. 4. **No secrets or unexpected content.** The only two added `value:` lines are paths. A scan of the diff for private keys, passwords, tokens, secrets, API keys and base64 blobs returned zero hits; the rest of the added text is explanatory comments and the test's own logic. No credential is read, widened or copied. 5. **CI on the exact head `9c6ad6947680fd321795a85842085ab26a18435f`** — read from `GET /repos/Homelab/infra-core/commits/<sha>/status`: combined state **`success`**, 11/11 contexts green — helm-validate, **runner-manifest-test**, receiver-test, dispatcher-test, alert-receiver-test, cockpit-test, cockpit-e2e, record-sql-test, shellcheck, yaml-lint, terraform-validate. `runner-manifest-test` is the job that carries the new step, so the gate ran at this head. The `/statuses` list also shows the enqueue-time `pending` rows from 00:14:42Z; each context's later `success` supersedes its own placeholder, and the combined status I read is success. 6. **The dangerous-class claim is stated, not hidden.** The body has an explicit "Class determination" section declaring dangerous **YES** and naming the single reason: `.forgejo/workflows/ci.yaml` matches `.forgejo/**` on the dangerous-path list, i.e. an oversight path. It also states that the two manifests and the test match no dangerous entry, and that dropping the CI step would make the PR non-dangerous. The policy constants themselves are not in this repo, so I cannot re-derive the list from here; what I can confirm is that the classification is declared up front and its reasoning matches the oversight-path rule. On that basis this is a dangerous-class change and needs two approvals from independent review sessions, not one. Beyond the checklist: the diagnosis holds — `readOnlyRootFilesystem: true` with `HOME=/home/ld` on that root does make `$HOME/.npm` impossible, so pointing the cache at a mount the pod already has fixes it without a new volume or widened permissions. `updateStrategy: OnDelete` means merging restarts nothing, which matches the body. I found nothing that must change. model: omniroute/deepseek-flash session: IR-20261112-npmcache-seatA-20261112T0232Z
Collaborator

APPROVE — ld-reviewer — head 9c6ad69476

  1. Diff is exactly the claim: 4 files, 240 additions, 0 deletions — .forgejo/workflows/ci.yaml +16, core/manifests/apps/sbx-gits/statefulset.yaml +9, core/manifests/apps/sbx-lab/statefulset.yaml +9, scripts/manifests/tests/test_sbx_npm_cache.py new +206. Each manifest hunk is a comment block plus one name/value env pair; no CPU/memory/limit, no volume or mount, no image, no other container, no whitespace churn. sbx-sendrast appears nowhere in the diff.

  2. Placement verified on the head render, not on the source alone: I ran kubectl kustomize on both app dirs at 9c6ad69 (sbx-lab 16984 lines, sbx-gits 22769 lines — matching the body) and parsed the documents. sbx-lab containers = [sandbox, skill-sync], sbx-gits = [sandbox, emu-health, emulator]. In both, npm_config_cache=/home/ld/work/.npm-cache is on the sandbox container's own env, together with HOME=/home/ld and readOnlyRootFilesystem: true on that same container. No initContainer and no sidecar carries it.

  3. The check bites. I re-implemented A–E against the same rendered documents and got PASS for both apps: one StatefulSet per app with the right name, env on sandbox, cache path under a writable mount (/home/ld/work, mounted without readOnly; the read-only mounts are elsewhere), and no other container carrying the variable. Dropping the env from the parsed template makes the assertion fail (npm_config_cache=None), so the step would go red if the variable were removed. Its dependencies are real on the k8s runner image: kubectl is pinned in core/manifests/apps/forgejo-runner/Dockerfile:72-77 and python3-yaml is installed at :12. The new step sits inside job runner-manifest-test, and ci.yaml runs on pull_request to live/epic/**. The leaked-to-another-container self-check mutation has a real second container to hit in both apps (skill-sync / emu-health+emulator), so that mutation cannot pass vacuously.

  4. No secrets or unexpected content: no token, credential, valueFrom, or secret reference added; the only new literals are the cache path, comments, and the offline checker.

  5. CI at the exact head 9c6ad69476: combined state success, 11/11 contexts success — CI Review / helm-validate (pull_request), CI Review / runner-manifest-test (pull_request) (12s: the job carrying the new check), CI Review / receiver-test, CI Review / alert-receiver-test, CI Review / record-sql-test, CI Review / cockpit-e2e, CI Review / dispatcher-test, CI Review / cockpit-test, Lint & Validate / shellcheck, Lint & Validate / yaml-lint, Lint & Validate / terraform-validate. The two workflow runs whose commit_sha is this head (repo runs #3638 on ci.yaml, #3639 on lint.yaml) are both success; the check-runs endpoint is 404 on this Forgejo, so statuses are the equivalent read.

  6. Class: the body states class=dangerous openly in its own "Class determination" section, names .forgejo/** as the dangerousPaths entry and the ci.yaml step as the only reason for the class, and says so is a choice rather than hidden. Nothing is concealed or downplayed. I also confirmed updateStrategy: type: OnDelete on both StatefulSets in the render, so merging rolls no pod and the variable lands at the next natural recreate.

No problems found; nothing to change.

model: omniroute/deepseek-flash
session: IR-20261112-npmcache-seatB-20261112T0232Z

**APPROVE — ld-reviewer — head 9c6ad6947680fd321795a85842085ab26a18435f** 1. Diff is exactly the claim: 4 files, 240 additions, 0 deletions — `.forgejo/workflows/ci.yaml` +16, `core/manifests/apps/sbx-gits/statefulset.yaml` +9, `core/manifests/apps/sbx-lab/statefulset.yaml` +9, `scripts/manifests/tests/test_sbx_npm_cache.py` new +206. Each manifest hunk is a comment block plus one `name`/`value` env pair; no CPU/memory/limit, no volume or mount, no image, no other container, no whitespace churn. `sbx-sendrast` appears nowhere in the diff. 2. Placement verified on the head render, not on the source alone: I ran `kubectl kustomize` on both app dirs at 9c6ad69 (sbx-lab 16984 lines, sbx-gits 22769 lines — matching the body) and parsed the documents. sbx-lab containers = `[sandbox, skill-sync]`, sbx-gits = `[sandbox, emu-health, emulator]`. In both, `npm_config_cache=/home/ld/work/.npm-cache` is on the `sandbox` container's own `env`, together with `HOME=/home/ld` and `readOnlyRootFilesystem: true` on that same container. No initContainer and no sidecar carries it. 3. The check bites. I re-implemented A–E against the same rendered documents and got PASS for both apps: one StatefulSet per app with the right name, env on `sandbox`, cache path under a writable mount (`/home/ld/work`, mounted without `readOnly`; the read-only mounts are elsewhere), and no other container carrying the variable. Dropping the env from the parsed template makes the assertion fail (`npm_config_cache=None`), so the step would go red if the variable were removed. Its dependencies are real on the k8s runner image: kubectl is pinned in `core/manifests/apps/forgejo-runner/Dockerfile:72-77` and `python3-yaml` is installed at `:12`. The new step sits inside job `runner-manifest-test`, and `ci.yaml` runs on `pull_request` to `live`/`epic/**`. The `leaked-to-another-container` self-check mutation has a real second container to hit in both apps (skill-sync / emu-health+emulator), so that mutation cannot pass vacuously. 4. No secrets or unexpected content: no token, credential, `valueFrom`, or secret reference added; the only new literals are the cache path, comments, and the offline checker. 5. CI at the exact head 9c6ad6947680fd321795a85842085ab26a18435f: combined state `success`, 11/11 contexts success — `CI Review / helm-validate (pull_request)`, `CI Review / runner-manifest-test (pull_request)` (12s: the job carrying the new check), `CI Review / receiver-test`, `CI Review / alert-receiver-test`, `CI Review / record-sql-test`, `CI Review / cockpit-e2e`, `CI Review / dispatcher-test`, `CI Review / cockpit-test`, `Lint & Validate / shellcheck`, `Lint & Validate / yaml-lint`, `Lint & Validate / terraform-validate`. The two workflow runs whose `commit_sha` is this head (repo runs #3638 on ci.yaml, #3639 on lint.yaml) are both `success`; the check-runs endpoint is 404 on this Forgejo, so statuses are the equivalent read. 6. Class: the body states `class=dangerous` openly in its own "Class determination" section, names `.forgejo/**` as the `dangerousPaths` entry and the ci.yaml step as the only reason for the class, and says so is a choice rather than hidden. Nothing is concealed or downplayed. I also confirmed `updateStrategy: type: OnDelete` on both StatefulSets in the render, so merging rolls no pod and the variable lands at the next natural recreate. No problems found; nothing to change. model: omniroute/deepseek-flash session: IR-20261112-npmcache-seatB-20261112T0232Z
Sign in to join this conversation.
No reviewers
No labels
operator-review
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Homelab/infra-core!952
No description provided.