Use when planning tasks that touch big source files, when an edit would push a file past the module-size ceiling, when the pre-commit module-size ratchet warns or blocks, or when generating policies/module-size-baseline.txt. Operationalizes FR-25 — module-size ratchet (ceiling default 800; over-ceiling files may shrink, never grow; extraction along a responsibility seam is in-scope, not scope creep). Do NOT use for docs/markdown size (FR-23 owns docs), to force decomposition of an existing mo...
Installs into .claude/skills of the current project.
Are you the author of Module Size Discipline?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/fusebase-dev-module-size-discipline)
---
name: module-size-discipline
description: Use when planning tasks that touch big source files, when an edit would push a file past the module-size ceiling, when the pre-commit module-size ratchet warns or blocks, or when generating policies/module-size-baseline.txt. Operationalizes FR-25 — module-size ratchet (ceiling default 800; over-ceiling files may shrink, never grow; extraction along a responsibility seam is in-scope, not scope creep). Do NOT use for docs/markdown size (FR-23 owns docs), to force decomposition of an existing monolith (that is its own explicit ticket), or to judge split quality by regex (seam-vs-mechanical is semantic — code-review owns it).
source_inspiration: conceptual-only
license_status: clean-room-original
fusebase_flow_version: "3.16"
risk_level: medium
invocation: automatic
expected_outputs:
- per-task target-file declarations (tasks.md) with extraction-or-exemption for over-ceiling targets
- extraction into a new module along a responsibility seam, or an explicit exemption
- policies/module-size-baseline.txt via agent-run --write-baseline (on the operator's chat go-ahead)
related_workflows:
- greenlight-implement.md
- verification-gate.md
- lightweight-lane.md
hook_dependencies:
- hooks/git/pre-commit (module-size step)
---
# Module-Size Discipline (FR-25)
## Purpose
Stop source files from accreting into monoliths nobody (human or agent) can load in one pass. Source in a Flow workflow is AI-read (FR-22/FR-24 audience principle): a 19k-line file forces slice-reads on every future session. Monoliths are the integral of N individually-reasonable diffs — no single diff is ever flagged — so the framework gates the accumulating dimension deterministically (line count is objective, unlike FR-22/FR-23 semantics) and steers extraction at plan- and write-time.
## The ratchet (deterministic gate)
Config: `policies/module-size.yml` (`ceiling` default 800 · `source_globs` · `exempt_globs` · `baseline_file` · `enforcement` · gitignored `module-size.local.yml` override). Engine: `hooks/shared/module_size.py`, wrapper `hooks/local/check-module-size.sh`, wired into `hooks/git/pre-commit`.
| Case | Verdict |
|---|---|
| Gated file NOT in baseline, lines ≤ ceiling | pass |
| NOT in baseline, **NEW** over ceiling (crosses the ceiling in this change) | **BLOCK** — extract, or adopt (`--write-baseline`) |
| NOT in baseline, **PRE-EXISTING** over ceiling (over ceiling at HEAD), touched/shrunk in a change gate (`--staged`/`--worktree`, not growing) | pass — the refactor path (delta-aware adoption grace) |
| NOT in baseline, **PRE-EXISTING** over ceiling, **GROWN** | **BLOCK** — a monolith may be touched/shrunk, never grown |
| Any over-ceiling not-baselined file under `--all` (audit) | **reported** (absolute; no HEAD delta) — tells you what to adopt |
| Baselined file, lines ≤ its baseline value | pass (shrinking/holding is always fine) |
| Baselined file, lines > its baseline value | **BLOCK** — over-ceiling files may not grow |
| File matches `exempt_globs` | never gated |
| No `baseline_file` committed | **warn-only** + generation instruction (adoption-safe) |
Modes: `--staged` (pre-commit default) · `--worktree` (vs HEAD; optional Stop-hook wiring) · `--all` (full scan; also a CI step in `fusebase-flow-verify.yml`) · `--write-baseline` (**agent-run on the operator's chat go-ahead** — freezes current over-ceiling files; the agent commits the result; presence switches warn → block) · `--write-baseline <path>` (re-keys ONE row — the targeted refresh; a full regen grandfathers every current violation, so prefer the single-file form).
Baseline shipping: the template **ships its own committed baseline** (Flow's own over-ceiling files), so the gate is live from commit #1 on greenfield instantiations. Installing into an **existing** repo with pre-existing monoliths: those files are **not** hard-blocked on first touch — the delta-aware change gate lets you touch/shrink a pre-existing over-ceiling file (only NEW-over-ceiling files and growth block). To freeze them at their current size (so future growth is caught against a recorded value, and to clear the `--all` audit), the agent runs `--write-baseline` once **on the operator's chat go-ahead** — it **auto-mints a single-use FR-07 approval** for the protected baseline path, and the agent then commits and consumes it (the sanctioned path; never `--no-verify`). The operator approves adoption in chat; they run no terminal command.
Local override (`policies/module-size.local.yml`, gitignored) is **additive-only**: it may append `exempt_globs` / `source_globs` entries; `enforcement`, `ceiling`, and `baseline_file` cannot be overridden locally (a gitignored kill switch would disarm the gate invisibly). The engine prints a notice whenever a local override is active.
Rename tripwire: the baseline keys by path — after renaming a baselined over-ceiling file, its first content edit blocks (fail-closed, zero-growth included) until it is re-keyed — the agent runs `--write-baseline <new-path>` on the operator's go-ahead (and the old row disappears on the next targeted or full refresh; stale rows for absent files are inert).
## Plan-time rule (where the problem starts)
In `implementation-planning` / `tasks.md`: **every task names its target file(s)**. A task targeting an over-ceiling file must either (a) extract the addition into a new module, or (b) carry a one-line exemption with reason. "Where does this code live" is a cheap question at Plan and a never-asked question mid-implement.
## Write-time rule (AI Developer)
- An edit that would push a gated file over the ceiling, or grow an already-over-ceiling file → extract along a **responsibility seam** (a nameable concern), not a mechanical `utils2.ts` split.
- That extraction is **in-scope for the task**: it is NOT scope creep and NOT by itself an FR-21 promotion trigger. (FR-21 interplay — see `lightweight-lane`.)
- Never bypass with `--no-verify` (FR-06). Remedies: extract, or surface the exemption question to the operator (FR-19).
## Exemptions (deliberate, reviewable)
Justified monolith classes go in `exempt_globs`: generated code (SDK/OpenAPI output), lockfiles, vendored complete-file mirrors, data-as-code catalogs/fixtures, migrations. Exemption is a policy edit the operator sees — never silent.
## What this skill does NOT do
- No forced refactor: existing monoliths freeze at baseline; decomposition is its own (usually Full-lane) ticket with its own risk assessment.
- No split-quality regex: seam-vs-mechanical is semantic — `code-review` checks it by reading.
- No docs gating: artifact/doc size is FR-23's axis.
## Anti-patterns
- Splitting mechanically to satisfy the gate — observable criterion: an extraction landing in a file whose name does not state a responsibility (`utils2`, `helpers2`, `misc`, `extra`, `more`-style names) is a review blocker; a named seam is judged by reading.
- Raising the baseline to make a violation pass — adoption needs the operator's explicit go-ahead (given in chat), never the agent's own initiative to dodge a block; on that go-ahead the agent runs `--write-baseline` (prefer the single-file form so refreshes are never global amnesties). The guard is the operator's *decision*, not making the operator *type the command*.
- Treating the ratchet warning as noise in warn-only mode — surface it to the operator with the activation instruction instead.
- Adding `exempt_globs` entries for ordinary source because extraction is inconvenient.
## Clean-room note
Original Fusebase Flow content. Ratchet-style size gating is common public CI practice (grandfathering + no-growth baselines); no third-party code, prompts, skill files, or hook scripts are copied. See `docs/source-map.md`.