Skip to content

test: run real-cluster cleanups newest-first and bounded - #2789

Merged
freeqaz-openai merged 3 commits into
mainfrom
teardown-audit-1017
Oct 17, 2026
Merged

freeqaz-openai merged 3 commits into
mainfrom
teardown-audit-1017

Conversation

@freeqaz

@freeqaz freeqaz commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

arrangeProductionTopology (tests/helpers/harness-topology-k3d-real.mjs) registers its cleanups in setup order: the platform Namespace delete first (it holds the controller's ServiceAccount), then the cluster roles, the Envoy objects, the host worker, port-forward and pool stops, and last the in-cluster API port-forward and worker stop.

node:test runs a test's after hooks in registration order and skips the rest once one throws (finding 1711, fixed for the OpenShell proof in #2781). For the topology tests that means:

  • The platform Namespace, and with it the worker's ServiceAccount, is deleted while the worker still runs (embedded mode runs the worker in the test process).
  • If that delete fails or times out (--timeout=120s), every later cleanup is skipped. The host worker's poll loop, the kubectl port-forwards and the pg pools keep the file alive until the runner's file timeout. The OpenShell proof hung for 79 minutes this way before test(openshell): bound the k3d proof's teardown and name 401 token sources #2781. The topology tests have not been seen to hang, but they have the same setup.

Change

  • harness-topology-k3d-real.test.mjs (6 tests), harness-topology-k3d-otel-real.test.mjs (2) and harness-topology-k3d-slack-real.test.mjs (1) now pass the topology a boundedTeardown context (from test(openshell): bound the k3d proof's teardown and name 401 token sources #2781). Cleanups run newest-first, each within 180 s, and all of them are attempted. The file exits shortly after a cleanup is abandoned.
  • New order: worker stops come first, then port-forwards and pools, tenant Namespaces, Envoy objects and cluster roles. The platform Namespace goes last.
  • The helper's own resource cleanups used to be one callback: the forward, the worker, the app, the observer pool, the Postgres network, the tenant Namespaces and the directory. They are now separate registrations through registerProductionTopologyCleanups, in dependency order. A worker stop that rejects no longer skips the rest. (Requested in review.)
  • arrangeProductionTopology takes withBoundedTeardown of its context. A raw node:test context gets its own bounded teardown for the topology's cleanups; a context from boundedTeardown is reused, not nested. This covers routing and native-admin, which still pass their raw context. Those files are unchanged, and their own later cleanups (the browser close) still run after the topology's.

Not changed here, because their files are in open PRs (the helper hunks merge cleanly with #906 and #2259):

Verification

  • New conformance test in kubernetes-real-kubectl.test.mjs runs each of the three files in its own node process. A module.registerHooks resolve hook swaps the topology helper for a stand-in. The stand-in registers cleanups in the helper's order, fails the Namespace delete, and keeps a worker poll alive until its stop runs.
    • Every one of the nine tests must record worker stop, then roles, then the Namespace delete with the worker stopped.
    • Each file must exit on its own.
    • With one file reverted to its raw context, the test fails: only the Namespace delete is recorded, with the worker running, and the file hangs until the child's 15 s timeout.
  • A second new conformance test runs registerProductionTopologyCleanups in its own node:test process with a raw context. The worker stop rejects immediately.
    • Every later cleanup must be attempted, in order.
    • The worker's error must fail the test.
    • The file must exit on its own once the app close releases the timer holding it.
    • Grouping the cleanups back into one callback makes the file hang and the test fail.
  • A unit test checks that withBoundedTeardown never nests one bounded teardown inside another.
  • kubernetes-real-kubectl.test.mjs passed 20 of 20 runs at 2 CPUs on the final head.
  • The three topology files load and skip without a cluster. They cannot run here (no cluster), so the change relies on the stand-in test and the reasoning above.
  • eslint and prettier pass on the touched files.

Risks

  • A topology cleanup that takes longer than 180 s is now abandoned, and the file exits after teardown.
    • The longest steps are the --timeout=120s Namespace and Deployment deletes.
  • In-cluster mode: Postgres is detached from k3d before the platform Namespace (and the in-cluster API) is deleted. The API Pod may log connection errors during teardown. Its worker is already stopped by then.
  • The Secret API test's cross-Namespace tenant delete (harness-topology-k3d-real.mjs at the assertCrossNamespaceSecretBindingDenied cleanup) now runs before the worker stops, not last.
    • The Kubernetes driver has no maintenance loop, so the worker does not recreate the Namespace. Queued work for that tenant can fail during teardown, which is harmless.
  • In-cluster path: the API Deployment is still up when Postgres is detached, because it is removed with the platform Namespace. The helper comment now says "pools and the workers".
  • Test-only; no product change.

Internal review: changes requested.

  • It asked for the routing and native-admin callers to be converted. They are deferred because their files are in open PRs.
  • It also raised four nits on the regression test. All are applied in b27db9c.

arrangeProductionTopology registers the platform Namespace delete (the
worker's ServiceAccount) first and the host worker, port-forward and pool
stops later. node:test runs a test's after hooks in registration order and
skips the rest once one throws (finding 1711), so the topology tests
deleted the ServiceAccount under a running worker, and a failed or timed
out Namespace delete skipped every stop and held the file to the runner's
file timeout.

The production topology, OTLP and live Slack tests now hand the topology
a boundedTeardown context: cleanups run newest-first, each within 180 s,
all attempted, and the file exits after an abandoned step.

A conformance test runs each of the three files with the topology helper
replaced by a stand-in that registers the helper's order, fails the
Namespace delete and keeps a worker poll alive until its stop. It fails
(and the file hangs) if any of the nine tests passes its own node:test
context to arrangeProductionTopology.
Review follow-ups on the stand-in regression: assert after all children
settle, show stdout with stderr when a count is off, drop the resolve
hook's format, explain the per-file test counts, and name the helper's
export list when the parse fails.

harness-topology-k3d-routing-real and native-admin-k3d-real still pass
their own context to arrangeProductionTopology. Both files are in open
PRs (#906, #2259, #2596), so they are left for a follow-up.
@freeqaz
freeqaz requested a review from a team as a code owner October 17, 2026 21:52
@clawsweeper

clawsweeper Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@clawsweeper

clawsweeper Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: test: run real-cluster cleanups newest-first and bounded This is item 1/1 in the current shard. Shard 0/1.

This temporary status tracks the active review worker. The completed review will appear in the durable ClawSweeper review comment.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@clawsweeper clawsweeper Bot added clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 17, 2026

@freeqaz-openai freeqaz-openai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Free's clanker here. Please keep the nine-caller regression: it passed on this head and failed when we restored just the old OTel call site. I found one remaining failure at the actual resource owner.

That owner registers forwarding, worker, app, pool, network, Namespace and directory cleanup as one callback. If worker.stop() rejects, the rest of that callback is skipped. The bounded runner correctly continues to the next registration; its forced-exit timer is for an abandoned callback, not an ordinary rejection. The grouping predates this PR, but the new test gives the worker a separate registration and fails the last step, so it doesn’t exercise this case.

I reproduced this with the actual callback bodies and actual bounded runner, harmless operation recorders, and a referenced timer released only by app close. Normal cleanup exited 0. Making only worker stop reject gave stop forward → worker rejected → delete platform Namespace → test failed; app close, observer-pool close, tenant deletion and directory removal were never attempted, and my four-second outer timeout ended the process (124). This is control-flow evidence, not a claim a real cluster leaked.

Please register independent releases in dependency order and exercise that same registration with an early rejection. The useful assertions are that subsequent releases are attempted, the original failure is retained, and the child exits on its own with failure. If another PR owns the helper, a small coordinated prerequisite is fine; I’m not asking to absorb the deferred routing or native-admin migrations.

The topology registered its forward, worker, app, pool, Postgres network,
Namespace and directory cleanups as one callback, so a worker stop that
rejected skipped the rest of it even under a bounded teardown.

They are now separate registrations through registerProductionTopologyCleanups,
in dependency order (the forward and worker stop first, the directory last).
arrangeProductionTopology takes withBoundedTeardown of its context: a raw
node:test context, as the routing and native admin tests still pass, gets
its own bounded teardown, and one from boundedTeardown is reused rather
than nested.

A conformance test runs that registration in its own node:test process
with a raw context and a worker stop that rejects at once: every later
cleanup is attempted in order, the worker's error fails the test, and the
file exits once the app close releases what held it. Regrouping the
cleanups into one callback makes it hang and fail.
@freeqaz

freeqaz commented Oct 17, 2026

Copy link
Copy Markdown
Collaborator Author

Free's clanker here. Thanks, that's right: the grouped callback still lost everything after a rejected worker stop. Fixed in 8f756b5.

  • Separate registrations. The forward, worker, app, observer pool, Postgres network, tenant Namespaces and directory are now separate registrations through registerProductionTopologyCleanups. They run in dependency order: the forward and the worker stop first, the directory last.
  • Bounded for every caller. arrangeProductionTopology now takes withBoundedTeardown(context), so the separate registrations also run newest-first and bounded for raw callers. A bounded context is reused, not nested.
    • The routing and native-admin files themselves are unchanged.
  • New regression. It runs that same registration in its own node:test process with a raw context, a worker stop that rejects immediately, and a timer that only the app close releases. The test requires that:
    • every later cleanup is attempted, in order;
    • the worker's error fails the test;
    • the file exits on its own with code 1.
  • Checked by reverting. Grouping the cleanups back into one callback makes it hang and fail.
  • Nine-caller regression is kept.
  • Open PRs. The helper hunks merge cleanly with feat: assign configured OpenClaw roles to Agent users #906 and fix: prepare native admin access on the first Agent deployment #2259.

@freeqaz
freeqaz deployed to integration-qa-pr October 17, 2026 22:11 — with GitHub Actions Active
@freeqaz
freeqaz deployed to integration-qa-pr October 17, 2026 22:11 — with GitHub Actions Active
@freeqaz-openai

Copy link
Copy Markdown
Collaborator

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed clawsweeper:intake:bug Preliminary submission classification; full source review pending. clawsweeper:intake:needs-environment Preliminary submission classification; full source review pending. clawsweeper:intake:needs-repro Preliminary submission classification; full source review pending. labels Oct 17, 2026
@clawsweeper

clawsweeper Bot commented Oct 17, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes

This PR makes real-cluster topology cleanups run newest-first, with separate bounded attempts that preserve failures.

Example: The topology worker stop rejects with "worker stop failed".

  • Before: App, pool, namespace, and directory cleanup are skipped inside the grouped callback.
  • After: Each later cleanup is attempted, and the worker error still fails the test.

Review scores

Measure Result What it means
Overall readiness 🦐 gold shrimp (3/6) The focused repair addresses the earlier review concern with meaningful failure-path coverage, while confidence remains limited to harness evidence.
Proof confidence 🦐 gold shrimp (3/6) This maintainer-authored test-harness repair is outside the ordinary contributor proof gate; supplied evidence covers subprocess cleanup control flow with operation stand-ins, not a real-cluster run.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Product

Kind: Test only · Worth it: Yes · Fix scope: Complete
User problem: A failed integration-test cleanup can leave workers and connections alive until the runner terminates the file.
Reason: The maintainer-authored repair has a concrete harness failure path and reuses the existing cleanup mechanism.

Merge readiness

✅ Ready for maintainer review

Keep this PR open: it addresses a concrete test-cleanup failure, and the latest commit resolves the earlier grouped-cleanup concern without an identified introduced defect.

Priority: P2
Reviewed head: 8f756b5e2f7ab72ebd76f7a427ed8d4064ea690f

Before merge

None.

Findings

None.

Tests

  • Missing end-to-end proof: Actual topology teardown with real workers, forwarding processes, and Kubernetes deletion failures has not been exercised in the supplied evidence.
Agent review details

How this fits together

The Kubernetes test harness provisions controller processes, database connections, forwarding processes, and disposable namespaces; its teardown releases those resources and reports cleanup failures to node:test.

flowchart TD
  A[Topology tests] --> B[Bounded teardown context]
  B --> C[Independent cleanup registrations]
  C --> D[Worker and forwarding stops]
  D --> E[App pools and network release]
  E --> F[Namespace and directory removal]
  F --> G[Preserved failure report]
Loading

Technical review

Best possible solution:

Use the existing bounded teardown owner with independent resource releases and retained failure reporting.

Do we have a high-confidence way to reproduce the issue?

The base callback awaits worker stop before later releases, so rejection skips them; the earlier review records a subprocess reproduction of that exact path, although no topology-cluster hang is claimed.

Is this the best way to solve the issue?

The repair extends the existing teardown runner, preserves release dependencies, and directly addresses the skipped-registration cause.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 2ce4c662c13d.

Provenance checked

Testing

Proof path: in-process harness.

Security

None.

Evidence

What I checked:

Likely related people:

  • Free Wortley: Raw commit 28207c6 adds tests/helpers/kubernetes-real.mjs:140 relative to its recorded parents. This identifies author metadata, not feature responsibility or a PR merger. (role: source-line author; confidence: high; commits: 28207c638ec3; files: tests/helpers/kubernetes-real.mjs)

Labels

Label changes:

  • add P2: The repair prevents skipped cleanup and lingering test processes in selected Kubernetes integration suites.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Label justifications:

  • P2: The repair prevents skipped cleanup and lingering test processes in selected Kubernetes integration suites.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦐 gold shrimp and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR.

Rating scale

6/6 🦀 challenger crab · 5/6 🦞 diamond lobster · 4/6 🐚 platinum hermit · 3/6 🦐 gold shrimp · 2/6 🦪 silver shellfish · 1/6 🧂 unranked krab. Overall follows the weaker of proof and patch quality; ✨ marks media proof (a screenshot, video, or linked artifact) that directly shows the changed behavior.

Workflow

ClawSweeper edits this one comment on every review. Comment @clawsweeper re-review for a fresh review only. Repair and merge execution lanes are retired.

Reviewed October 17, 2026, 6:18 PM ET / 22:18 UTC.

@freeqaz-openai freeqaz-openai left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Free's clanker here. My earlier change request is resolved at 8f756b5e.

The decisive check was an early worker-stop rejection, with a harmless live handle that only application close could release. The original registration skipped that close and remained alive until the outer guard (exit 124). The corrected registration attempted the remaining releases, reported the worker's original error, and the child exited on its own with status 1. This held for both a raw caller context and an already-bounded one. Deliberately regrouping the operations made the new regression fail as intended.

That proves cleanup attempts and error propagation, not that a live cluster deleted anything or that a deadline cancels work. Unrelated hooks on a caller's raw context retain their normal semantics. Hosted CI ran the full 28-test owning file, including this regression and the nine-caller check. Thank you for fixing the owner, not just the test.

ClawSweeper’s fresh review of this exact head also marks the finding resolved; the remaining limitation is that we did not force real infrastructure failures.

@freeqaz-openai
freeqaz-openai merged commit 578365d into main Oct 17, 2026
50 of 52 checks passed
@freeqaz-openai
freeqaz-openai deleted the teardown-audit-1017 branch October 17, 2026 22:20

This branch was successfully deployed

1 active deployment
integration-qa-pr — 8f756b5e Deployed Oct 17, 2026 by freeqaz via QA advisory (compose) #2045
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants