Skip to content

Commit 65a033b

Browse files
LTSCommerceclaude
andcommitted
Plan 00055: relocate acceptance script into plan folder + add deploy/triage
Apply the new plan-local-scripts convention to Plan 00055: - git mv scripts/acceptance-container-watch.bash → CLAUDE/Plan/00055-.../acceptance.bash (now resolves repo root via `git rev-parse --show-toplevel` instead of a fixed ../ hop, since it sits deeper in the tree). - Add deploy.bash (runs the opt-in play on the HOST) and triage.bash (forces a scan, shows the timer schedule + findings). Both fail-fast, bash -n + shellcheck clean. - Update PLAN.md / testing.md / testing-checklist.md references to the new in-folder path. - Task 4.2 → In Progress: deploy confirmed on HOST (timer registered + fired, oneshot service runs, status/list OK — 0 findings). Panel + L2/L3 HOST runs still pending. The permanent no-kill QA gate stays at scripts/qa-nokill-containerwatch.bash and the regression suite stays under tests/helpers/containerwatch/ — those are persistent deliverables, not plan scaffolding. Refs: CLAUDE/Plan/00055-container-process-watchdog Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ec250c8 commit 65a033b

6 files changed

Lines changed: 94 additions & 9 deletions

File tree

CLAUDE/Plan/00055-container-process-watchdog/PLAN.md

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,14 @@ decisions.
144144
every file). If instead using an explicit per-file loop (like `play-speech-to-text.yml`),
145145
**enumerate every file** (`extension.js` + `metadata.json` + any `prefs.js`/schemas) —
146146
any file omitted from the loop silently never deploys.
147-
- [ ]**Task 4.2**: (On HOST, not CCY) deploy + verify timer fires, report
148-
generates, panel + CLI both show a synthetic finding.
147+
- [ ] 🔄 **Task 4.2**: (On HOST, not CCY) deploy + verify. Use the plan-local
148+
`deploy.bash` (runs the play) then `triage.bash` (forces a scan, shows the timer
149+
schedule + findings). **Partially confirmed on HOST**: deploy succeeded, the
150+
`container-watch.timer` is registered and has fired, the oneshot service runs, and
151+
`container-watch status`/`list` work (`OK — 0 findings`, the healthy steady state).
152+
**Still to confirm**: panel + notification react to a finding (drive via
153+
`container-watch scan --inject` per `testing-checklist.md`), i.e. the L3 visual
154+
pass and the L2 `acceptance.bash` run on the HOST.
149155

150156
### Phase 5: Testing & acceptance (full process in [`testing.md`](testing.md))
151157

