Agent Skills: Steel org recon

>-

UncategorizedID: nibzard/skills-marketplace/steel-org-recon

Install this agent skill to your local

pnpm dlx add-skill https://github.com/nibzard/skills/tree/HEAD/skills/steel-org-recon

Skill Files

Browse the full folder contents for steel-org-recon.

Download Skill

Loading file tree…

skills/steel-org-recon/SKILL.md

Skill Metadata

Name
steel-org-recon
Description
>-

Steel org recon

Reconstruct one organization's real behaviour from evidence, not from status columns.

Invocation: /steel-org-recon <org_id> — or any request naming an org id.

The whole reason this skill exists: Steel's own success fields lie. A day containing ten hard failures and seventeen sessions that never did anything is recorded as sessions_count: 40, failed_count: 0, stopped_count: 40. Every session ends stopped because that is the normal terminal state. If you answer from raw_steel_org_sessions_daily or from session tags, you will confidently report the opposite of what happened. The truth only exists in the raw event stream, read chronologically, one session at a time.

Non-negotiables

Read-only. Follow AGENTS.md. Warehouse, Tigris, and every production system are read-only here. No cp/sync to a bucket, no writes, no deletes, no wildcard mutations. Local scratch and the local DuckDB cache are yours.

Privacy has two tiers, and they differ. Analysis in conversation may name identifiers — that is the point of the investigation and the person asking has warehouse access. Any file you write under analysis/ or anywhere shareable must be scrubbed: no customer or member names, no email addresses, no raw org/session ids, no full URLs or query parameters, no credentials, no OTPs, no raw manifests. Aggregate or redact instead. When in doubt, keep the finding in chat and write the scrubbed version to disk.

Reconstructed secrets stay in chat. Input.dispatchKeyEvent events store the literal typed character, so keystroke sequences reconstruct into plaintext emails, search terms, and one-time codes. Use them when they answer the question, never write them to a file, and flag it if you find a password among them — that is a Steel logging defect worth reporting, not a detail.

Method

Run the phases in order. The order is the point: identity before traces, because knowing "solo developer, launch plan, account created 13 minutes ago" reframes everything you read next. Skipping ahead to the traces is how you end up inventing a narrative and then defending it.

Phase 1 — Identity and coverage

.claude/skills/steel-org-recon/scripts/profile-org.sh <org_id>

One command gives you the org record, members, signup questionnaire, projects, per-day session totals, a warehouse-vs-storage session count reconciliation, and which stored artifacts exist.

Read it for:

  • Who and how new. Plan, creation time, member count, and the questionnaire's self-description. A one-person launch org minutes old is a trial; a paid multi-member org is a workload. This single fact changes every downstream interpretation.
  • The self-description gap. The questionnaire is what they said; the traces are what they did. When those disagree, the disagreement is the finding.
  • Coverage. Warehouse session count versus log directory count. If they differ, some sessions wrote no log and your trace has a known ceiling — say so later rather than implying you saw everything.
  • Absence. Empty profiles/ means no persisted browser identity, which predicts repeated cold logins and challenge walls. Empty extensions/ means any extension you see in the logs is Steel-injected, not theirs. These absences are among the highest-yield facts available and they cost seconds to check.

If the org is missing from the warehouse entirely, suspect a stale snapshot before concluding the org does not exist — compare max(created_at) against the session dates.

Phase 2 — Cache the logs locally

.claude/skills/steel-org-recon/scripts/cache-logs.sh <org_id> [max_sessions]

This copies the org's session events into a local DuckDB file so every later query is instant and free. Do this before exploring. Querying S3 parquet repeatedly is slow enough that it changes how much you are willing to look, which quietly caps the quality of the answer.

For large orgs, pass max_sessions and sample rather than caching everything. Say in the report that you sampled, and how.

Phase 3 — Learn the schema, do not guess it

.claude/skills/steel-org-recon/scripts/dump-trace.sh <db_path> --schema

This enumerates the JSON keys actually present under each event_type. Guessing JSON paths wastes turns and silently returns all-NULL columns that look like real findings. Enumerate once, then write queries against what is there. references/data-map.md documents the event shapes you will normally see.

Phase 4 — Read the flat trace

.claude/skills/steel-org-recon/scripts/dump-trace.sh <db_path> > trace.txt

One line per meaningful action, sorted by session and time: session | time | action | target[accessibleName] = value.

Then actually read it. This is where understanding happens; everything before is plumbing and everything after is verification. Typed values, clicked element names, and navigation URLs in sequence tell you what the person was trying to accomplish in a way no aggregate can. Expect a few hundred lines for a small org — read it all rather than sampling, because the tasks are identifiable only from the sequence.

Group the trace into tasks, not sessions. One task often spans several sessions (a login request in one, the magic-link click in another), and one session often does several unrelated things. Deep-link URL parameters are especially informative: dates, origins, destinations, party sizes, and search terms are usually right there in the query string.

