# cartography

This skill should be used when the user says "build context on a flow", "trace a flow", "map how X works", "cartography", "/cartography", "document a flow", "create a flow map", "trace how authentication works", "map the data flow", or wants to explore and document how a specific code flow works so that context can be quickly rebuilt on future visits. This is the primary skill for creating cartography files in grimoire/cartography/.

- **Kind:** skill
- **Source:** https://github.com/JoranHonig/grimoire
- **Page:** https://forefy.com/skills/c302b06d-f1dc-472c-8bb1-053a50c29acb
- **API (JSON + files):** https://forefy.com/api/asr/c302b06d-f1dc-472c-8bb1-053a50c29acb

---

## SKILL.md

---
name: cartography
description: >-
  This skill should be used when the user says "build context on a flow",
  "trace a flow", "map how X works", "cartography", "/cartography",
  "document a flow", "create a flow map", "trace how authentication works",
  "map the data flow", or wants to explore and document how a specific
  code flow works so that context can be quickly rebuilt on future visits.
  This is the primary skill for creating cartography files in
  grimoire/cartography/.
user_invocable: true
---

# Cartography

Explore a code flow, document which parts of the codebase are relevant, and create a cartography
file so context can be rebuilt quickly on future visits.

## Philosophy

**Context is expensive to build and cheap to store.** Security researchers repeatedly need to
understand the same flows — authentication, data pipelines, permission checks. Building this
context from scratch each time wastes tokens and time. Cartography files are lightweight pointers
that document *where* to look, not *what the code does*. They enable any agent to rebuild flow
context in seconds instead of minutes.

## Workflow

When this skill is activated, create a todo list from the following steps. Mark each task
in_progress before starting it and completed when done.

```
- [ ] 1. Verify infrastructure — confirm grimoire/cartography/ exists
- [ ] 2. Check index for existing flows — run index script, present matches
- [ ] 3. Explore the flow — discover entry points, components, sequence, security notes
- [ ] 4. Document the flow — create cartography file following format spec
- [ ] 5. Update index — re-run index script, verify new flow appears
- [ ] 6. Present to user — summary and suggest follow-up skills
```

---

### 1. Verify Infrastructure

Check that the `grimoire/` directory and `grimoire/cartography/` subdirectory exist.

- If `grimoire/cartography/` exists, proceed.
- If `grimoire/` exists but `cartography/` does not, create `grimoire/cartography/`.
- If `grimoire/` does not exist, warn the user that Grimoire has not been summoned on this
  codebase. Suggest running [[summon]] first to set up the workspace. If the user wants to
  proceed anyway, create both `grimoire/` and `grimoire/cartography/`.

### 2. Check Index for Existing Flows

Run the indexing script to see what flows already exist:

```bash
bash skills/cartography/scripts/index-cartography.sh grimoire/cartography/
```

If the index returns results:
- Present the list to the user.
- Check whether any existing flow matches or overlaps with what the user is asking about.
- If a match exists, offer to load that flow's context instead of creating a new one. If the
  user wants to refine an existing flow, suggest [[review-cartography]] instead.

If the index is empty or the user's flow is new, proceed to exploration.

### 3. Explore the Flow

There are two exploration modes depending on what the user provides:

**Seeded exploration** — the user provides starting files or hints (e.g., "trace how
authentication works starting from `gateway/src/middleware/auth.ts`"). Use these as entry points
and trace the flow by following the callgraph:

1. Read the seeded files carefully
2. Identify all function/method calls made from the entry point
3. For each callee, open its definition and repeat — follow the callgraph depth-first along the
   flow's execution path
4. At each hop, note: the file, the symbol, and its role in the flow (one line)
5. Stop when you reach leaf functions (no further relevant calls), external service boundaries,
   or when the flow loops back to already-visited code
6. Use subagents for branches — if the callgraph forks into independent paths (e.g., an async
   side-effect vs. the main return path), give each branch to a subagent to trace in parallel

Use `path/to/file:symbol` notation to track each hop. This directly becomes the Flow Sequence
and Key Components in the cartography file.

**Unseeded exploration** — the user describes a flow but doesn't point to specific files (e.g.,
"map how secrets are retrieved"). Deploy a **swarm** of subagents to explore the codebase:

