本文へ移動
cccskills
無料GitHub で公開

storm

Diagnose and CURE storms that wedge the portal — resubscribe/retry storms, NotFound/DeliveryFailure storms, per-instance-hub accumulation, create-in-render loops. A storm makes the process unresponsive → liveness /healthz times out → the pod is pulled from the Service → ingress returns 502 → SIGKILL + restart. Use when a portal pod restarts/502s, memory climbs unbounded, the log volume explodes, or you are reviewing reactive code that could re-fire on every emission. Storms come from exactly two roots: (1) sync-over-async blocking, (2) uncaught / not-forwarded exceptions or delivery failures. Grounded in ErrorPropagationAndWedges, AccessContextPropagation.md; pairs with the /async skill.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaverスター 12(GitHub 12 + cccskills 0)
連携ツールKubernetes

実行スコア 未検証

インストール方法を見る

含まれるファイル(1)

  • SKILL.md11.2 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

/storm — Storms wedge the portal. Cure the root, never the symptom.

A storm is the same cheap operation re-firing without bound — a resubscribe, a retry, a NotFound re-post, a thread/hub re-activation, a write that fails and is retried. Individually each is fast; together they starve the scheduler / exhaust memory / pin the action block. The process stops responding → the k8s liveness probe /healthz times out → the pod is removed from the Service endpoints → the ingress has no healthy backend → 502 → liveness SIGKILLs (exit 137) → restart. Users see a 502 outage. This must never happen.

Canonical references:

The two roots (there are only two)

Every storm traces to one of these. Find which, fix THAT:

  1. Sync-over-async / blocking. async/await/Task<T>/.ToTask()/.FirstAsync()(blocking)/ .Wait()/.Result/.GetAwaiter().GetResult()/Observable.FromAsync/SemaphoreSlim on a hub action block, grain turn, or Blazor circuit. It parks the scheduler; the awaited message can never be processed → deadlock that presents as a hang. → fix per /async.

  2. Uncaught / not-forwarded exceptions or delivery failures. A write fails (often because AccessContext was lost across a .Subscribe/IIoPool hop → RLS denies), a stream errors, a request gets NotFound/DeliveryFailure — and the error is swallowed (.Catch(Observable. Empty), catch {}, _ => { /* ignore */ }) or retried (a 1 s ticker, a watchdog, a resubscribe, a .Retry()) instead of surfaced. The amplifier turns one mishandled error into a flood. (The 2026-06-08 outage was an initial-state retry watchdog; the 2026-06-25 outage was thread-hub accumulation + a swallowed-and-retried heartbeat read.)

The cure (one principle)

Surface errors until you reach a boundary where you can error gracefully — then error gracefully. Do NOT swallow, and do NOT retry a "shouldn't happen" state. Forward the error up the reactive chain (OnError) until it reaches a real sink: an activity's terminal Status = Failed, the GUI error area / PortalErrorSink, a thread's output cell, a logged warning at a known boundary. Everything before that sink must forward, not absorb.

Band-aids that are FORBIDDEN as "the fix" (they hide the storm): raising a timeout/pool size/retry count; a watchdog/poller that resubscribes; .Catch(Observable.Empty); catch {}; a Clear() "for isolation"; a sleep. See AGENTS.md → "No band-aids".

Diagnose a live/just-happened storm (the procedure)

The portal hung silently while wedged (a deadlocked/starved process stops logging), then SIGTERM at shutdown emits a mass-disposal storm that can rotate the pre-hang logs out of the buffer. Work the timeline:

🚨 Break-glass reads. Restart counts, pod events and the previous container's log are exactly the three questions the control instance's Hosting API does not answer yet (measured 2026-09-08: Hosting/DeploymentStatus and Hosting/LogEntry exist as types with zero instances). Take them read-only through az aks command invoke, name them as break-glass in what you write down, and never turn the cure into a kubectl write — a restart is a Restart Hosting/InstanceAction. Policy: OperatingFromThePortal.md.

# 1. Did the pod restart, and how? exit 137 + "failed liveness probe" = wedge (not OOMKilled).
az aks command invoke -g <rg> -n <cluster> --command \
  "kubectl get pod <portal-pod> -n <ns> -o jsonpath='{.status.containerStatuses[0].restartCount}{\" \"}{.status.containerStatuses[0].lastState}'; \
   kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -30"

# 2. Find the SILENCE = the hang window. Per-minute line counts around the kill.
#    Zero lines for minutes, then a huge burst in the final minute = hung, then mass disposal.
az aks command invoke ... --command \
  "L=\$(kubectl logs <pod> -n <ns> --previous --timestamps 2>/dev/null); \
   for m in 45 46 47 48 49 50 51 52 53 54 55 56; do echo -n \"HH:\$m \"; echo \"\$L\" | grep -c \"THH:\$m\"; done"