@@ -184,7 +190,7 @@ decisions.
184190
workspace path, or argv captured from an actual incident. (`report.json` itself is a
185191
runtime artifact under `$XDG_RUNTIME_DIR`, never committed — the leak surface is the
186192
committed fixtures, which can bypass the email/IP-shaped commit scanner.)
187-
- [x]**Task 5.3**: L2 host integration — `scripts/acceptance-container-watch.bash`
193+
- [x]**Task 5.3**: L2 host integration — `acceptance.bash` (in this plan folder)
188194
written (threshold-overridden `CW_AGE_S=1 CW_CPU_PCT=5`, engine-gated, spins a
189195
throwaway CPU-burner per available engine and asserts flag + attribution +
190196
`exec_hint` + NSpid + the behavioural **safety** assert (burner survives) +
@@ -341,7 +347,7 @@ decisions.
341347

342348
### 2026-06-24 (implementation — Phase 5 L2 + L3 authored; all in-container work done)
343349

344-
- **Phase 5 L2**: `scripts/acceptance-container-watch.bash` — host-only,
350+
- **Phase 5 L2**: `acceptance.bash` (in this plan folder) — host-only,
345351
engine-gated, fail-fast acceptance test (burner per engine, full assertion set
346352
incl. the behavioural survive-assert, allowlist, DBus, `systemd-analyze --user verify`, trap cleanup). `bash -n` + shellcheck clean; in `qa-all.bash` bash gate.
347353
- **Phase 5 L3**: `testing-checklist.md` — numbered nested-GNOME visual checklist
@@ -353,6 +359,23 @@ decisions.
353359
**In Progress** because the remaining work is HOST-only and cannot run in this
354360
container: **Task 4.2** (`ansible-playbook play-container-watch.yml`, then confirm
355361
the timer fires + panel/CLI show a finding) and the **HOST execution** of L2
356-
(`scripts/acceptance-container-watch.bash`) and L3 (`testing-checklist.md`). Once
362+
(`acceptance.bash`) and L3 (`testing-checklist.md`). Once
357363
those pass on the HOST, mark Task 4.2 ✅, set the plan Complete, and move it to
358364
`CLAUDE/Plan/Completed/`.
365+
366+
### 2026-06-24 (HOST deploy confirmed + plan-local-scripts convention)
367+
368+
- **Deployed on HOST**: `deploy.bash` ran the play cleanly; `container-watch.timer`
369+
is registered and has fired; the oneshot service runs; `container-watch status`/`list` return `OK — 0 findings` (correct healthy state — no runaway
370+
containers present). Task 4.2 moved to 🔄 (deploy/timer/CLI confirmed; panel +
371+
acceptance run still pending on HOST).
372+
- **New convention — plan-local scripts (set in stone)**: the L2 script was
373+
relocated `scripts/acceptance-container-watch.bash`
374+
`acceptance.bash` (this folder), and `deploy.bash` + `triage.bash` were added
375+
here. Transient, plan-specific scripts/artifacts now live in the plan folder, not
376+
the repo root; only persistently-useful tooling (e.g. the permanent
377+
`scripts/qa-nokill-containerwatch.bash` gate, the `tests/helpers/containerwatch/`
378+
regression suite) stays in its normal home. The rule is encoded in
379+
`CLAUDE/PlanWorkflow.md` and `CLAUDE/Plan/CLAUDE.md`. All three plan scripts
380+
resolve the repo root via `git rev-parse --show-toplevel` and pass `bash -n` +
381+
shellcheck (error-level).

CLAUDE/Plan/00055-container-process-watchdog/acceptance.bash

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ need jq
101101
need timeout
102102

103103
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
104-
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
104+
# This script lives in its plan folder (CLAUDE/Plan/NNNNN-*/), not at repo root,
105+
# so resolve the repo root via git rather than a fixed parent-dir hop.
106+
if ! REPO_ROOT="$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel 2>&1)"; then
107+
echo "Cannot locate repo root (not a git checkout?): $REPO_ROOT" >&2
108+
exit 1
109+
fi
105110

106111
# Resolve the CLI robustly: prefer the deployed wrapper on PATH; else the module
107112
# form. CW[] is the argv prefix every invocation reuses.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/usr/bin/env bash
2+
# Plan 00055 — deploy the container-process watchdog on the HOST.
3+
#
4+
# HOST-ONLY: this runs Ansible. NEVER run it inside the CCY container (/workspace)
5+
# — per CLAUDE/ContainerRules.md the container is edit-only; deployment happens on
6+
# the Fedora host. Runs the opt-in play that installs the helper, the
7+
# container-watch CLI wrapper, the systemd --user timer, and the GNOME panel
8+
# extension. Idempotent — safe to re-run. Extra args pass through to
9+
# ansible-playbook (e.g. --check, --diff, -t <tag>).
10+
#
11+
# ./CLAUDE/Plan/00055-container-process-watchdog/deploy.bash
12+
# ./CLAUDE/Plan/00055-container-process-watchdog/deploy.bash --check --diff
13+
set -euo pipefail
14+
15+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16+
if ! REPO_ROOT="$(cd "$SCRIPT_DIR" && git rev-parse --show-toplevel 2>&1)"; then
17+
echo "Cannot locate repo root (not a git checkout?): $REPO_ROOT" >&2
18+
exit 1
19+
fi
20+
cd "$REPO_ROOT"
21+
22+
exec ansible-playbook playbooks/imports/optional/common/play-container-watch.yml "$@"

CLAUDE/Plan/00055-container-process-watchdog/testing-checklist.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ react, and was the guidance usable?* Everything else is covered by L0/L1/L2.
77

88
> **Prerequisite**: L0 (`./scripts/qa-all.bash`, ESLint, `check_extension_compat`,
99
> the no-kill guard), L1 (`./scripts/qa-helper-tests.bash`), and L2
10-
> (`./scripts/acceptance-container-watch.bash`) are all green. This checklist is
10+
> (`acceptance.bash` in this plan folder) are all green. This checklist is
1111
> the **final** gate, run only after the automated layers pass.
1212
1313
> **Public-repo note**: every identifier below is a reserved placeholder

CLAUDE/Plan/00055-container-process-watchdog/testing.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ is a fixture `proc_root` directory with `<pid>/{cgroup,stat,status,cmdline}` fil
150150

151151
## 4. L2 — Host integration acceptance script (HOST, 100% automated)
152152

153-
A single fail-fast script, e.g. `scripts/acceptance-container-watch.bash`
153+
A single fail-fast script, `acceptance.bash` in this plan folder
154154
(host-only — **not** in the CCY container; it starts real containers). It prints a
155155
PASS/FAIL line per assertion and exits non-zero on any failure. It overrides
156156
thresholds (`CW_AGE_S=1 CW_CPU_PCT=5`) so nothing waits.
@@ -238,7 +238,7 @@ auditable.
238238
guard**.
239239
- [ ] L1 green: unit suite via `./scripts/qa-helper-tests.bash` — full attribution
240240
matrix (all engines) + detection + schema + `comm`/NSpid/allowlist cases.
241-
- [ ] L2 green: `scripts/acceptance-container-watch.bash` PASS for every **present**
241+
- [ ] L2 green: `acceptance.bash` (this plan folder) PASS for every **present**
242242
engine, incl. the behavioural **safety** assert (process survives) and DBus +
243243
systemd checks; no stray test containers left.
244244
- [ ] L3 confirmed: GNOME panel/notification visual pass (5a) + one real guided
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env bash
2+
# Plan 00055 — post-deploy triage for the container-process watchdog.
3+
#
4+
# HOST-ONLY: confirms the deployed watchdog is healthy on the Fedora host. Run it
5+
# after deploy.bash (and any time you want to re-check). It forces one scan and
6+
# prints the timer schedule + current findings. The watchdog is reporting-only, so
7+
# "forcing a scan" only writes a report + emits a signal — it never kills anything.
8+
#
9+
# Healthy output with no runaway containers is "OK - 0 findings": that is a PASS,
10+
# not a problem — it means nothing in any container is currently long-running AND
11+
# CPU-pinned.
12+
#
13+
# ./CLAUDE/Plan/00055-container-process-watchdog/triage.bash
14+
set -euo pipefail
15+
16+
if ! command -v container-watch >/dev/null; then
17+
echo "container-watch not on PATH — has deploy.bash run, and is ~/.local/bin on PATH?" >&2
18+
exit 1
19+
fi
20+
21+
echo "== run a scan now (oneshot service) =="
22+
systemctl --user start container-watch.service
23+
24+
echo
25+
echo "== timer schedule =="
26+
systemctl --user list-timers container-watch.timer --no-pager --all
27+
28+
echo
29+
echo "== current findings =="
30+
container-watch status
31+
container-watch list
32+
33+
echo
34+
echo "Triage OK. (0 findings is the healthy steady state — a finding only appears"
35+
echo "when a container process is BOTH >= CW_AGE_S old AND CPU-pinned.)"

0 commit comments

Comments
 (0)