1. Study the user's flow description. Ask clarifying questions if the scope is ambiguous.
2. Generate search queries — brainstorm function names, module names, class names, route
   patterns, config keys, error messages, log strings, and domain-specific keywords related to
   the flow. Aim for **100 subagents** — breadth matters more than precision at this stage.
3. Spawn all subagents in parallel. Each subagent gets one search query and should:
   - Search for the term across the codebase (file names, symbols, content)
   - For each match, read enough context to assess relevance (a few lines around the match)
   - Return: file path, symbol, one-line relevance assessment, confidence (high/medium/low)
4. Collect all results. Deduplicate by file. Rank by frequency (files appearing in multiple
   subagent results are more likely to be core to the flow) and confidence.
5. From the top-ranked files, switch to **callgraph-following** (as in seeded mode) to trace the
   actual execution path and build the flow sequence.

The swarm casts a wide net; the callgraph pass refines it into a coherent flow. Don't skip
step 5 — a bag of search results is not a flow map.

In both modes, gather:
- **Entry points** — where execution begins for this flow (endpoints, handlers, CLI commands)
- **Key components** — modules and files that participate, with a one-line role description
- **Flow sequence** — numbered steps tracing execution through the system, with file references
- **Security notes** — trust boundaries, validation gaps, TOCTOU windows, crypto observations,
  anything security-relevant

Use subagents to parallelize exploration. Keep the main context focused on assembling the map
rather than reading every file in detail.

Consult `references/cartography-format.md` for the exact format specification and the examples:
- `examples/cartography-example.md` — single-service flow, one conditional section
- `examples/cross-service-auth-example.md` — cross-service flow, multiple conditionals

### 4. Document the Flow

Create a cartography file at `grimoire/cartography/<slug>.md` where `<slug>` is a URL-friendly
version of the flow name (lowercase, hyphens, no spaces).

The file must follow the format defined in `references/cartography-format.md`:

1. **Frontmatter** — `name`, `description`, `created`, `updated`, `tags`, `related`
2. **Overview** — 2-3 sentences, security relevance
3. **Entry Points** — `path/to/file:symbol` notation
4. **Key Components** — files with one-line role descriptions
5. **Flow Sequence** — numbered steps with file references
6. **Security Notes** — trust boundaries, gaps, observations
7. **Conditional sections** — for sub-flows that are independently useful but would pollute
   context if always loaded. Use when: the main body exceeds ~80 lines, or a sub-flow is only
   relevant for specific investigations. Write the `<!-- condition: ... -->` comment as a
   concrete, matchable topic description — an agent reads this comment and decides whether to
   load the section based on the user's current question. **Load** when the user's question
   directly matches the condition topic. **Skip** when the user is focused on the main flow and
   the conditional topic hasn't been mentioned.
8. **Related Flows** — cross-links to other cartography files

**Key constraint:** the file documents *where* to look, not *what the code does*. If you find
yourself writing detailed code explanations, stop. Add the file path and a one-line role
description instead.

Set `created` and `updated` to today's date.

### 5. Update Index

Re-run the indexing script:

```bash
bash skills/cartography/scripts/index-cartography.sh grimoire/cartography/
```

Verify the new flow appears in the output. If it doesn't, check that the frontmatter has
valid `name` and `description` fields on single lines.

### 6. Present to User

Show the user:
- A summary of the flow that was documented
- The file path where the cartography file was created
- The entry points and key components discovered
- Any security notes worth highlighting

Suggest follow-up actions:
- **[[review-cartography]]** — to verify and refine the flow against the actual codebase
- **[[gc-cartography]]** — if there are many flows, to clean up overlap and duplication

---

## Context Rebuild

Cartography files exist so that agents don't repeat expensive exploration. When an agent needs
to understand a flow that has already been mapped, it should **rebuild context from the
cartography file** rather than re-exploring from scratch:

1. **Load the cartography file** — read the frontmatter, overview, and entry points to confirm
   this is the right flow.
