diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..36c12b6 --- /dev/null +++ b/.oxlintrc.json @@ -0,0 +1,8 @@ +{ + "$schema": "./node_modules/oxlint/configuration_schema.json", + "categories": { "correctness": "off" }, + "ignorePatterns": ["src/**/*.test.ts", "src/property-harness.ts"], + "rules": { + "eslint/max-lines-per-function": ["error", { "IIFEs": true, "max": 52, "skipBlankLines": false, "skipComments": false }] + } +} diff --git a/AGENTS.md b/AGENTS.md index 64645e0..faf8c17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,7 +78,9 @@ vendored the same way, at `spec/adf-schema/`, rather than as the `@atlaskit/adf- dependency — CommonJS-only, some fifty packages with React among them, and a release most days for Renovate to automerge — re-pinned by hand when a payload or a report shows the need. `devDependencies`: few, each earning its keep; they never reach a consumer. `fast-check` earns its -place shrinking a failing generated document to the nodes that break it. +place shrinking a failing generated document to the nodes that break it, `oxlint` measuring §10's +size ratchet — TypeScript 7 is a native compiler publishing no in-process parser, only the +`unstable/` AST surface an out-of-process handshake reaches. ## 6. The package contract @@ -254,6 +256,16 @@ functions, and a branch floor that only ever moves upward. It sits below 100 bec `noUncheckedIndexedAccess` and ADF's optional keys force — `?? []`, `?? {}`, `?.`, an index compared against `undefined` — have a half no valid document reaches. +The size ratchet is the other such number, `.oxlintrc.json`'s single rule over the files +`tsconfig.build.json` builds: a per-function line ceiling, set at that set's worst and moving only +downward. It covers the built files alone, since one ceiling over the tests too would have to be +their worst, loosening the guard over the shipped code. It guards against drift and never drives a +refactor, so no cyclomatic rule and no second lint rule join it: neither measure picked out what +nine readers found hard (the comprehension panel, 2026-09-20). Three switches guard a silent green: +`IIFEs: true`, since oxlint exempts an IIFE otherwise; an explicit `-c`, so a config gone missing +fails the leg instead of falling back to oxlint's own defaults; and `--deny-warnings`, since a rule +from a category this config never names arrives as a warning it exits 0 on. + The corpus, all checked in: hand-built fixtures per node and combination; real ADF Atlassian's editor wrote; the CommonMark spec suite against `markdownToAdf` and `markdownToHtml`. diff --git a/ci.sh b/ci.sh index 2c99726..b2f035c 100755 --- a/ci.sh +++ b/ci.sh @@ -5,6 +5,7 @@ source ./docker-runner.sh leg "install ($node_image)" in_image "$node_image" npm ci leg "typecheck ($node_image)" in_image "$node_image" npm run typecheck +leg "size ratchet ($node_image)" in_image "$node_image" npm run size-ratchet test_log=$(mktemp) node_tests() { diff --git a/package-lock.json b/package-lock.json index d7eeb19..ea73450 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,360 @@ "devDependencies": { "@types/node": "24.13.3", "fast-check": "4.10.0", + "oxlint": "1.83.0", "typescript": "7.0.2" }, "engines": { "node": ">=18" } }, + "node_modules/@oxlint/binding-android-arm-eabi": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm-eabi/-/binding-android-arm-eabi-1.83.0.tgz", + "integrity": "sha512-0yGY24EwsLk5YDe6F+VkmZyRHSwJDALa3nIrPpq7FXmp2lV2d0TzvBCGeZk+wgiULRGr5blhyr4QMp5KCXJUqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-android-arm64": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-android-arm64/-/binding-android-arm64-1.83.0.tgz", + "integrity": "sha512-hHfJ0vc17A4iUjH5p9BsTUPYbYRNxGpvD2lbu1aBRk54bzNIx9o5TtYF39QPZcV95DagZd+4DEAw2RH3G2ZsMg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-arm64": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-arm64/-/binding-darwin-arm64-1.83.0.tgz", + "integrity": "sha512-hsOjYjszLb/3zym/TkzUMPAoQlTJcuzSyEPOAyA+skXJIX9M0o+4JfOtqopX/Vf4hSLrJ98j0nvFo23gzk8auQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-darwin-x64": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-darwin-x64/-/binding-darwin-x64-1.83.0.tgz", + "integrity": "sha512-mjh5oH2EA+wl5yRJYT9K9G61O2zFlpuv+yf2JwZOi0+dq2FnTUtm1h8i+5Ik0fXPWIu/k84I1psZR9aQsLAnyA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-freebsd-x64": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-freebsd-x64/-/binding-freebsd-x64-1.83.0.tgz", + "integrity": "sha512-fNHr64/YaO8YssuoDVC8+F4Uk5enR86q5uxfHkQrjAPs1dbAILOrD2uaud+J7MO8Fx774g44ERLD0IGIvZE48w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-gnueabihf": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.83.0.tgz", + "integrity": "sha512-Qpwy3zzAwMj+8/lyYItHmkSMwbkprFNWTK7jPYDOxSyxEhaSLOWYUTCMkjF334J8/WD0nznCCsoBbIH6hpsuIw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm-musleabihf": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-1.83.0.tgz", + "integrity": "sha512-s+BirYLFq7JL2k9sP0XI3ZXJ9dYvJ8sX3jLCLoag7tt+zrSHpZxP0jqznfL+Gdgwu7ay0dYgGYJXrQvq3iWloA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-gnu": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.83.0.tgz", + "integrity": "sha512-7lihXt3vKr+GIyapNbHrnFHm/biiW30le6Zv/DExbAFPF6YwCQXVFlONPFehxs0CpGO4CBfYPM9rdDT+XMoIlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-arm64-musl": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.83.0.tgz", + "integrity": "sha512-q63JalLYVkZiZvls1z3PPUnpmQluOMXp0khqQMznCeAPLGydfNY8JhvuA4WlK57JfrvikU8wB5lPVveqpIXvew==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-ppc64-gnu": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.83.0.tgz", + "integrity": "sha512-krQmDF+dRbxvdqVPV88ZuOoPPu8X5BuqDA8Hd+qcS4YMRQCb+nexA57DazgGsc/rGdKBe3QmV0mnv0bdpW/p5g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-gnu": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-1.83.0.tgz", + "integrity": "sha512-MmOl8Y6txEAXZU1RG8Rr264jQ6D7VPmqFsU/45x/FeWsGe32hklTqGrLE6UxHzp5Rjt0wP+20tY8YXKgSFB3mw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-riscv64-musl": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-1.83.0.tgz", + "integrity": "sha512-u1rMymh0W3JZkq370kzQsYPULGWqhE09pZRqnZvUSoYaI9pVO5yVX+iYIslmWuEgwuzH9YAaOsScJiobWCHoOw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-s390x-gnu": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.83.0.tgz", + "integrity": "sha512-y0zK3HNwGysu7rqtE+BQG/d0bx5gh/KwlOtghN8oWeK1KcWzeaLqtZrbm8owqdma1lFyrce/hTO5ismuNu+INQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-gnu": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.83.0.tgz", + "integrity": "sha512-rS5gM0NgD7ngmuJmbIehsidtrOwKkLFwCQbKEeb9KuyQrrWNq5Zkn0uV6AYdXOMJ0grrWEiLwBuvMxt8w5vsNw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-linux-x64-musl": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-linux-x64-musl/-/binding-linux-x64-musl-1.83.0.tgz", + "integrity": "sha512-W2IH4EtpcPaWcvNGCA95YoDg4vxqE/ZiPCi3arrxEEpsK7+JQN9WYwrlYFx9pcdP6KPXqRqkv3zdQPHcx7b6YQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-openharmony-arm64": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-openharmony-arm64/-/binding-openharmony-arm64-1.83.0.tgz", + "integrity": "sha512-6LyKkUyoajssTPLlZmDbZIbu4IZ5B4bGuRUnBgCGpEvHP3FQMaYITncHA/unPUo7q+Z+pIu2HhdkQ+8d1SG7iA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-arm64-msvc": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.83.0.tgz", + "integrity": "sha512-Uz/fObEtF0jmNJQJ8CGRBKfefYstS0/wjD3s6IGzP8nUwsJykHQJBiN3npHwKiGRGn/vvBEgNr4B3cCzmmatvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-ia32-msvc": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.83.0.tgz", + "integrity": "sha512-u7XcvPW6Bk58tY5iWs2ESb0vJjoE/kuSpHxopbwp/p3ZtWVQXZ6wor5w3ssVTHOqd/v8b+QdhSFWQ4grEUNWpA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxlint/binding-win32-x64-msvc": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/@oxlint/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.83.0.tgz", + "integrity": "sha512-LZRubd7ph13QmAg4fFecTYVZkiYbROR2Htaxh/ufWRkDhPOm2wrwaEYR89e0YpPFD3dqBrPoxS7myBw5hmYA7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@types/node": { "version": "24.13.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", @@ -390,6 +738,55 @@ "node": ">=12.17.0" } }, + "node_modules/oxlint": { + "version": "1.83.0", + "resolved": "https://registry.npmjs.org/oxlint/-/oxlint-1.83.0.tgz", + "integrity": "sha512-cyDzSzaw3uzP0TeCeq3lLRPPoaUxkbB4ZOXj+kn+5r+BX9V+4bNVGk9lxer+WrgcpebH4JxLlJ3KQjveVztOLQ==", + "dev": true, + "license": "MIT", + "bin": { + "oxlint": "bin/oxlint" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/oxc-project" + }, + "optionalDependencies": { + "@oxlint/binding-android-arm-eabi": "1.83.0", + "@oxlint/binding-android-arm64": "1.83.0", + "@oxlint/binding-darwin-arm64": "1.83.0", + "@oxlint/binding-darwin-x64": "1.83.0", + "@oxlint/binding-freebsd-x64": "1.83.0", + "@oxlint/binding-linux-arm-gnueabihf": "1.83.0", + "@oxlint/binding-linux-arm-musleabihf": "1.83.0", + "@oxlint/binding-linux-arm64-gnu": "1.83.0", + "@oxlint/binding-linux-arm64-musl": "1.83.0", + "@oxlint/binding-linux-ppc64-gnu": "1.83.0", + "@oxlint/binding-linux-riscv64-gnu": "1.83.0", + "@oxlint/binding-linux-riscv64-musl": "1.83.0", + "@oxlint/binding-linux-s390x-gnu": "1.83.0", + "@oxlint/binding-linux-x64-gnu": "1.83.0", + "@oxlint/binding-linux-x64-musl": "1.83.0", + "@oxlint/binding-openharmony-arm64": "1.83.0", + "@oxlint/binding-win32-arm64-msvc": "1.83.0", + "@oxlint/binding-win32-ia32-msvc": "1.83.0", + "@oxlint/binding-win32-x64-msvc": "1.83.0" + }, + "peerDependencies": { + "oxlint-tsgolint": ">=7.0.2001", + "vite-plus": "*" + }, + "peerDependenciesMeta": { + "oxlint-tsgolint": { + "optional": true + }, + "vite-plus": { + "optional": true + } + } + }, "node_modules/pure-rand": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz", diff --git a/package.json b/package.json index 0d696e2..cab9c6e 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ }, "scripts": { "build": "tsc -p tsconfig.build.json", + "size-ratchet": "oxlint --deny-warnings -c .oxlintrc.json src", "test": "node --test --experimental-test-coverage --test-coverage-exclude=\"src/**/*.test.ts\" --test-coverage-exclude=src/property-harness.ts --test-coverage-branches=98 --test-coverage-functions=100 --test-coverage-lines=100 \"src/**/*.test.ts\"", "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.build.json" }, "devDependencies": { "@types/node": "24.13.3", "fast-check": "4.10.0", + "oxlint": "1.83.0", "typescript": "7.0.2" } } diff --git a/todo-history.md b/todo-history.md index bdcf259..a82f1cb 100644 --- a/todo-history.md +++ b/todo-history.md @@ -856,6 +856,27 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li dropping the outer link silently as `closeLink`'s `applyMark` does today, with a normalization fixture per shape (the stability-reviewer, 2026-09-16; the maintainer, 2026-09-17). +- [x] **17 — A machine-enforced size ratchet (`0.2.0`).** Add a per-function line ceiling to the + gate, set at today's worst and only ever moving down, so the largest body of new code cannot + exceed what is already here (the systems-architect, 2026-09-16; narrowed by the comprehension + panel, 2026-09-20). oxlint's `eslint/max-lines-per-function` measures it — one devDependency, + carrying the musl binding the gate's image needs, since TypeScript 7 is the native compiler + and exposes no parser to write the check against. No cyclomatic rule: `eslint/complexity` + charges `?.` and `??` a point each, the guards §10 already exempts from the branch floor, and + its two worst functions, `readBlockLine` and `parseInline`, went unnamed by all nine readers + while `isNodeArray` and `blockNode` were volunteered as among the clearest code here. Length + ranks no better: `emitList` and `blockNode` are both 26 lines, one the panel's unanimous top + four and the other the clearest map of the format in the repo. So the ceiling guards against + drift and never drives a refactor — 19 to 27 are where the hard work actually is. + **Done** (2026-09-20): `.oxlintrc.json` carries the one rule, `correctness` off so nothing + else runs, over the 40 files `tsconfig.build.json` builds — the tests and + `property-harness.ts` out, five functions over the ceiling with them, the worst 64. The + ceiling is 52, `parseInline`'s length and the built set's worst; at 51 the gate reddens on + it. `skipBlankLines` and `skipComments` are spelled at oxlint 1.83.0's defaults, so a changed + default cannot move what 52 counts. The leg runs `npm run size-ratchet` beside the typecheck + at 0.8s, and the lockfile carries every platform binding, so `npm ci` resolves the musl one + inside the image. §10 holds the rule and what each switch guards (the stability-reviewer, + 2026-09-20). - [x] **18 — The subtree the directive spelling asks about (`0.2.0`).** The parser asks `commonMarkSpelling` at every directive-spelled block and the answer emits the whole subtree below, so a node at depth d is spelled d times: three nested rule-first directive lists cost diff --git a/todo.md b/todo.md index 740f8a6..c48462f 100644 --- a/todo.md +++ b/todo.md @@ -18,7 +18,8 @@ Start a session with: `Read AGENTS.md and todo.md, then do what todo.md's "Next ## Milestones Shipping order: 3h, 3i, 3j, 5a, 5b, 5c, 5d, 5 → `0.1.0` (shipped 2026-09-05); 3k, 11, 4, 12, 13, 4b, -4c, 14, 15, 16, 18, 4d, 28, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27, 10, 6, 7, 5f, 5g → `0.2.0`; +4c, 14, 15, 16, 18, 4d, 28, 17, 29, 19, 20, 21, 22, 23, 24, 25, 30, 26, 27, 10, 6, 7, 5f, 5g → +`0.2.0`; 8, 9 → TBD; 5e last. The numbering is the order the work was planned in, not the order it ships. Everything known and shaped ships in one release rather than a string of them: nothing waits on a version, and no @@ -36,7 +37,25 @@ head (2026-09-20). They read ahead of 6, 7 and 10 because every one of them is c HTML format lands than after: 19 and 20 because HTML has no answer without them, 21 to 24 because HTML doubles the importers and the file count they touch, and 25 to 27 because they are what the panel says the next reader pays for. +29 and 30 come from 17's prose pass (2026-09-20). 29 reads first because every goal is what a later +ask is settled against, 19's included; 30 sits beside 25, the other chunk rereading AGENTS.md. +- [ ] **29 — Goal 3 names the whole of the exception (`0.2.0`).** README §Goals 3 says "the three + carve-outs and the one gap below are the whole of the exception", and §The guarantees says + "apart from the raw HTML below, with three carve-outs … and one gap". Raw HTML is a fourth + exception, the commonest one in markdown written elsewhere, and `unmappable-html` refuses it + today. Name it in the goal, or stop enumerating there. Settle at the same time whether the + goal is written for `0.2.0`, where the element mapping the error text promises could make + raw HTML convert — the answer decides which repair is the true one, so it is the + maintainer's. +- [ ] **30 — AGENTS.md says each thing once (`0.2.0`).** §15's ask protocol — name the class, cite + the earlier asks of it, never "A or B?" — is the rule reviewers cite most and has no heading, + two thirds down a 50-line section in a file with no index. Give it one. §15 also offers "the + gate's seconds" as a stated number that is kept, and no such number is stated anywhere, §14 + forbidding the category outright; drop the example. Then the restatements: §15 repeats the + one-chunk rule three times and `version`/`NPM_TOKEN` twice, §12 says the default is delete + twice, and §5's "few, each earning its keep; they never reach a consumer" is npm's own + definition of the field. Cut to one copy each, the one carrying the why. - [ ] **19 — A home for what both formats read (`0.2.0`).** Settle where a construct both formats need lives, and say so in AGENTS.md §11. Today `adf/` may hold no format knowledge and each format directory holds its own shared layer, so there is no third place; the first ADF-shaped @@ -253,18 +272,7 @@ panel says the next reader pays for. - [x] **14 — The CommonMark subset's directory (`0.2.0`).** - [x] **15 — The href-less directive link (`0.2.0`).** - [x] **16 — The link wrapping a link (`0.2.0`).** -- [ ] **17 — A machine-enforced size ratchet (`0.2.0`).** Add a per-function line ceiling to the - gate, set at today's worst and only ever moving down, so the largest body of new code cannot - exceed what is already here (the systems-architect, 2026-09-16; narrowed by the comprehension - panel, 2026-09-20). oxlint's `eslint/max-lines-per-function` measures it — one devDependency, - carrying the musl binding the gate's image needs, since TypeScript 7 is the native compiler - and exposes no parser to write the check against. No cyclomatic rule: `eslint/complexity` - charges `?.` and `??` a point each, the guards §10 already exempts from the branch floor, and - its two worst functions, `readBlockLine` and `parseInline`, went unnamed by all nine readers - while `isNodeArray` and `blockNode` were volunteered as among the clearest code here. Length - ranks no better: `emitList` and `blockNode` are both 26 lines, one the panel's unanimous top - four and the other the clearest map of the format in the repo. So the ceiling guards against - drift and never drives a refactor — 19 to 27 are where the hard work actually is. +- [x] **17 — A machine-enforced size ratchet (`0.2.0`).** - [x] **18 — The subtree the directive spelling asks about (`0.2.0`).** - [x] **28 — `emitLine`'s retry loop cannot spin (`0.2.0`).**