# 3. What stormed? Source frequency in the wedge/disposal window; storm counts.
az aks command invoke ... --command \
  "L=\$(kubectl logs <pod> -n <ns> --previous 2>/dev/null); \
   echo \"\$L\" | grep -E 'fail:|warn:' | grep -oE '(fail|warn): [A-Za-z0-9._]+' | sort | uniq -c | sort -rn | head; \
   echo -n 'NotFound: '; echo \"\$L\" | grep -c NotFound; \
   echo -n 'distinct sync hubs disposed: '; echo \"\$L\" | grep -oE 'sync/[A-Za-z0-9_-]+' | sort -u | wc -l"

# 4. Distinct vs looping. Is it N things created (creation storm) or 1 thing re-firing (loop)?
az aks command invoke ... --command \
  "kubectl logs <pod> -n <ns> --previous 2>/dev/null | grep -oE '<the-repeating-token>' | sort | uniq -c | sort -rn | head"

# 5. Is the CURRENT (restarted) container re-accumulating? memory trend + the repeating block.
az aks command invoke ... --command "kubectl top pod -n <ns>; kubectl logs <pod> -n <ns> --since=60m | grep -A1 '<token>' | head -30"

Read the last lines before the silence (grep -v 'THH:<kill-minute>' | tail -50) — the last thing logged before it froze is the trigger. If those are rotated out, the mass-disposal volume (distinct sync/ hubs) tells you the scale of the accumulation, and step 5 tells you whether it's still happening.

Storm taxonomy — tell → cure

