agentleFS
0xmortuex0xmortuex/claude-code-skills/skills/dormancy-guard/SKILL.md

dormancy-guard

Skill0 starsChanged 4 months agoMITView on GitHub

Audits whether an inactivity-based account deactivation or seat-reclaim sweep uses an activity signal that covers how the account is used — a `last_login`/interactive-session timestamp that goes stale forever for an account only ever driven by an API key, webhook, OAuth app, or service account, so a "clean up dormant accounts" job deactivates something in daily automated use. Checks what column the sweep reads, whether it folds in API calls, webhook deliveries, and token refreshes or only a UI login, whether the action is reversible with a notice/grace window, and whether reactivation needs the same login the policy assumed everyone has. Use when adding or reviewing a dormancy sweep, right after an integration broke the day after a "cleaned up inactive accounts" job ran, or when asked "does our inactivity policy count API usage", "will this dormancy job catch service accounts", or "can an integration survive our 90-day-inactive policy".

The file

Files
SKILL.md

dormancy-guard

"Inactive" reads as "nobody is using this," but the policy almost never measures that — it measures nobody logged in, which used to be a reasonable proxy when logging in was the only way to use an account. It isn't anymore: a growing share of what an account does — a service account minting tokens for a backend job, an OAuth app refreshing on a schedule, a webhook endpoint a customer wired up once and never touches again, a CI pipeline authenticating with a long-lived key — happens with no human session in the loop at all. A dormancy sweep that only reads last_login (or an equivalent "last seen in the UI" column) can't tell the difference between an account nobody needs and an account a dozen automated systems depend on every hour, and the two look identical in the one column it checks. The failure is silent on the writing side — the sweep runs, logs a clean "deactivated 340 dormant accounts," and ships — and loud on the other end, hours or days later, when an integration that was never touched by a human starts failing with no commit, deploy, or config change to explain it.

Step 1: find the exact signal the sweep reads, and what it leaves out

Locate the dormancy/inactivity query or job and identify precisely what "last active" means to it: a single timestamp column (last_login_at, last_seen_at), updated only by the interactive session/login code path? Then trace every other way this account type can act on the system and check whether any of them touch that same column:

  • API requests authenticated by a long-lived key or token — does the auth middleware update an activity timestamp on every authenticated call, or only on interactive login?
  • Webhook deliveries to a customer's registered endpoint — these prove the account is wired into a live integration even though the account itself never "does" anything; does anything record that as activity?
  • OAuth app token refreshes and scheduled/cron job runs authenticated as the account — a refresh grant being exercised is activity; is it logged anywhere the sweep reads?
  • A separate service_account or is_bot flag, if one exists — is it excluded from the sweep entirely, or does the sweep run uniformly across every account type because nobody added the exclusion once the flag was introduced?

The real-world version of this gap is documented, not hypothetical: Atlassian's own community support confirms Jira Cloud's inactivity-based site suspension counts only interactive user login and not API traffic — administrators have reported SUSPENDED_INACTIVITY errors hitting API-driven automation accounts that were making calls the entire time, because "no user logged in" was the only signal the sweep checked. If the account being audited here has any non-interactive access pattern and the sweep's query doesn't demonstrably fold that pattern's activity into its "last active" calculation, this is the primary finding — name the exact column and the exact code paths that do or don't write to it.

Step 2: check what firing the sweep actually does, and whether it's safe to be wrong

Even a sweep with a perfect signal will occasionally fire on something that still mattered (a yearly-renewal integration, a disaster-recovery credential that's supposed to sit idle). Check what the action actually is and how far it goes before a human can intervene:

  • Is there a notice step before the action — an email to an account owner, a grace period, a dashboard warning — or does the sweep deactivate/delete/downgrade in the same job that detected dormancy, with no window to object?
  • Is the action itself reversible in a way that doesn't require what the policy just revoked? A deactivated API key that can only be re-enabled by a human logging in to a UI is a dead end for a pure service account that has no human owner checking that UI regularly — confirm there's a way back in that doesn't assume the login the dormancy sweep itself proved wasn't happening.
  • What breaks downstream when the action fires, and does it fail loud or quiet? Deactivation that merely flips a status flag can leave dependent systems reading stale "still active" state and silently stop updating anything through that account — the documented CloudHealth behavior, where a deactivated account loses the API access it needs to keep its own linked assets' status current, so those assets look fine long after the account that reported on them stopped being able to.

Step 3: confirm the policy decision was actually made, not defaulted into

Ask directly: was a decision ever made about whether service accounts, webhook-only integrations, and OAuth apps are subject to this sweep at all, and on what timeline — or does the sweep simply apply the same human-login-oriented threshold to every account row because nobody wrote an exception? A real postmortem (a July 2025 incident at a fintech vendor) describes exactly this: active institutional accounts had their users deactivated and API keys removed as part of a routine inactivity-based offboarding pass, requiring an incident response to restore access and regenerate keys — not because the policy was wrong in principle, but because it never distinguished "no human logged in" from "no activity of any kind." If no such decision is visible in the code or docs, that's a gap to surface, not something to assume either way.

Report

BLOCK for: a dormancy sweep whose only signal is interactive login (or an equivalent UI-only timestamp) applied uniformly to accounts with a non-interactive access pattern (API key, webhook, OAuth app, service account) that the sweep's own column never reflects; or an action that deactivates/deletes with no notice window and no reactivation path that doesn't itself require the login the policy proved wasn't happening. CAUTION for a signal that's correct in shape (it does fold in API/token activity) but unverified against every access pattern the account type actually supports, or a notice/grace period that exists but is short enough or silent enough (no owner email, dashboard-only) that an unmonitored service account would still miss it. Name the exact column, query, or job — "the sweep looks reasonable" is not a finding; either every access pattern this account type supports writes to the signal the sweep reads, and the action is reversible within a real window, or it isn't.

Boundaries

  • Not seat-guard's territory — that skill audits seat/license enforcement being too loose (deactivation that doesn't free a seat it should, provisioning that bypasses the seat check). This skill audits dormancy detection itself being too blunt an instrument, reclaiming accounts, seats, or licenses that were never actually idle.
  • Not reaccept-guard's territory — that skill audits whether a ToS/policy-version re-acceptance gate reaches non-UI surfaces (API, webhook, service account). This skill isn't about a policy-acceptance gate at all; it's about an inactivity measurement being blind to the same non-UI surfaces.
  • Not push-guard's territory — that skill audits whether a dead push-notification token gets cleaned up once a provider reports it invalid. This skill is about an account being wrongly judged dormant, a different object and a different failure direction (acting on a live account, not failing to act on a dead token).
  • Doesn't design a retention or offboarding policy from scratch, and doesn't second-guess a deliberate, documented decision to exempt or include service accounts on a stated timeline — it only flags the case where that decision looks like it was never made, and the sweep's uniform behavior is a default, not a choice.
  • Can't confirm from source alone that a given account in production actually is or isn't dormant — it audits whether the code's own signal is capable of telling the difference, and says plainly when confirming a specific account's real activity needs a database/log query rather than a code read.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.