2. **Evaluate conditional sections** — read each `<!-- condition: ... -->` comment. If the
   user's current question matches the condition topic, load that section. If not, skip it.
   When in doubt, skip — loading unnecessary conditionals wastes context.
3. **Open the referenced files** — use the Key Components and Flow Sequence as a reading list.
   Open these files in the order the flow sequence specifies. This is where actual understanding
   is built — the cartography file is the map, the source files are the territory.
4. **Check freshness** — if the `updated` date is old or the referenced files have changed
   significantly since the cartography file was written, the map may be stale. Consider running
   [[review-cartography]] to update it before relying on it.
5. **Follow cross-links** — if the investigation touches Related Flows, load those cartography
   files too (repeating steps 1-3 for each).

An agent that follows this process gets to a working understanding of the flow in seconds,
using tokens only on reading source files rather than searching for them.

---

## Guidelines

- **Subagents for exploration.** Use subagents to search for relevant code in parallel. This
  keeps the main context clean and speeds up discovery.
- **Pointers, not content.** Cartography files should never contain code snippets or detailed
  logic explanations. They are navigation maps.
- **One flow per file.** Don't combine unrelated flows. Use the `related` field and
  `[[cartography/...]]` links to connect them.
- **Conditional sections for complexity.** If a flow has sub-paths that are only sometimes
  relevant (e.g., shared vault access within a general retrieval flow), use conditional sections
  to keep the main flow lean.
- **Security lens.** Every flow should have security notes. If you can't think of any, you
  haven't looked hard enough. Trust boundaries, validation gaps, and crypto operations are
  always worth noting.
- **Check existing flows first.** Always run the index before creating a new file. Duplicate
  flows create confusion and get in each other's way.

## examples

```

```

## examples/cartography-example.md

# Example Cartography File

This is a worked example of a cartography file for the VaultBridge project. It documents the
"Secret Retrieval" flow.

---

```markdown
---
name: Secret Retrieval
description: How secrets are fetched, decrypted, and returned to the client via the retrieval API
created: 2026-02-16
updated: 2026-02-18
tags: [crypto, data-flow, kms, retrieval]
related: [secret-creation, key-rotation]
---

## Overview

The secret retrieval flow handles authenticated requests to read stored secrets from a vault. It
spans the API gateway, core service, and AWS KMS — involving session authentication, RBAC policy
evaluation, key unwrapping, and client-side decryption. This is the primary path through which
stored credentials can be exfiltrated if any step is compromised.

## Entry Points

- `gateway/src/routes/secrets.ts:getSecret` — HTTP GET /api/v1/vaults/:id/secrets/:name
- `core/src/handlers/secret_handler.rs:handle_get_secret` — gRPC handler invoked by gateway

## Key Components

- `gateway/src/middleware/auth.ts` — session validation, extracts user identity from Redis-backed session
- `gateway/src/middleware/rate_limit.ts` — rate limiting on secret access endpoints
- `core/src/authz/policy.rs` — RBAC permission evaluation for vault access
- `core/src/authz/roles.rs` — role definitions and hierarchy
- `core/src/crypto/envelope.rs` — DEK unwrapping and envelope decryption helpers
- `core/src/crypto/kms_client.rs` — AWS KMS integration for KEK unwrap operations
- `core/src/storage/vault_store.rs` — fetches encrypted blob and wrapped DEK from PostgreSQL

## Flow Sequence

1. Client sends authenticated GET request (`gateway/src/routes/secrets.ts:getSecret`)
2. Gateway rate limiter checks request against per-user limits (`gateway/src/middleware/rate_limit.ts`)
3. Gateway auth middleware validates session token against Redis (`gateway/src/middleware/auth.ts:validateSession`)
4. Gateway serializes request and forwards to core service via gRPC
5. Core evaluates RBAC policy for the requesting user against the target vault (`core/src/authz/policy.rs:evaluate`)
6. Core fetches encrypted blob + wrapped DEK from PostgreSQL (`core/src/storage/vault_store.rs:get_secret`)
7. Core sends wrapped DEK to AWS KMS for unwrapping (`core/src/crypto/kms_client.rs:unwrap_key`)
8. Core returns encrypted blob + unwrapped DEK to gateway, gateway proxies to client
9. Client decrypts blob locally using the DEK

## Security Notes

- Trust boundary: gateway performs session auth, core performs RBAC — a request that passes gateway auth
  but targets a vault the user lacks access to should be caught at step 5. Verify both layers are
  consistent and that gateway doesn't cache stale permission grants.
- TOCTOU: permission check (step 5) and data fetch (step 6) are separate database queries. A permission
  revocation between the two queries could leak a secret.
- DEK is transmitted in plaintext from core to client (step 8). This relies entirely on TLS for
  confidentiality. No application-layer encryption on this leg.
- KMS latency in step 7 could enable a timing side-channel to distinguish "secret exists but no access"
  from "secret does not exist" — the KMS call only happens after a successful fetch.
- Rate limiter (step 2) is per-user but does not account for team/service-account tokens, which could
  allow higher volume enumeration.

## Conditional: Shared Vault Access

<!-- condition: load only when investigating shared vaults, team-level access, or cross-team secret sharing -->

Shared vaults allow multiple teams to access the same secret store. The permission path differs:

### Entry Points

- `core/src/authz/sharing.rs:evaluate_shared_access` — additional policy check for shared vaults

### Key Components

- `core/src/authz/sharing.rs` — shared vault permission logic, checks team membership + vault sharing grants
- `core/src/storage/sharing_store.rs` — tracks which vaults are shared with which teams

### Flow Sequence

1. Steps 1-4 from main flow
2. Core detects vault is shared (`core/src/storage/vault_store.rs:get_secret` returns sharing metadata)
3. Core evaluates shared access policy (`core/src/authz/sharing.rs:evaluate_shared_access`)
4. If shared access granted, continues from step 6 of main flow

### Security Notes

- Shared access grants are cached in Redis for performance — stale cache entries could outlive revocation
- Team membership changes should invalidate shared access, but this relies on an async event pipeline

## Related Flows

- [[cartography/secret-creation]] — the write path; same authz checks but different crypto direction
- [[cartography/key-rotation]] — KEK rotation affects the unwrap step; during rotation grace period,
  both old and new KEKs may be valid
```