StormTell in logs / codeCure
Resubscribe / initial-state retryObservable.Interval/Timer or .Catch that re-subscribes the source on a "shouldn't happen" state; repeated SubscribeRequestDelete the retry; let the error surface. If the initial state is genuinely missing, find why it's dropped/errored.
Swallow-and-retry_ => { /* swallow; next tick retries */ } on a per-tick read; .Catch(Observable.Empty) inside a loopForward the error to a sink; drop the path from the retry set on terminal failure; don't re-read a NotFound every tick.
NotFound / DeliveryFailure[ROUTE] NotFound repeating; ownerless satellite (_Thread/_Activity/… with no real owner); a path advertised before its CreateNode completedNever advertise a node path until Create completes (stamp inside .Subscribe). Anchor satellites under a real owner. Lean on the MeshNodeStreamCache negative cache.
Fan-out to a dead subscriber (#2426/#2546)[ROUTE] Stream-routed delivery to 'portal/…' / 'node/…' has no live subscriber at the change-feed rate for hours after a restart/disconnect; senders are per-node owners; "a NACK has no NACK of its own"Not a retry — the owner's server-side stream is immortal (only an UnsubscribeRequest disposes it). The router's refusal is stamped DeliveryFailure.TargetUnserved, is delivered to a grain-hosted sender over the grain transport, and the owner EVICTS that subscriber's streams (Workspace.EvictClientSubscriptions). Never a negative cache on the delivery path; the LOG is windowed (DeadTargetRefusalLog).
Per-instance hub accumulationthousands of sync/ hubs at disposal; a per-hub Observable.Interval heartbeat that runs until dispose; memory climbingEnsure per-instance hubs (thread/round/sync) are disposed when their round/thread reaches terminal state, not only at mesh shutdown. A live timer must not outlive its purpose.
Create-in-renderStartThread/SubmitMessage/CreateNode inside a .Select/.Subscribe on GetMeshNodeStream(...) that re-fires every emissionMove creation out of the render/subscription; one-shot it (.Take(1)/init) or make it idempotent (deterministic path), guard with DistinctUntilChanged.
Sync-over-async deadlock/healthz timeout with no error; .Result/.Wait()/.ToTask() on a hub/circuitPer /async: compose IObservable + Subscribe; push the leaf to IIoPool.

Where to look first (highest-yield)

Storms cluster where an error crosses an async boundary and isn't forwarded — usually because AccessContext was lost (write fails closed → swallowed → retried). Grep for the amplifiers:

rg -n "Catch\(Observable\.Empty|Catch<[^>]+>\(_ *=> *Observable\.Empty|catch *\{\s*\}|/\* *swallow|/\* *ignore|\.Retry\(" src
rg -n "Observable\.Interval|Observable\.Timer\(|RegisterForDisposal\(_ *=> *sub" src   # unbounded tickers — does the sub die with its purpose?
rg -n "\.Subscribe\(\s*_? *=> *\{ *\}\s*,\s*_? *=> *\{\s*\}\)" src                      # write with both arms empty = swallowed failure

For each hit decide: graceful sink (logged/surfaced at a real boundary → OK) or silent swallow / blind retry (→ fix: surface, or stop retrying a terminal state). When the swallowed thing is a write, check the /async AccessContext rule first — the failure is usually a fail-closed RLS denial from a lost identity, and the real fix is to carry the context, then let any remaining error surface.

Checklist before merging reactive code

  • No blocking async on a hub/grain/circuit (.Result/.Wait()/.ToTask()/.FirstAsync()-blocking/Observable.FromAsync/SemaphoreSlim). → /async
  • Every .Subscribe write has an onError that surfaces or logs at a real boundary — never an empty swallow inside a loop/ticker.
  • No timer/watchdog/.Catch that re-triggers a "shouldn't happen" state. If state is missing, fix the source, don't retry.
  • Writes on .Subscribe/IIoPool/Observable.Create hops carry AccessContext (else fail-closed → swallow → storm).
  • Node paths are advertised only after CreateNode completes; satellites anchor under a real owner.
  • Per-instance hubs / timers are disposed at terminal state, not only at mesh shutdown.
  • If a bound exists (top-N, no-retry, sampling), it's log()-ged — silent truncation reads as success.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

async

無料

Forwarding AccessContext through async/reactive boundaries, and the no-async rule on the actor-model mesh. Use when writing or reviewing any hub-reachable / Blazor-view / agent-round / IIoPool code that creates, reads, or updates a mesh node, OR when a write "silently does nothing" / "Access denied" appears after a .Subscribe / IIoPool / Observable.Create hop. The async/await/ToTask/.Wait/.Result/.GetAwaiter/FirstAsync family and lost AccessContext are the two ways async boundaries cause wedges and storms. Grounded in AsynchronousCalls.md, SyncedMeshNodeQueries.md, AccessContextPropagation.md.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

ci

無料

What CI proves, what it cannot prove, and how to write a gate that cannot pass on no evidence. Use when authoring or changing a GitHub Actions workflow, adding a required check, wiring a satellite repo CI, or reasoning about whether a green run actually covers your change. Covers the absolute no-skip-trapdoor rule (a gate must never carry continue-on-error on its input step nor an if that asks whether a secret is set — GitHub paints a skipped job the same colour as a passed one), the reusable workflow_call jobs satellite repos must call instead of hand-rolling, why runs on main are deliberately never cancelled, and the large body of in-mesh C# that no dotnet build or test ever type-checks.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

debug

無料

Debug a deadlock / hang / timeout in the mesh — a test that times out, a hub handler whose response never arrives, a GetRemoteStream / GetMeshNodeStream / GetQuery that never emits, an agent round or per-instance-hub activation that parks forever. Use whenever something times out and you need to find WHERE the message flow breaks down. The timeout is the symptom, never the bug — raising it just hangs longer. Grounded in DebuggingMessageFlow.md.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

Remove / reset a user (and their partition) on a running MeshWeaver portal (AKS). Use when you need to kick out a user — e.g. a throwaway test account — so they re-onboard from scratch. The normal delete API REFUSES a user's home ("user partition root … cannot be deleted"), so deletion is a Postgres-schema drop, not an MCP call. Covers where a user's data actually lives (per-user PG schemas named by BOTH id AND email, e.g. `rbuergi` + `rbuergi@systemorph.com`), how to reach PG (the connection string is inline in the portal's `ConnectionStrings__memex` env — NOT the `POSTGRES_PASSWORD` secret), the substring-match footgun (`buergi` also matches admin `rbuergi`), the in-memory resurrection gotcha (a recycle re-saves the node — a portal restart is what actually clears it), and the half-onboarded case where the user has NO schema at all (in-memory only → restart is the whole fix).

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

deploy

無料

Deploy MeshWeaver — the two routes (shared AKS cluster, and Azure Container Apps via the Aspire test/prod modes) and the traps that make a deploy look done when it is not. Use when rolling a code change onto a portal, standing up or patching an environment, or verifying that what is running is what you built. FIRST RULE (maintainer, 2026-09-08): on AKS an operation is a Hosting/InstanceAction on the control instance (Roll, Restart, Suspend, Audit, Reconcile, …) executed by the in-cluster operator — az aks command invoke / kubectl are break-glass, never the procedure. Also covers the bootstrap build-image / set-image / restart sequence the operator runs for you, why an env deploy.sh is first-time setup and not a code-update path, why the database migration is a run-once Job that a helm upgrade runs — never a Deployment to roll — and the DbVersionGate that makes a portal refuse to serve ahead of its schema.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

gui

無料

Build or review ANY MeshWeaver UI — layout areas, editable views, forms, tables, content editing, side-panel composers — by wiring the framework's EXISTING data-binding pieces, NEVER hand-rolling. The one rule is ALWAYS DATA-BIND, especially editable views: the backend layout area declares WHAT to render (a UiControl tree + node PATHS), and every value read / write-back happens on the GUI side through the per-node IMeshNodeStreamCache. Hand-woven binding — replicating a node into a /data/{id} copy + a save subscription, emitting HTML strings, hand-built selects/checkboxes, .Take(1) on a live stream, async/await/QueryAsync in a layout area — is the bug class behind "awaiting first data" / "renders empty" / a stuck editable view. Use when writing or reviewing a layout area, control, editor, form, table, or any data-bound view. Grounded in src/MeshWeaver.Layout/DataBinding/, Doc/GUI/DataBinding.md + Editor.md, the Edit extension (EditorExtensions.cs), and the node-bound editor controls.

日本語の概要は準備中です。原文の説明を表示しています。

Systemorph/MeshWeaver122026年10月29日 更新

Systemorph のスキルをすべて見る

このスキルの問題を報告する