---
title: "Selective Spec Loading: Measured Token Savings From a Self-Map of Command Specs"
description: "ForgeDock symlinks ~27 command specs (~1.1 MB) into every Claude Code session. We built a queryable graph of the specs and load only the ones a task actually reaches — and measured how many tokens that saves."
tags: ["ai", "performance", "tokens", "productivity", "opensource"]
cover_image:
canonical_url: https://github.com/RapierCraftStudios/ForgeDock
published: false
---

<!-- Generated by ForgeDock pipeline — Issue #868 -->

AI coding agents pay for context by the token. ForgeDock's pipeline is driven by
markdown command specs — `work-on`, `review-pr`, `quality-gate`, and ~40 more —
that are symlinked into `~/.claude/commands/` and therefore loaded into the model's
context at session start, whether or not a given run ever touches them. This
article measures that cost and quantifies how a **self-map of the specs** lets a
run load only the specs it actually reaches.

---

## The Problem: The Whole Corpus Loads Even Though One Command Runs

A single ForgeDock run invokes exactly one entry command — usually `work-on`.
That command reaches a small, well-defined set of downstream specs: its own
sub-phases (`work-on/build/*`, `work-on/review`, `work-on/close`) and the review
orchestrator it delegates to (`review-pr`). It never reads `pipeline-health.md`,
`geo-audit.md`, `audit.md`, `incident-response.md`, or the two dozen other
specs that have nothing to do with the issue at hand.

But all of them are present in context. Measured on the current spec set:

| Metric | Value |
|--------|-------|
| Command spec files | 44 |
| Total bytes | 1,104,004 |
| Approx. tokens (1 token ≈ 4 chars) | **~276,000** |

That is ~276K tokens of spec text loaded on every session — the overwhelming
majority of which is dead weight for any single task.

---

## The Mechanism: A Queryable Graph of the Specs

ForgeDock already builds a **spec knowledge graph** — a zero-dependency self-map
of its own command specs (`scripts/build-spec-graph.mjs`, queried by
`scripts/graph-query.sh`). The graph records which command *contains* which
sub-phase, which command *requires* which devdoc, which annotation a command
*reads* or *writes*, and so on.

The graph already answered "what is the blast radius if I change node X?" via its
`impact` query (transitive **reverse** reachability). Selective loading needs the
mirror image: the **forward** transitive reachable set of a command. We added a
`load-set` subcommand that does exactly that:

```bash
$ graph-query.sh load-set work-on
["commands/review-pr.md","commands/work-on.md","commands/work-on/build.md",
 "commands/work-on/build/architect.md","commands/work-on/build/context.md",
 "commands/work-on/build/implement.md","commands/work-on/build/validate.md",
 "commands/work-on/close.md","commands/work-on/decompose.md",
 "commands/work-on/investigate.md","commands/work-on/review.md"]
```

`load-set` seeds at the command node and walks `CONTAINS` (sub-phases) and
`REQUIRES` (devdocs) edges forward to a fixpoint, then maps the reachable nodes to
their repo-relative file paths. It is read-only, auto-builds the graph if the
gitignored JSON is absent, and shares the existing `impact` resolver's cycle-safe
termination. No graph logic was reimplemented — the feature *consumes* the
builder and query script that were already there.

`work-on` Phase 0 now calls `load-set work-on` and reads **only** the returned
paths instead of relying on the whole symlinked corpus.

---

## Methodology

- Token counts are estimated at the standard rate of **1 token ≈ 4 characters**.
- **Baseline (load-all)** = the byte sum of every spec under `commands/`.
- **Selective (load-set)** = `wc -c` of just the files returned by
  `graph-query.sh load-set <command>` for the entry command of a session.
- Measured across **five representative sessions**, each keyed by its entry command.

This mirrors the methodology used in the per-repo adaptive-scripts measurement,
so the numbers are directly comparable.

---

## Baseline

Every session, with the load-all corpus:

| | Files | Bytes | Approx. tokens |
|--|------:|------:|---------------:|
| **Load-all corpus** | 44 | 1,104,004 | ~276,000 |

---

## Target: Per-Session Selective Load

| Session (entry command) | Files | Bytes | Approx. tokens | Tokens saved vs baseline | % saved |
|-------------------------|------:|------:|---------------:|-------------------------:|--------:|
| `work-on` (full feature build) | 11 | 296,172 | ~74,000 | ~202,000 | **73%** |
| `orchestrate` (multi-issue) | 14 | 390,054 | ~98,000 | ~178,000 | **65%** |
| `review-pr` (PR review) | 1 | 64,395 | ~16,000 | ~260,000 | **94%** |
| `quality-gate` (pre-commit) | 1 | 47,676 | ~12,000 | ~264,000 | **96%** |
| `issue` (fast-lane triage) | 1 | 19,945 | ~5,000 | ~271,000 | **98%** |

---

## Savings

Across the five representative sessions, selective loading removes between
**~178,000 and ~271,000 tokens** of spec text from context — a **65%–98%**
reduction depending on how much of the pipeline the entry command actually
reaches. The heaviest run (`orchestrate`, which fans out to many sub-phases)
still drops nearly two-thirds of the corpus; the lightest (`issue`) drops 98%.

The win scales with breadth: the more specialized commands ForgeDock grows, the
larger the dead-weight fraction the load-all corpus carries — and the more a run
saves by loading only what its graph neighborhood reaches. Because the resolution
is graph-driven and deterministic (the transitive walk lives in `jq`, not in the
prompt), adding a new command automatically narrows the set for every *other*
command, with no manual bookkeeping.

---

## How To Reproduce

```bash
# Baseline: full corpus
find commands -name '*.md' -type f -exec cat {} + | wc -c

# Selective: minimal set for a given entry command
graph-query.sh load-set work-on | jq -r '.[]' \
  | while read -r p; do cat "$p"; done | wc -c
```

Divide bytes by 4 for the approximate token count.