## examples/cross-service-auth-example.md

# Example: Cross-Service Authentication Flow

This is a worked example of a more complex cartography file — a cross-service authentication
flow spanning multiple services with two conditional sections. Compare with
`cartography-example.md` (single-service, one conditional) to see how conditional sections
scale.

---

```markdown
---
name: User Authentication
description: End-to-end authentication flow from login form through token issuance, session creation, and downstream service authorization
created: 2026-03-10
updated: 2026-03-12
tags: [auth, session, jwt, rbac, cross-service]
related: [password-reset, token-refresh, service-mesh-authz]
---

## Overview

The user authentication flow handles credential submission, identity verification, token
issuance, and session propagation across three services (API gateway, auth service, user
service). It is the primary trust establishment path — a bypass at any stage grants full
account access. The flow branches into OAuth and passkey sub-flows that use different
verification paths but converge at token issuance.

## Entry Points

- `gateway/src/routes/auth.ts:login` — POST /api/v1/auth/login
- `gateway/src/routes/auth.ts:loginCallback` — GET /api/v1/auth/callback (OAuth return)
- `auth-service/src/grpc/auth_server.py:Authenticate` — gRPC handler for internal auth requests

## Key Components

- `gateway/src/middleware/csrf.ts` — CSRF token validation on login form submission
- `gateway/src/middleware/rate_limit.ts` — brute-force protection on login endpoints
- `auth-service/src/grpc/auth_server.py` — gRPC service handling authentication requests
- `auth-service/src/providers/credential.py` — password hashing and comparison (argon2id)
- `auth-service/src/token/issuer.py` — JWT creation, signing (ES256), claims population
- `auth-service/src/token/keys.py` — signing key rotation and JWKS endpoint backing
- `auth-service/src/session/store.py` — Redis session creation with configurable TTL
- `user-service/src/grpc/user_server.go` — user record lookup and status checks
- `user-service/src/repo/user_repo.go` — PostgreSQL user store with soft-delete awareness

## Flow Sequence

1. Client submits credentials via POST (`gateway/src/routes/auth.ts:login`)
2. Gateway validates CSRF token (`gateway/src/middleware/csrf.ts:validate`)
3. Gateway checks brute-force rate limit (`gateway/src/middleware/rate_limit.ts:checkLogin`)
4. Gateway forwards credentials to auth service via gRPC (`auth-service/src/grpc/auth_server.py:Authenticate`)
5. Auth service requests user record from user service (`user-service/src/grpc/user_server.go:GetUser`)
6. User service fetches record from PostgreSQL (`user-service/src/repo/user_repo.go:FindByEmail`)
7. Auth service verifies password against stored hash (`auth-service/src/providers/credential.py:verify`)
8. Auth service issues JWT with user claims (`auth-service/src/token/issuer.py:issue`)
9. Auth service creates Redis session (`auth-service/src/session/store.py:create`)
10. Gateway sets session cookie and returns JWT to client

## Security Notes

- Rate limiter (step 3) keys on IP + email pair — distributed attacks across IPs with a fixed
  email are caught, but credential stuffing across many emails from one IP may slip through.
- User lookup (step 6) does not distinguish "user not found" from "user soft-deleted" in the
  gRPC response — auth service treats both as invalid credentials. Verify this doesn't leak
  account existence via timing.
- Password verification (step 7) uses argon2id with server-side timing — but the gRPC round
  trip to user-service (step 5-6) adds variable latency that could mask or reveal the
  verify step's constant-time properties.
- JWT signing key (step 8) rotation is manual via config reload. If key rotation is delayed,
  compromised keys have unbounded validity.
- Session TTL (step 9) defaults to 24h but is overridden per-environment in config. Check
  whether staging/dev environments use longer TTLs that could be exploited if accessible.
- CSRF validation (step 2) is skipped for requests with `Authorization: Bearer` header —
  verify this doesn't create a bypass when both cookie and bearer are present.

## Conditional: OAuth Provider Flow

<!-- condition: load only when investigating OAuth login, third-party identity providers, SSO, or the /callback endpoint -->

OAuth login replaces steps 4-7 of the main flow with provider-delegated verification.

### Entry Points

- `gateway/src/routes/auth.ts:loginCallback` — OAuth callback after provider redirect
- `auth-service/src/providers/oauth.py:handle_callback` — processes OAuth code exchange

### Key Components

- `auth-service/src/providers/oauth.py` — OAuth code exchange and profile mapping
- `auth-service/src/providers/registry.py` — registered OAuth providers and their configs
- `auth-service/src/providers/profile_mapper.py` — maps provider profile to internal user schema

### Flow Sequence

1. Steps 1-3 from main flow (CSRF + rate limit still apply)
2. Gateway redirects to OAuth provider authorize URL
3. User authenticates with provider, provider redirects to callback
4. Gateway receives callback (`gateway/src/routes/auth.ts:loginCallback`)
5. Auth service exchanges code for token (`auth-service/src/providers/oauth.py:exchange_code`)
6. Auth service fetches user profile from provider (`auth-service/src/providers/oauth.py:fetch_profile`)
7. Auth service maps provider profile to internal user (`auth-service/src/providers/profile_mapper.py:map_to_user`)
8. Continues from step 8 of main flow (JWT issuance + session)

### Security Notes

- OAuth state parameter must be validated to prevent CSRF on callback — check `handle_callback`
- Provider profile email may differ from internal user email — verify mapping doesn't allow
  account takeover by registering a matching email at the OAuth provider
- Code exchange (step 5) must use PKCE or server-side secret — check which flow is configured

## Conditional: Passkey / WebAuthn Flow

<!-- condition: load only when investigating passkeys, WebAuthn, FIDO2, or passwordless authentication -->

Passkey authentication replaces the password verification step (step 7) with a WebAuthn
challenge-response.

### Entry Points

- `auth-service/src/providers/webauthn.py:begin_authentication` — generates challenge
- `auth-service/src/providers/webauthn.py:complete_authentication` — verifies assertion

### Key Components

- `auth-service/src/providers/webauthn.py` — WebAuthn ceremony handling (challenge + verify)
- `auth-service/src/storage/credential_store.py` — stored public key credentials per user

### Flow Sequence

1. Steps 1-6 from main flow (user lookup still required to find registered credentials)
2. Auth service generates WebAuthn challenge (`auth-service/src/providers/webauthn.py:begin_authentication`)
3. Client performs WebAuthn ceremony with authenticator
4. Client sends signed assertion back
5. Auth service verifies assertion against stored credential (`auth-service/src/providers/webauthn.py:complete_authentication`)
6. Continues from step 8 of main flow (JWT issuance + session)

### Security Notes

- Challenge must be single-use and time-bounded — check if challenges are stored in Redis with TTL
- Credential store should enforce per-user credential limits to prevent DoS via credential flooding
- Verify that passkey flow cannot be downgraded to password flow without user confirmation

## Related Flows

- [[cartography/password-reset]] — shares the user lookup path (steps 5-6) and session creation
- [[cartography/token-refresh]] — uses the same JWT issuer but with refresh-specific claims
- [[cartography/service-mesh-authz]] — downstream services validate the JWT issued in step 8
```

