`::toc` renders through a `toc` fragment receiving level, text and the id goldmark already assigns. The engine collects, the theme decides whether that is a list, a sidebar or nothing — the reference theme emits a flat <ol> with a level class per entry, so indentation is CSS rather than markup. A theme cannot enumerate headings, because templates cannot parse HTML, so the engine is the only thing that can supply them. Collection lives in ext; only the Heading type and one Fragment field are core. That is what needed the ceiling: CORE_LOC_MAX 2800 -> 2850, the second raise. ADR-0041 said to read a second raise as evidence something belongs in ext, and the test was applied rather than waived. feed.go and discover.go are the features that should leave core, and they cannot, because an ext feature cannot own a route until the extension registry exists — which the counter says buys nothing yet. The thing that should move is blocked on a different decision, so the honest fix was the ceiling and an ADR saying exactly that. When routes become ownable, they leave and this comes back down. Entry text is the heading's words with markup stripped: a link inside a link is not markup a browser accepts. An entry whose heading has no id is skipped rather than linked nowhere, and a page with no headings renders no nav at all. core 2804/2850, ext 1825/2000, 34 gates green, 0 warnings.
34 lines
2.6 KiB
Bash
34 lines
2.6 KiB
Bash
# Growth budgets. `_MAX` fails verify.sh; `_WARN` prints and moves on.
|
|
# Raising a _MAX needs an ADR stating old and new values. Growth requires a signature.
|
|
#
|
|
# Only whole-system budgets fail. A hard per-file limit is the one gate whose cheapest fix makes the
|
|
# code worse — sharding a coherent file into a `_helpers.go` turns it green while creating the package
|
|
# CLAUDE.md rule 3.6 bans. Total mass cannot be gamed by moving code between files; file length can.
|
|
|
|
CORE_LOC_MAX=2850 # cmd/ + internal/{content,render,web} + repo root, non-test .go (ADR-0041, ADR-0065)
|
|
EXT_LOC_MAX=2000 # internal/ext/ — composition, grows after the core freezes
|
|
FILE_LOC_WARN=500 # any single .go file — advisory
|
|
FUNC_LOC_WARN=60 # any single function — advisory
|
|
DEPS_MAX=6 # total modules in go.mod, direct plus indirect
|
|
CLAUDE_LOC_MAX=150 # CLAUDE.md only — the one file billed on every turn (ADR-0053)
|
|
|
|
# Two ceilings because "the core stops growing after Arc 2" (architecture.md invariant 9) is only an
|
|
# invariant if something measures it: post-freeze CORE holds and only EXT rises.
|
|
# Costed, not round, and re-costed once (ADR-0041): the first figure covered spine, bundles, queries,
|
|
# render, routing and templates (~1200-1800 → 2000) but never budgeted what cmd/ would hold. Adding `check`
|
|
# ~250, `new` ~100, `-dev` ~100, change detection ~100 and the page cache ~200 to a core already at 1870
|
|
# gives ~2620 → CORE 2800. A second raise is evidence something belongs in internal/ext/, not evidence the
|
|
# number is still too small.
|
|
# Feeds, sitemap, OpenGraph, shortcodes, image sizing, galleries, paging, indieweb ~900-1200 → EXT
|
|
# 2000, deliberately loose since that is where growth belongs. Reaching CORE asks what to delete;
|
|
# reaching EXT asks whether a template would have done it — only .go lines count here.
|
|
# Raised a second time, 2800 → 2850 (ADR-0065), for eight lines of theme contract. ADR-0041 said to read a
|
|
# second raise as evidence something belongs in `ext`: it is, and the candidates are `feed.go` and
|
|
# `discover.go`, which cannot move until a feature can own a route. That is the registry's trigger, not this
|
|
# feature's problem, and the honest fix was not to pretend the contract fits.
|
|
#
|
|
# CLAUDE_LOC_MAX is a different kind of budget: every other ceiling here bills once, when someone reads
|
|
# the code, while CLAUDE.md is re-sent on every turn of every session. 150 leaves ~20 lines of headroom
|
|
# over the current file. Reaching it means moving detail to the doc that owns the topic, never deleting
|
|
# a rule to fit — `docs/README.md`'s compression contract governs which of the two you are doing.
|