Phase 5 — Forensics battery

.claude/skills/steel-org-recon/scripts/forensics.sh <db_path>

Prints the standard evidence set: per-session overview, injected-extension noise split, non-2xx responses by host, page errors, first/last URL per session, CDP input summary, keystroke cadence, and click-coordinate deltas.

Two of these need explaining, because they answer questions people always ask:

Noise split. Steel injects a CAPTCHA-solver extension. When its context dies it can hot-loop failed fetches to chrome-extension://invalid/ — in one real org that was 92% of every RequestFailed event. Compute error rates after excluding chrome-extension:// URLs, or you will report our bug as the customer's unreliability. Related: an extension id appearing in the logs is Steel's unless extensions/{org}/ is non-empty; confirm by checking whether the same id appears in an unrelated org.

Human takeover. People ask whether a human was driving. Decide it on physics, not vibes:

  • Programmatic typing has a near-constant inter-keystroke gap (a fixed delay plus round-trip jitter — e.g. 535 ms ±10 ms, identical for @ and . as for letters). Human typing runs 80–300 ms with 50–150 ms deviation and strong digraph variation.
  • Programmatic clicks land on the target's exact bounding-box centre (dx = dy = 0.0) at sub-pixel coordinates. Non-zero offsets that are constant are computed offsets, not jitter.
  • Absent mouseMoved events mean no cursor simulation.
  • A composite key like Control+a sent as one keyDown with no modifier bitmask is synthesized; real keyboards emit separate events.

Watching is not takeover. Steel logs do not record live-viewer attachment, so you can rule out human input with confidence but cannot prove nobody was watching. State it that way.

Phase 6 — Classify every session

Assign exactly one outcome per session using observable evidence. references/outcomes.md has the full rubric and the signatures for each class. Summary:

| Class | Signature | |---|---| | never_driven | zero CDP commands, ~2 events, final URL about:blank — client never attached | | goal_reached | terminal success state: confirmation/thank-you page, is_success=true, target view loaded | | partial | authenticated or reached a candidate/result state, no terminal action | | blocked | wall unresolved at session end: 403 cluster on target host, challenge as final URL, captcha widget still being poked, navigation that never commits | | client_error | the agent broke itself — malformed input, off-viewport coordinates, blind submits — with no wall evidence | | abandoned | navigated, no interactions, no wall, stopped |

Report the rate two ways, because they answer different questions: goals reached over driven sessions (how well the automation works) and over all sessions created (what the customer actually paid for).

Two traps the repo already documents in README.md and worth re-reading there: a wall marker is not a failure — government portals load Incapsula and reCAPTCHA assets as ordinary page assets — and Steel session tags are not failure evidence.

Phase 7 — Report

Use the structure in references/report-template.md. Lead with a one-paragraph verdict, then the inferred tasks, the scorecard, blockers by target host, agent-quality observations, what Steel itself cost them, and an explicit confidence-and-gaps section.

Discipline that prevents wrong answers

These are the failure modes that actually happen. They cost real rework.

Name the artifact before you claim. Before asserting anything, say what would have to exist on disk or in the warehouse if it were true, then go look. "This is their own product" implies ownership artifacts. "They used feature X" implies X's stored state. A story that merely fits is not evidence, and the moment you have a coherent narrative is exactly when you stop testing it.

Check timestamp order before claiming causation. "They read the code from their inbox" needs the inbox access to precede the code entry. Sort the events before you explain them.

Absence is evidence. An empty storage prefix explained seven wasted logins in one investigation. A missing session before an OTP proved the access was out-of-band. Look for the thing that is not there.

Reconcile two independent sources. Warehouse count versus storage count, tags versus events. Where they disagree, that gap is a finding, not an inconvenience.

Separate our noise from their failures before computing any rate. See Phase 5.

Distinguish "not logged" from "did not happen." Steel's CDP logging is whitelisted — in practice only Page.navigate, Input.dispatchKeyEvent, and Input.dispatchMouseEvent appear. The absence of Runtime.evaluate does not mean the client never used it. This limit blocks harness identification: session logs carry no user-agent, headers, or SDK identifier, so if asked what framework a customer used, say it is not determinable from these logs and point at Steel's API-layer request logs instead. Do not name a framework you cannot evidence.

Bound expensive work explicitly. Cross-org S3 scans cost minutes each. Decide how many you are buying, and if you end up with a single comparison, say n=1 rather than implying a survey.

Reference files

  • references/data-map.md — warehouse tables, bucket layouts, log paths, event payload shapes. Read when you need a column or path you do not remember.
  • references/query-cookbook.md — the query battery plus DuckDB traps that will bite you (reserved words, JSON operator inconsistency, a sampling clause that silently returns nothing). Read before writing ad-hoc SQL.
  • references/outcomes.md — the full outcome rubric with evidence signatures per class.
  • references/report-template.md — output structure and the privacy scrub list.