## references

```

```

## references/cartography-format.md

# Cartography File Format

This reference defines the format for cartography files stored in `grimoire/cartography/`.

## File Location

All cartography files live in `grimoire/cartography/` with a slugified filename derived from the
flow name: `grimoire/cartography/secret-retrieval.md`.

## Frontmatter

Every cartography file starts with YAML frontmatter:

```yaml
---
name: Secret Retrieval
description: How secrets are fetched, decrypted, and returned to the client
created: 2026-02-16
updated: 2026-02-16
tags: [crypto, data-flow, kms]
related: [key-rotation, secret-creation]
---
```

| Field         | Required | Description                                                    |
|---------------|----------|----------------------------------------------------------------|
| `name`        | yes      | Short flow name. Used in the index and for display.            |
| `description` | yes      | One-line description. Used in the index and for agent matching.|
| `created`     | yes      | ISO date when the file was first created.                      |
| `updated`     | yes      | ISO date of the last modification.                             |
| `tags`        | no       | Freeform categorization tags for filtering.                    |
| `related`     | no       | List of other cartography file slugs (without `.md`).          |

**Constraint:** `name` and `description` must each be a single line. The indexing script relies
on this.

## Body Sections

### Overview

2-3 sentences describing the flow and its security relevance. This is what an agent reads to
decide whether to load the full file.

