mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 13:18:58 +00:00
Two-sided ratchets with zero slack made parallel tasks conflict on shared numbers, recompute them after every rebase and hit a ceiling because a neighbour merged first (#689 after #691). - Core lines (test/core-file-budget.test.mjs): a branch may grow up to CORE_BAND = 50 lines over the beta ceiling; shrinking no longer fails it. - Bundle graphs (bundle-budget.mjs): initial View and lazy graphs fail only above ceiling + 2 000 B; below the ceiling is not a branch finding. The absolute INITIAL_VIEW_GZIP_BUDGET stays the wall. - Monolith numbers (monolith-metrics.mjs, unused-locals-gate.mjs): METRIC_BANDS — 5 for delegates, port members and privates, 25 for host. refs, 2 000 B for dist/; a lower number is reported, not failed. - Browser mutation guards: 200 is a guideline — mutation-gate --check warns above it instead of failing; every guard still needs its reason line. - scripts/ratchets.mjs: `report [--warn]` and `tighten` — on the beta candidate the release manager sets every ceiling to the fact in one commit; release:prerelease prints loose ceilings as a warning. Canon: PROCESS.md §3 (browser guards, monolith numbers) and §8 «Храповики»; docs/TESTING.md. Issue: #699 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
68 lines
4.3 KiB
JavaScript
68 lines
4.3 KiB
JavaScript
// #699, PROCESS.md §8: полоса над потолком беты вместо точки; потолки
|
|
// опускает до факта релиз-менеджер одной командой на кандидате.
|
|
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import {
|
|
BUNDLE_CEILINGS, CORE_BAND, CORE_BUDGET_FILE, BUNDLE_BUDGET_FILE, formatRow, ratchetRows, ratchetState,
|
|
readConst, readCoreCaps, rewriteConst, rewriteCoreCaps,
|
|
} from '../scripts/ratchets.mjs';
|
|
import { METRIC_NAMES } from '../scripts/monolith-metrics.mjs';
|
|
|
|
const read = (path) => readFileSync(fileURLToPath(new URL(`../${path}`, import.meta.url)), 'utf8');
|
|
|
|
test('#699 состояние храповика: равен, рыхлый, в полосе, выше полосы, не измерен', () => {
|
|
assert.equal(ratchetState(100, 100, 50), 'tight');
|
|
assert.equal(ratchetState(90, 100, 50), 'loose');
|
|
assert.equal(ratchetState(150, 100, 50), 'over', 'граница полосы включительная — как у гейтов ветки');
|
|
assert.equal(ratchetState(151, 100, 50), 'grew');
|
|
assert.equal(ratchetState(undefined, 100, 50), 'missing');
|
|
assert.equal(ratchetState(100, null, 50), 'missing');
|
|
});
|
|
|
|
test('#699 потолки ядер читаются и переписываются в исходнике теста, комментарии на месте', () => {
|
|
const source = read(CORE_BUDGET_FILE);
|
|
const caps = readCoreCaps(source);
|
|
// Имена ядер здесь не пишутся: тесты, читающие монолит как текст, заморожены (#624).
|
|
const [first, second] = Object.keys(caps);
|
|
assert.equal(Object.keys(caps).length, 2, 'два ядра');
|
|
const next = rewriteCoreCaps(source, { [first]: 12000 });
|
|
assert.equal(readCoreCaps(next)[first], 12000);
|
|
assert.equal(readCoreCaps(next)[second], caps[second]);
|
|
assert.equal(next.split('\n').length, source.split('\n').length, 'меняется только число');
|
|
assert.match(next, /2026-09-27, #676/, 'история решений остаётся');
|
|
assert.match(source, new RegExp(`^export const CORE_BAND = ${CORE_BAND};$`, 'm'), 'полоса теста и инструмента — одно число');
|
|
});
|
|
|
|
test('#699 потолки графов читаются и переписываются с разрядами', () => {
|
|
const source = read(BUNDLE_BUDGET_FILE);
|
|
for (const entry of BUNDLE_CEILINGS) assert.ok(readConst(source, entry.name) > 0, entry.name);
|
|
const next = rewriteConst(source, 'INITIAL_VIEW_GZIP_CEILING', 300323);
|
|
assert.equal(readConst(next, 'INITIAL_VIEW_GZIP_CEILING'), 300323);
|
|
assert.match(next, /^export const INITIAL_VIEW_GZIP_CEILING = 300_323;$/m);
|
|
assert.throws(() => readConst(source, 'NO_SUCH_CEILING'), /нет export const NO_SUCH_CEILING/);
|
|
});
|
|
|
|
test('#699 отчёт видит все три вида храповиков и называет, что делать', () => {
|
|
const rows = ratchetRows({
|
|
coreCaps: { 'a.ts': 100 }, coreFacts: { 'a.ts': 90 },
|
|
bundleCeilings: Object.fromEntries(BUNDLE_CEILINGS.map((e) => [e.name, 1000])),
|
|
bundleFacts: Object.fromEntries(BUNDLE_CEILINGS.map((e) => [e.metric, 1000])),
|
|
metrics: Object.fromEntries(METRIC_NAMES.map((n) => [n, 10])),
|
|
baseline: Object.fromEntries(METRIC_NAMES.map((n) => [n, n === 'hostRefs' ? 5 : 10])),
|
|
});
|
|
assert.equal(rows.length, 1 + BUNDLE_CEILINGS.length + METRIC_NAMES.length);
|
|
assert.equal(rows.find((r) => r.kind === 'core').state, 'loose');
|
|
assert.ok(rows.filter((r) => r.kind === 'bundle').every((r) => r.state === 'tight'));
|
|
assert.equal(rows.find((r) => r.name === 'hostRefs').state, 'over');
|
|
assert.match(formatRow(rows.find((r) => r.kind === 'core')), /факт 90 · потолок 100 \(-10, полоса \+50\) — рыхлый/);
|
|
});
|
|
|
|
test('#699 публикация беты напоминает о храповиках, но не останавливается на них', () => {
|
|
const local = read('scripts/release-prerelease.mjs');
|
|
const main = local.slice(local.indexOf('const main = async'));
|
|
assert.match(main, /'scripts\/ratchets\.mjs', 'report', '--warn'/);
|
|
assert.match(main, /'scripts\/ratchets\.mjs', 'report', '--warn'\], \{ allowFailure: true, inherit: true \}\)/);
|
|
});
|