Skip to content
Back to skills

Audit First

ASecurity

Audit-before-fix protocol — first find all gaps across 7 planes, prioritize, pin the gap table, and only then fix. Principle: never touch code until the full picture of problems is understood. Gap table → approval → flow-first per gap → library-first → plan → code. Use when: task begins with looking for problems ("find gaps in X", "audit security", "what's wrong with Y"), not with a specific fix

  • 19 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
toolsrustgoshellbashsqlreactdebugginggitapifrontend

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 3, 2026

npx -y skills add bearded-illirian/trailmark --skill audit-first --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Audit First?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Audit First
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/bearded-illirian-audit-first-2f604c11/badge)](https://www.skillsdirectory.com/skills/bearded-illirian-audit-first-2f604c11)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: audit-first
description: |
  Audit-before-fix protocol — first find all gaps across 7 planes, prioritize, pin the
  gap table, and only then fix. Principle: never touch code until the full picture of
  problems is understood. Gap table → approval → flow-first per gap → library-first →
  plan → code.

  Use when: task begins with looking for problems ("find gaps in X", "audit security",
  "what's wrong with Y"), not with a specific fix
---

# Audit-First Protocol

For tasks where **you must first find all the problems**, and only then fix. Use when:

- An area is suspicious but the specific gap is unknown
- Need to make sure we're fixing the right problem, not a symptom
- Task sounds like "look at what's wrong in X" rather than "fix specific Y"
- Before an important deploy — a preventive audit

**Difference from `arch-first`:**

| | arch-first | audit-first |
|---|---|---|
| Purpose | Execute a complex task | Discover problems |
| Phase 0 | Understand the task | Find all gaps |
| Result | Decomposition into blocks | Gap table with priorities |
| When to fix | Immediately per the plan | Only after table approval |
| Launch | "Do X" | "Find what's wrong in X" |

## Input

A fix task where the specific problems are unknown; the module or integration area to audit.

## Output

`audit-doc.md` with a holes table across 7 planes (Security / Data / Logic / Integrations / Observability / UX / Performance), prioritized, plus per-hole reports as fixes close.

## Hands off to

Per-hole `flow-first` cycle for each prioritized hole, then `ship-first` at task-level closure.

---

## Principle 1 — Plane: Security

The most critical plane. HIGH by default. Checked first.

### 1.1 Auth / Authz

Endpoint accessible without token → HIGH. Internal endpoint protected only by hardcoded token → HIGH. Authorization doesn't check resource-to-tenant ownership (IDOR) → HIGH.

### 1.2 Injections (SQL, CMD, XSS)

String concatenation in queries, `shell=True`, `innerHTML` with user data → grep and flag.

### 1.3 Encryption

**In transit:** SMTP without TLS/SSL → HIGH. HTTP for external calls → MED. **At rest:** passwords in plaintext → HIGH. OAuth tokens in DB without encryption → LOW (single-tenant) / MED (multi-tenant).

### 1.4 OAuth / Tokens

- CSRF: `state = user_code` instead of `secrets.token_urlsafe()` → HIGH
- Token TTL: `access_token` used without checking `expires_at` → MED
- Storage: tokens in logs, git, unignored env → HIGH

### 1.5 Webhook validation

Two mandatory levels: signature (HMAC-SHA256 verified) + timestamp (abs(now - ts) > 300 rejected). Only level 1 → replay possible → MED. Neither → HIGH.

### 1.6 Secrets in code and configs

Hardcoded secret in code = HIGH always, even for a private repo.

---

## Principle 2 — Plane: Integrations architecture

Doesn't break immediately — breaks under load / when external service fails. MED default, HIGH on critical path.

### 2.1 Rate Limit (429)

Generic `except Exception` swallows FloodWaitError / RateLimitError → message lost silently. Correct: catch specific error before generic Exception, sleep + one retry.

### 2.2 Service-specific errors

Every external service has errors that must not fall into generic Exception. Telegram FloodWaitError → sleep + retry. Whisper 413 → check size BEFORE sending. Zoom webhook stale timestamp → reject with 400. SMTP wrong encryption → ValueError, not silent fail.

### 2.3 In-Memory state

Module-level dict resets on deploy. `_oauth_state_store` → in-flight OAuth breaks (MED). `_token_cache` → extra requests after restart (LOW). `_active_sessions` → everyone logged out on deploy (HIGH).

### 2.4 Validation BEFORE an expensive network request

File >25MB sent to Whisper without a check → learn about the problem only on 413 → retry loop. Correct: `if getsize > limit: raise` before opening the file.

### 2.5 Circuit Breaker

Every external HTTP call has `timeout=`. `ConnectionError` handled separately from business errors. Service unavailable → task marked failed, not hanging.

---

## Principle 3 — Plane: Data and DB

Rare but irreversible. Especially dangerous in multi-tenant systems.

### 3.1 Missing transactions (Race Condition)

Trigger for check: read → decide → write based on what was read. Two `execute` calls in a row without `with conn:` → concurrent requests can insert a duplicate.

### 3.2 Missing constraints

The DB must protect integrity independently of the code. Email without UNIQUE → duplicate accounts. FK without ON DELETE → orphans. Status without CHECK → invalid values. Numeric without NOT NULL DEFAULT 0 → NULL surprises.

### 3.3 N+1 queries

Query inside a loop. 100 records = 101 queries instead of 1. In SQLite less critical than in PostgreSQL, still noticeable on large datasets.

### 3.4 Plaintext for sensitive fields

User passwords in plaintext → HIGH always. OAuth tokens in DB → LOW / MED. API keys in DB → MED (better in env).

### 3.5 No TTL and no cleanup of temporary data

In-memory stores without cleanup, tokens/nonce tables without periodic `DELETE WHERE expires_at < now()`, logs without partitioning → LOW at small scale, grows with load.

---

## Principle 4 — Plane: Error handling

Most invisible plane. Gaps here don't obviously break — they make the system unpredictable.

### 4.1 Silent Failures

Exception swallowed with only a log + return False. Caller ignores False. Email didn't send, no one knows. Rule: either re-raise (critical operations) or return False + explicit caller-side check.

### 4.2 Retry loops without a limit

Retry on 413 (file too large) — won't help, file won't shrink. Retry on 401 (invalid key) — won't help either. Rule: retry only for **transient** errors (network dropped, service temporarily unavailable). For deterministic errors → fail fast with a clear message.

### 4.3 No Fallback

External service unavailable → what happens to user? Options (best to acceptable): degradation (simpler version) / queue with TTL / notify user of deferred / skip with log (non-critical only). Unacceptable: skip without log and without notification.

### 4.4 Uninformative error messages

`HTTPException(500, str(e))` leaks stack trace + internal details. Rule: 400 for invalid input, 401 auth, 403 access, 404 not found, 502 external service, 500 unexpected (no details externally, details in logs).

---

## Principle 5 — Plane: UI/UX

Breaks trust, not data. Empty screen instead of error, button that doesn't react, form that accepts garbage.

### 5.1 No Loading State

User clicked → nothing visual → clicks again → double submission. Rule: button `disabled = true` + indicator during any async action.

### 5.2 No Error State

Request failed → blank screen or stale data. Rule: show message (not technical details), give action ("Try again" / "Contact support"), don't hide behind blank.

### 5.3 No Empty State

List loaded, no data → empty space. Three states any list must have: Loading (skeleton), Empty ("No X yet. Create the first →"), Error ("Failed to load. Try again.").

### 5.4 No frontend validation

Form accepts invalid input → server returns 400 → user sees unclear error. Validate on frontend in addition to backend: required fields before submission, email/phone/URL format inline, numeric ranges, interdependent fields.

### 5.5 Broken Edge Cases

Golden path only. Typical failures: `title = null` → crash on `.length`; empty list → blank; very long name → overflow; number 0 → `if (count)` yields false; date in past → "Start" button active for expired.

### 5.6 Destructive actions without confirmation

Delete, disconnect, reset → without "Are you sure?". Rule: irreversible action requires explicit confirmation describing consequences.

---

## Principle 6 — Plane: Observability

Doesn't break — makes incidents impossible to investigate.

### 6.1 Silent Success without log

Operation done, nothing logged. Week later: "did it send Wednesday?". Every meaningful operation → log with identifiers.

### 6.2 No Structured Logging

`_log.info("sending email")` — can't tie to specific user. Include always: user_code + what's happening + relevant IDs (meeting, job, request_id).

### 6.3 Critical operations without Audit Trail

OAuth connect/disconnect, tenant settings change, data deletion, auth failure, webhook receipt — all must be logged always and immutably. For compliance, incident debugging, disputes.

### 6.4 No metrics for external calls

Whisper started answering in 30s instead of 3 — anyone notice before user complaints? Log elapsed for slow ops. Warning if slower than expected threshold.

### 6.5 Logging secrets

Tokens / passwords / API keys in log lines. Rule: log only non-confidential identifiers. Config with password → log only non-secret fields.

---

## Principle 7 — Plane: Config in code instead of source of truth

The most invisible. UI "works" (form saves), DB "works" (data stored), tests "pass" — but real effect is missing. User loses trust: "I did change this".

### 7.1 Hardcoded behavior parameters

`DIGEST_SEND_HOUR = 9` hardcoded in code. User sets 18:00 in UI → saved to `settings.digest_hour` → digest still goes at 9:00. Silent, invisible bug.

Typical cases: HOUR / DAYS / INTERVAL / REMIND / LIMIT / THRESHOLD / TEMPLATE_ID as constants when they should read from DB. Criticality: HIGH if affects money/sending/access. MED for UX (time, threshold). LOW for metadata.

### 7.2 Unused DB fields (saved but not read)

Settings table has a field. UI writes it. Business logic never reads it — uses default or constant instead. Grep test: field name → INSERT/UPDATE only, no reads outside migrations → confirmed gap.

### 7.3 "Phantom" settings forms

UI form saves data, backend doesn't apply. Check intersection: "what POST accepts" vs "what business logic reads". Excess on POST side → phantom fields.

---

## Output format — Gap table

After scanning all planes, consolidate into one table. Only format used. Approval point before fixes.

```
| # | Plane | Integration / Module | Gap | File:line | Criticality |
|---|---|---|---|---|---|
| 1 | Security | Zoom OAuth | state = user_code, no CSRF nonce | routes/oauth.py:34 | HIGH |
| 2 | Security | Email (SMTP) | No TLS, plaintext | services/email_service.py:91 | HIGH |
| 3 | Architecture | Telegram | FloodWaitError in generic except | services/telethon_manager.py:44 | MED |
```

### Criticality definitions

| Level | When to set |
|---|---|
| **HIGH** | Exploitable without privileges OR data loss/corruption/unauthorized access. Examples: CSRF in OAuth, plaintext passwords, webhook without signature, SQL injection, open authorization. |
| **MED** | Requires certain conditions OR data loss under a coincidence of factors. Examples: FloodWaitError in except, replay attack, file >25MB without check. |
| **LOW** | Technical improvement or potential vector at growing load. Doesn't break now, creates risk. Examples: in-memory cache without TTL, tokens in plaintext single-tenant, missing metrics. |

### Rules

- One row = one gap (don't group even if in same file)
- File:line = specific code line or nearest related
- Gap = one sentence: what specifically is missing or wrong
- HIGH does not exceed 3 in a normal task. 5+ HIGH → re-check criteria

Final line:

```
Total: {N} HIGH / {M} MED / {K} LOW — {X} gaps, of which {P0} P0 blocks and {P1} P1 blocks.
Waiting for approval → then I pin the block order and ask the work mode.
```

---

## Workflow — 5 phases

Sequential. Cannot start fixing until all 5 phases are completed.

### Step 0 — Check / create the task

`{log_dir}` set → proceed silently. Not set → form slug `{date}-audit-{area-slug}`, show "Creating task: {slug}. Rename?", mkdir, INSERT into `artifacts`. Cannot skip.

**Child task branch:** triggers "create child task" / "next audit stage" / "continuation of {parent}". Form `{parent}-part{N}`, register with `links (from, to, 'part_of')`. Direct mkdir + Write + INSERT bypassing this protocol is prohibited — the `part_of` link won't register and ship-first won't see unclosed chain areas.

### Step 0.5 — Calibrate incoming artifacts

audit-first expects `idea-first-*.md` with type "fix" or "problem". Type differs → propose switching to arch-first. Artifact missing → allowed (manual invocation), skip calibration, proceed with Phase 0.

### Phase 0 — Define the audit area

Do not scan until the area is specified. **Never scan the whole project** — analog of flow-first "going into code without anchors" → 50 LOW gaps, none important. Area given → use as starting point. Not given → ask one specific question.

### Phase 1 — Scan across the 7 planes

Sequential walk. Order matters:

| Order | Plane | Why here |
|---|---|---|
| 1 | Security | Only plane HIGH by default |
| 2 | Integrations architecture | Gaps manifest under load |
| 3 | Data and DB | Data loss irreversible |
| 4 | Error handling | Masks gaps in 1-3 |
| 5 | UI/UX | After backend planes |
| 6 | Observability | Last — doesn't affect correctness |
| 7 | Config in code | Silent gaps, UI+DB "work" but settings not applied |

For each plane: read area files → grep → pin what was found. "Not affected" rule: irrelevant plane → explicit "not applicable" row, don't leave blank.

### Phase 2 — Assemble the gap table

All found gaps → one table. HIGH at top, then MED, then LOW. Assign a block to each HIGH and MED. LOW at user's discretion.

### Phase 3 — Table approval

Show table + summary line → wait for explicit approval.

**Before approval — no code.** Even if the gap is obvious and fix is 2 lines. This is the main rule.

User can: approve → Phase 4; remove row (LOW deferred); change criticality; add missed gap. Each edit → updated table → wait for approval again.

**After approval — register blocks in task_blocks** for each HIGH/MED gap. Sub-blocks use `block_num = N*10 + M`, `parent_block_num = N`.

Append `## Blocks` section to task.md. Output explicit "Registered N blocks: {list}. Launching Skill('flow-first')."

### Phase 3.5 — Batch call to cadence-first

After the INSERT loop and before asking work mode → invoke cadence-first in batch mode so each gap block gets `recommended_cadence` pre-baked. HIGH gaps usually Tier 1 full cycle; LOW often Tier 3 plan-only — cadence-first formalizes this differentiation.

**Why here:** cadence is decided at GENERATION time (rich context — audit area, gap table, criticality), not at EXECUTION time.

On failure → log warning, continue. Blocks execute without pre-baked cadence.

### Final step of Phase 3 — Ask work mode

```
Blocks pinned. How do we work?

1. 🔁 Manual — I run each block, wait for your "ok"
2. 🚀 Autopilot — dev-auto-first takes over
```

Choice 1 → `Skill('flow-first')` for first HIGH block. Choice 2 → `Skill('dev-auto-first')`.

**Save audit-doc.md** to log_dir with the approved gap table. Register in `task_artifacts`. Cannot skip — on session drop, chat table isn't recoverable.

### Phase 4 — Fix per-block

Per-block cycle for each gap in order HIGH → MED → LOW: flow-first (understand landscape) → library-first (ready pattern) → plan-first → execute → ship-first (per-block).

**Block transition rule:** after "go" on ship-first per-block → first action is `Skill(flow-first)` for the next block. Not Read, not grep, not plan-first directly. flow-first cannot be skipped even if "task is obvious".

Each fix = separate commit with clear description of what it closes.

After all fixes → ship-first task-level updates gap table (✅ Fixed / 🔁 Deferred), does final report, sessions, STATUS, push + sync.

---

## Anti-patterns

### ❌ Shovel the whole project without an area

Grep everywhere → 80 lines of noise. Half false positives, other half LOW. Rule: area set before first grep.

### ❌ Fix along with scanning

Found a gap → immediately fix without waiting for remaining planes. Fixing first breaks second you haven't found yet. Rule: scan → table → approval → only then code.

### ❌ Silently skip a plane

"UI isn't relevant" → plane simply not mentioned. User doesn't know: didn't look, or looked and clean? Rule: explicit "not applicable" row for irrelevant planes.

### ❌ Mix audit and fix in one step

"Found and fixed straight away". User didn't see the table, didn't approve priorities. Rule: table shown before any code change. Approval mandatory.

### ❌ Everything is HIGH

Lowering the bar — user stops trusting criticality. Rule: HIGH only for exploitable-without-privileges OR data loss/corruption. 3+ HIGH → re-check criteria.

### ❌ Don't pin LOW in the table

LOW seems insignificant → skip. Six months later LOW is MED, then HIGH, nobody remembers it was known. Rule: all found gaps in the table. Decision what to do with LOW belongs to user.

### ❌ Don't check settings forms

Form saves → agent considers "Config in code" plane clean. But doesn't check whether field is READ in business logic. Rule: for each settings field grep-check reads (not save/update). Written but not read → gap.

### ❌ "go" on next block → plan-first instead of flow-first

After block finished, user answered "go" → agent goes directly to plan-first. Rule: "go" on next block = first action Skill(flow-first). flow-first mandatory for each block without exception.

### ❌ Create child task directly via bash bypassing Step 0

Direct `mkdir` + `Write` + `INSERT INTO artifacts` without the Step 0 child-task branch → task appears without `part_of` link → ship-first doesn't see unclosed siblings on closure. Rule: task creation only via Step 0 branch. Direct tools prohibited.

### ❌ One big fix for the whole table

All gaps in a single commit → impossible to roll back specific fix, impossible to review. Rule: one gap = one commit.

### ❌ Inlining the skill instead of using the Skill() tool

Executed the protocol from memory — outdated version, no accuracy guarantee. Always via `Skill('audit-first')`.

---

## Related skills

- **idea-first** — upstream artifact read in Step 0.5; type "fix"/"problem" → audit-first; other types → arch-first
- **flow-first / library-first / plan-first / ship-first** — per-block execution chain
- **cadence-first** — batch-invoked at Phase 3.5 to pre-bake per-block cadence
- **arch-first** — sibling for feature/product tasks (decomposition instead of gap discovery)

---

## Step 99 — Log invocation

```bash
sqlite3 {routing_db} \
  "INSERT INTO skill_invocations (task_id, block_num, skill_name, invoked_at)
   VALUES ('{slug}', '{N}', 'audit-first', datetime('now'))" 2>/dev/null || true
```

Files in this skill

  • INFO.md2.2 KB
  • SKILL.md18.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.