```markdown
## Overview

The secret retrieval flow handles authenticated requests to read stored secrets. It involves
permission checks, KMS key unwrapping, and client-side decryption. Security-critical because it
is the primary path to exfiltrating stored credentials.
```

### Entry Points

List the entry points into this flow using `path/to/file:symbol` notation. Entry points are
where execution begins for this flow — API endpoints, CLI commands, event handlers, etc.

```markdown
## Entry Points

- `gateway/src/routes/secrets.ts:getSecret` — HTTP GET /api/v1/vaults/:id/secrets/:name
- `core/src/handlers/secret_handler.rs:handle_get_secret` — gRPC handler
```

### Key Components

Modules and files that participate in the flow, with a brief note on their role. This is the
navigation map — it tells you *where* to look, not *what the code does*.

```markdown
## Key Components

- `core/src/authz/policy.rs` — RBAC permission evaluation for vault access
- `core/src/crypto/envelope.rs` — DEK unwrapping and envelope decryption
- `core/src/crypto/kms_client.rs` — AWS KMS integration for KEK operations
- `core/src/storage/vault_store.rs` — fetches encrypted blob and wrapped DEK from PostgreSQL
```

### Flow Sequence

Numbered steps tracing execution through the system. Reference files at each step. Keep it
linear — branch points or conditional paths that are independently useful should go in
conditional sections.

