From 6efbac45983e7249c3b6ad90ce93f1e3df891b98 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 20 Sep 2026 18:10:21 +0200 Subject: [PATCH 1/4] 17 - the gate holds every built function to 52 lines --- .oxlintrc.json | 8 + AGENTS.md | 10 +- ci.sh | 1 + package-lock.json | 397 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 2 + todo-history.md | 18 +++ todo.md | 13 +- 7 files changed, 436 insertions(+), 13 deletions(-) create mode 100644 .oxlintrc.json diff --git a/.oxlintrc.json b/.oxlintrc.json new file mode 100644 index 0000000..7fedf96 --- /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", { "max": 52 }] + } +} diff --git a/AGENTS.md b/AGENTS.md index 64645e0..f83610b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -78,7 +78,8 @@ 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 and exposes no parser to write that check against. ## 6. The package contract @@ -254,6 +255,13 @@ 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: no function past 52 lines, today's worst (`parseInline`), moving only +downward. 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), and `eslint/complexity` charges a point for each `?.` and `??` the branch floor +above already exempts. + 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..be196c5 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,14 @@ }, "scripts": { "build": "tsc -p tsconfig.build.json", + "size-ratchet": "oxlint 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..28ee8f6 100644 --- a/todo-history.md +++ b/todo-history.md @@ -856,6 +856,24 @@ 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, every category and plugin off, + over the 40 files `tsconfig.build.json` builds — the tests and `property-harness.ts` out, as + they are out of the coverage floors, the harness's own 59-line function with them. The + ceiling is 52, `parseInline`'s length and the repo's worst; at 51 the gate reddens on it. + `npm run size-ratchet` is the leg, beside the typecheck at 0.8s, and the lockfile carries + every platform binding, so `npm ci` resolves the musl one inside the image. - [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..4fc268c 100644 --- a/todo.md +++ b/todo.md @@ -253,18 +253,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`).** -- 2.52.0 From b969bb36d69a2aea5004d9dbf34ded4d2d808743 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 20 Sep 2026 18:20:10 +0200 Subject: [PATCH 2/4] Review: the ratchet counts an IIFE, and no missing config or stray warning passes it --- .oxlintrc.json | 2 +- AGENTS.md | 13 ++++++++----- package.json | 2 +- todo-history.md | 13 ++++++++----- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/.oxlintrc.json b/.oxlintrc.json index 7fedf96..36c12b6 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -3,6 +3,6 @@ "categories": { "correctness": "off" }, "ignorePatterns": ["src/**/*.test.ts", "src/property-harness.ts"], "rules": { - "eslint/max-lines-per-function": ["error", { "max": 52 }] + "eslint/max-lines-per-function": ["error", { "IIFEs": true, "max": 52, "skipBlankLines": false, "skipComments": false }] } } diff --git a/AGENTS.md b/AGENTS.md index f83610b..6fe017b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -79,7 +79,8 @@ dependency — CommonJS-only, some fifty packages with React among them, and a r 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, `oxlint` measuring §10's -size ratchet — TypeScript 7 is a native compiler and exposes no parser to write that check against. +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 @@ -257,10 +258,12 @@ 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: no function past 52 lines, today's worst (`parseInline`), moving only -downward. 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), and `eslint/complexity` charges a point for each `?.` and `??` the branch floor -above already exempts. +downward. It stops where the package does, because one ceiling covering the tests and the harness +too would have to be their worst — 64 — which loosens 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). The +leg passes `--deny-warnings` and an explicit `-c`, since oxlint exits 0 on a warning and, finding +no config, falls back to 96 default rules — either way green with the ceiling gone. 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/package.json b/package.json index be196c5..cab9c6e 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "scripts": { "build": "tsc -p tsconfig.build.json", - "size-ratchet": "oxlint src", + "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" }, diff --git a/todo-history.md b/todo-history.md index 28ee8f6..5ddaa64 100644 --- a/todo-history.md +++ b/todo-history.md @@ -868,11 +868,14 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li 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, every category and plugin off, - over the 40 files `tsconfig.build.json` builds — the tests and `property-harness.ts` out, as - they are out of the coverage floors, the harness's own 59-line function with them. The - ceiling is 52, `parseInline`'s length and the repo's worst; at 51 the gate reddens on it. - `npm run size-ratchet` is the leg, beside the typecheck at 0.8s, and the lockfile carries + **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. The leg runs `oxlint --deny-warnings -c .oxlintrc.json src` beside the typecheck at 0.8s, + the two flags because oxlint exits 0 on a warning and, discovering no config, falls back to + 96 default rules; `IIFEs`, `skipBlankLines` and `skipComments` are spelled so a changed + default cannot move what 52 counts (the stability-reviewer, 2026-09-20). The lockfile carries every platform binding, so `npm ci` resolves the musl one inside the image. - [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 -- 2.52.0 From f79241f56f905e0c93c66cc3165be4acadf2db6d Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 20 Sep 2026 18:23:46 +0200 Subject: [PATCH 3/4] Review: name what each ratchet switch guards, IIFEs included --- AGENTS.md | 8 +++++--- todo-history.md | 7 ++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 6fe017b..2acbc3c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -261,9 +261,11 @@ The size ratchet is the other such number, `.oxlintrc.json`'s single rule over t downward. It stops where the package does, because one ceiling covering the tests and the harness too would have to be their worst — 64 — which loosens 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). The -leg passes `--deny-warnings` and an explicit `-c`, since oxlint exits 0 on a warning and, finding -no config, falls back to 96 default rules — either way green with the ceiling gone. +neither measure picked out what nine readers found hard (the comprehension panel, 2026-09-20). +Three switches are load-bearing: `IIFEs: true`, since oxlint exempts an IIFE otherwise; an explicit +`-c`, since finding no config it falls back to 96 default rules, green with the ceiling gone; and +`--deny-warnings`, since a rule from a category the 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/todo-history.md b/todo-history.md index 5ddaa64..911e084 100644 --- a/todo-history.md +++ b/todo-history.md @@ -874,9 +874,10 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li ceiling is 52, `parseInline`'s length and the built set's worst; at 51 the gate reddens on it. The leg runs `oxlint --deny-warnings -c .oxlintrc.json src` beside the typecheck at 0.8s, the two flags because oxlint exits 0 on a warning and, discovering no config, falls back to - 96 default rules; `IIFEs`, `skipBlankLines` and `skipComments` are spelled so a changed - default cannot move what 52 counts (the stability-reviewer, 2026-09-20). The lockfile carries - every platform binding, so `npm ci` resolves the musl one inside the image. + 96 default rules; `skipBlankLines` and `skipComments` are spelled so a changed default cannot + move what 52 counts, and `IIFEs` because oxlint exempts an IIFE by default (the + stability-reviewer, 2026-09-20). The lockfile carries every platform binding, so `npm ci` + resolves the musl one inside the image. - [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 -- 2.52.0 From 77ae596d2e7f2eb00124555932586738fe50d03a Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 20 Sep 2026 18:29:39 +0200 Subject: [PATCH 4/4] Prose pass: the config is discovered, so -c guards only its absence --- AGENTS.md | 17 ++++++++--------- todo-history.md | 11 +++++------ todo.md | 21 ++++++++++++++++++++- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 2acbc3c..faf8c17 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -257,15 +257,14 @@ functions, and a branch floor that only ever moves upward. It sits below 100 bec 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: no function past 52 lines, today's worst (`parseInline`), moving only -downward. It stops where the package does, because one ceiling covering the tests and the harness -too would have to be their worst — 64 — which loosens 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 are load-bearing: `IIFEs: true`, since oxlint exempts an IIFE otherwise; an explicit -`-c`, since finding no config it falls back to 96 default rules, green with the ceiling gone; and -`--deny-warnings`, since a rule from a category the config never names arrives as a warning it -exits 0 on. +`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/todo-history.md b/todo-history.md index 911e084..a82f1cb 100644 --- a/todo-history.md +++ b/todo-history.md @@ -872,12 +872,11 @@ The done `todo.md` items in full, as they were written. `todo.md` keeps a one-li 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. The leg runs `oxlint --deny-warnings -c .oxlintrc.json src` beside the typecheck at 0.8s, - the two flags because oxlint exits 0 on a warning and, discovering no config, falls back to - 96 default rules; `skipBlankLines` and `skipComments` are spelled so a changed default cannot - move what 52 counts, and `IIFEs` because oxlint exempts an IIFE by default (the - stability-reviewer, 2026-09-20). The lockfile carries every platform binding, so `npm ci` - resolves the musl one inside the image. + 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 4fc268c..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 -- 2.52.0