```markdown
## Flow Sequence

1. Client sends GET request to gateway (`gateway/src/routes/secrets.ts:getSecret`)
2. Gateway middleware authenticates session (`gateway/src/middleware/auth.ts:validateSession`)
3. Gateway forwards to core via gRPC (`core/src/handlers/secret_handler.rs:handle_get_secret`)
4. Core evaluates RBAC policy (`core/src/authz/policy.rs:evaluate`)
5. Core fetches encrypted blob + wrapped DEK (`core/src/storage/vault_store.rs:get_secret`)
6. Core unwraps DEK via KMS (`core/src/crypto/kms_client.rs:unwrap_key`)
7. Core returns encrypted blob + unwrapped DEK to client
8. Client decrypts locally using DEK
```

### Security Notes

Trust boundaries, validation gaps, observations, and areas that warrant investigation. These
are the security researcher's annotations — the *reason* this flow matters.

```markdown
## Security Notes

- Trust boundary between gateway authz and core authz — are they consistent?
- DEK is in plaintext in transit from core to client (relies on TLS)
- Permission check (step 4) and data fetch (step 5) are separate queries — TOCTOU window?
- KMS call in step 6 could be used for a timing side-channel to enumerate valid secret IDs
```

### Conditional Sections

Optional sections for sub-flows that would pollute the main flow's context. An agent evaluates
the condition comment to decide whether to load the section.

```markdown
## Conditional: Shared Vault Access

<!-- condition: load only when investigating shared vaults, team-level access, or cross-team secret sharing -->

Shared vaults use a different permission path...
```

**Format:**
- Section heading: `## Conditional: [Sub-flow Name]`
- Condition comment: `<!-- condition: load only when [topic description] -->`
- Body follows the same patterns (entry points, components, sequence, notes) but scoped to the
  sub-flow

Use conditional sections when:
- The main flow body exceeds ~80 lines
- A sub-flow is only relevant for specific investigations
- Including the sub-flow would dilute the main flow's signal-to-noise ratio

### Related Flows

Cross-links to other cartography files. These should match the `related` field in frontmatter.

```markdown
## Related Flows

- [[cartography/secret-creation]] — the write path for secrets
- [[cartography/key-rotation]] — KEK rotation affects the unwrap step in this flow
```

## Key Constraint

**Cartography files are pointers, not containers.** They document *where* to look, not *what
the code does*. An agent reading a cartography file should know exactly which files to open and
in what order — but should read those files itself to understand the actual logic.

If you find yourself writing detailed code explanations, stop. Add the file path and a one-line
role description instead. Detailed analysis belongs in `grimoire/tomes/`.

## scripts

```

```

## scripts/index-cartography.sh

```bash
#!/usr/bin/env bash
# index-cartography.sh — Index cartography files by reading YAML frontmatter.
# Outputs tab-separated: name\tdescription\tfilepath
# Usage: index-cartography.sh [directory]
# Default directory: grimoire/cartography/

set -euo pipefail

dir="${1:-grimoire/cartography/}"

# Ensure directory exists
if [ ! -d "$dir" ]; then
  exit 0
fi

for file in "$dir"/*.md; do
  # Handle case where glob matches nothing
  [ -e "$file" ] || continue

  # Skip _index.md if present
  basename=$(basename "$file")
  if [ "$basename" = "_index.md" ]; then
    continue
  fi

  name=""
  description=""
  in_frontmatter=0

  while IFS= read -r line; do
    # Detect frontmatter boundaries
    if [ "$line" = "---" ]; then
      if [ "$in_frontmatter" -eq 0 ]; then
        in_frontmatter=1
        continue
      else
        # End of frontmatter
        break
      fi
    fi

    if [ "$in_frontmatter" -eq 1 ]; then
      # Extract name field
      case "$line" in
        name:*)
          name="${line#name:}"
          name="${name# }"
          name="${name#\"}"
          name="${name%\"}"
          name="${name#\'}"
          name="${name%\'}"
          ;;
        description:*)
          description="${line#description:}"
          description="${description# }"
          description="${description#\"}"
          description="${description%\"}"
          description="${description#\'}"
          description="${description%\'}"
          ;;
      esac
    fi
  done < "$file"

  # Only output if we found both fields
  if [ -n "$name" ] && [ -n "$description" ]; then
    printf '%s\t%s\t%s\n' "$name" "$description" "$file"
  fi
done
```

