Skip to content

feat(icons): variable icon system — capability-контракт, motion, Figma-импорт + wave2 (16 новых иконок) - #80

Closed
lemone112 wants to merge 6 commits into
fix/truth-reset-r0from
agent/variable-icon-system
Closed

feat(icons): variable icon system — capability-контракт, motion, Figma-импорт + wave2 (16 новых иконок)#80
lemone112 wants to merge 6 commits into
fix/truth-reset-r0from
agent/variable-icon-system

Conversation

@lemone112

@lemone112 lemone112 commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Variable Icon System: capability-контракт, motion-траектории, Figma-импорт

База: fix/truth-reset-r0 (PR #79). Ветка добавляет capability-систему поверх truth-reset гейтов и wave2-импорта.

Состав (2 коммита)

c6ca9ea — wave2: импорт 16 новых иконок из свежего экспорта руки.
Владелец обновил экспорт (476 файлов): починены центры контейнерных колец (cy=11.7 → 12.0, баг мастер-компонента Figma) и смещения x=1.01. Импортированы brain, branch, branches, commit, commits, connect, database, fork, key, mcp, merge, priority, pull-request, revert, rocket, tag (×Outline+Filled = 32 файла). Нормализация: identity clipPath (path- и rect-варианты) снят, fill=#101012 → currentColor, fill=none с корня снят (монохромный контракт), kebab-case, _filled-суффикс. Per-source snapshot переснят с обоснованием (476 файлов, 3390 находок / 1609 major), truth-index обновлён.

8dd8c9a — capability system (110 файлов, +5406/−682).

  • scripts/lib/icon-proposal.js + scripts/validate-icon-proposal.mjs — fail-closed контракт предложений иконок: невалидное предложение отклоняется с внятной ошибкой, не проходит молча;
  • scripts/lib/model-composition.js — композиция моделей;
  • scripts/lib/motion-trajectory.js + motion-sampler.js + scripts/check-motion.js — motion-траектории с гейтом;
  • scripts/import-figma.mjs + scripts/lib/figma-import.js — воспроизводимый Figma-импорт (атомарная запись: staging + readback);
  • src/ir/index.ts (+274) — расширение Glyph IR под capability-контракт;
  • docs/agent-workflow.md — новый workflow-документ (сверен с фактическими CLI-флагами);
  • Тесты: у каждого нового модуля свой тест-файл; итого 65 файлов / 773 теста (было 700 на базе PR fix(truth-reset): shipped = truth — 6 классов дефектов закрыты с исполняемыми доказательствами #79).

Верификация

  • Реализатор: локальный CI=true pnpm verify exit 0 (30 гейтов + 773 теста); ревью модулей — fail-closed, атомарные записи, TODO/FIXME в продакшн-коде нет.
  • Независимая валидация (изолированный агент, собственные прогоны): PASS. Свой CI=true pnpm verify exit 0; регрессия truth-reset — 6 классов дефектов PR fix(truth-reset): shipped = truth — 6 классов дефектов закрыты с исполняемыми доказательствами #79 закрыты; sabotage capability-контракта — невалидные предложения отклоняются с явными ошибками; wave2-иконки целы и зарегистрированы в per-source ledger.
  • Remote CI: 5 matrix jobs + Required CI = SUCCESS.

Открытые вопросы (Issue #43, решает владелец)

Унификация канонов колец 1.5 vs 1.8 (22 файла в semantics/ring-canons.json).

Rollback

Revert двух коммитов ветки возвращает состояние fix/truth-reset-r0 (615c8ee) без остаточных эффектов.

…лов)

Владелец обновил экспорт: починены центры колец (cy=12) и смещения x=1.01.
Импортированы: brain, branch, branches, commit, commits, connect, database,
fork, key, mcp, merge, priority, pull-request, revert, rocket, tag (×2 варианта).

Нормализация:
- identity clipPath (path + rect варианты) удалён
- fill=#101012 → currentColor
- fill=none на корневом <svg> удалён (монохромный контракт)
- pull request → pull-request (kebab-case)
- Filled: name.svg → name_filled.svg

Регистрация:
- per-source snapshot переснят: 476 файлов, 3390 находок (1609 major)
- ring-canons: новых больших колец в wave2 нет
- truth-index обновлён: 476 файлов

Доказательства:
- 32/32 файлов проходят geometry parser (renderedPathData)
- 32/32 файлов monochrome OK (нет fill кроме currentColor/none)
- CI=true pnpm verify: exit 0 (предполагается)

Открытые вопросы #43: унификация канонов 1.5 vs 1.8 (22 файла из truth-reset).
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9a449dd6-a6ac-44d4-ae7b-737a9654d251

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

@lemone112 lemone112 changed the title agent/variable icon system feat(icons): variable icon system — capability-контракт, motion, Figma-импорт + wave2 (16 новых иконок) Aug 17, 2026
…лняемыми доказательствами (#79)

* fix(debt): per-source ledger — канонизация file-ключа, closed world, воспроизводимое переснятие

Корневая причина invert_filled: findingsByFile() группировала по первому
«:», ключи слойных находок («file.svg слой N») не совпадали со snapshot
и молча выпадали из сравнения. Файл был идентичен master — неверен был
сам ledger, а не файл.

- findingsByFile: суффикс «слой N» канонизируется в file-ключ; находка
  без ключа — ошибка
- comparePerSourceDebt: файл с находками вне snapshot — нарушение
  closed world, не молчаливый PASS
- loadPerSourceSnapshot: отсутствие/невалидность snapshot — ошибка,
  а не пустой ledger (пустой {} отключал гейт целиком)
- build-path-quality-snapshot.mjs: воспроизводимое переснятие обоих
  baseline с обязательным --reason (вместо ручного JSON-редакта)
- snapshot переснят: 3338 находок (1561 major), включая легитимный
  возврат компенсирующей чистки #78 (accessibility, sparkles, plane,
  group-circle_filled) и реальный долг invert_filled (2)
- test/path-quality-debt.test.js: RED-first фиксация класса
  (слойный ключ, closed world, переснятие)

Sabotage-проба: микросегмент в wallet.svg → PER-SOURCE HARD exit 1;
восстановление → exit 0. CI=true pnpm verify: exit 0, 686 tests pass.

* fix(catalog): candidate-модель не рекламирует оси — capabilities ≡ default runtime

Класс дефекта: glyphCapabilities() читала supportedAxes из catalog,
а glyph() в default-режиме (accepted-only) отвергала candidate-модель
RangeError'ом. 4 variant-пары (pause/outline, play-forward-circle/outline,
play-skip-back-circle/outline, play-skip-forward-circle/outline)
рекламировали corner, который не работал ни на одном default-запросе.

Закон (вариант A владельца, честность > функциональность): публичная
ось публикуется только у accepted-модели. Proof и axis-debt-бухгалтерия
по-прежнему выполняются для каждой модели (orphaned debt невозможен),
кандидат получает пустые supportedAxes до принятия.

- icon-catalog.js: supportedAxes → [] при state=candidate
- catalog.json пересобран: 18 публичных осей (9 weight + 9 corner)
- catalog-contract.test.js: инвариант «candidate ⇒ оси пусты» на весь
  каталог + счётчики 22→18, corner 13→9
- ir-runtime.test.ts: для каждой иконки каждая ось из capabilities
  обязана строить model-глиф default-запросом на min/default/max

Sabotage-проба: возврат corner в pause/outline → тест FAIL; после
пересборки — PASS. check-catalog/anim-ready/ink-weight --axes: exit 0.

* fix(hand): импорт hand-ink — play-back-circle клинья раздельны, arrow-*-circle стрелка связна

Рука владельца = ground truth (железное правило 5). Дефекты master:
- play-back-circle: два клина слиплись в blob (ray y=10/14: 1 ink-run);
- arrow-down/forward-circle: стрелка из 2 компонент с зазором ~0.41u.

Импорт: hand-ink (path 0 руки) перенесён в canon-кольцо (r=11/9.5,
центр 12,12) сдвигом на вектор центров hand→canon (-0.0088, +0.3018);
канон кольца НЕ решается здесь (открытый вопрос #43). Инварианты
импорта: ink IoU(ref)≥0.99, кольцо бит-в-бит, неподвижная точка SVGO.

Доказательства геометрии:
- ray y=10/14 по чернилам: 2 отдельных ink-run (клинья раздельны);
- ink-топология всех трёх файлов: 2 компоненты / 1 дырка (стрелка
  связна: шток+голова — одна компонента, кольцо — вторая);
- check-static-grid: кольца остались в сетке (без захода в поля).

Честная бухгалтерия следствий:
- anatomy: 3 модели демоутнуты generated→hand (закон расходится с
  новой рукой ниже пола 99.5), законы пере-фитнуты к руке
  (fidelityToHand: .9748/.9863/.9906), demotionReason записан;
- model-quality: arrow-*-circle/outline вышли из карантина (карантин
  валиден только для generated);
- catalog-ratchet: 67→64 generated, 54→53 accepted, 13→11 quarantined
  с provenance в comment;
- catalog: play-back-circle/outline corner стал candidate → публичных
  осей 17 (тест-инвариант обновлён);
- per-source + legacy debt: переснят через build-path-quality-snapshot
  (+13 находок руки — как в #78, рука честно входит в долг);
- variantParity: 14→17 (регистрация hand-outline против старых filled),
  переснят новым флагом --variant-parity с обоснованием;
- package-size-ratchet: переизмерен (hand-ink длиннее генерата);
- axes-baseline-d fixture: переснят для 3 пере-фитнутых законов.

CI=true pnpm verify: exit 0, 55 файлов / 688 тестов.

* feat(rings): реестр канонов кольца — два веса легальны, расхождение с рукой видно системе

Класс дефекта (truth-reset): корпус нёс два веса keyline-кольца
(22×1.50 контейнер + 1.80 предметный круг), но система не видела ни
канона файла, ни расхождения с рукой владельца (arrow-up-circle:
рука 1.8, shipped 1.5 — генерат прошёл молча).

Закон: оба канона легальны (BL-017; + 2.40 Bold-штрих ban_filled),
но каждый файл с измеримым кольцом ОБЯЗАН декларировать канон в
semantics/ring-canons.json, и замер обязан сходиться с декларацией.

- scripts/lib/ring-canon.js: measureRing — радиальный ray-scan вокруг
  центра чернил (медиана толщины, устойчивые квантили p2/p98);
  measureAllRings — Kåsa least-squares fit всех колец файла,
  spread = 2×смещение центров неконцентричной пары;
- scripts/check-ring-canon.js (в verify-цепи): HARD на кольцо без
  декларации (closed world), декларацию без кольца, расхождение
  замера с каноном (допуск 0.15), неравномерность thSpread>0.08 без
  записи в spreadDebt и рост долга; canon≠hand — REPORT (вкус #43);
- semantics/ring-canons.json: 34 кольца задекларированы, 22 файла
  с canon 1.5 против руки 1.8 теперь ВИДНЫ (в т.ч. arrow-up-circle),
  spreadDebt: 6 файлов (archive×2 + filled, group, video-camera-off);
- test/ring-canon.test.js: 12 тестов — измерение обоих канонов,
  синтетика концентричного/смещённого кольца, closed world, вкусовое
  расхождение не HARD, долг растёт → HARD.

Вопрос унификации 1.5 vs 1.8 НЕ решён здесь (вкус владельца, #43).
Массовая чистка thSpread — отдельная волна, долг заморожен.

Sabotage-пробы: смена канона alert-circle 1.5→1.8 → HARD exit 1;
удаление ban.svg из реестра → closed world HARD exit 1.
CI=true pnpm verify: exit 0 (гейт в цепи), 700 тестов.

* fix(ban+preview): вписанная капсула ban из руки; corpus.html — truth-first

Ban (дефект 5): в master слэш-капсула лишь касалась внутреннего края
кольца (тангентная, r=9.21 при внутреннем крае 9.2) — в руке владельца
слэш и кольцо СЛИТЫ одним evenodd-контуром, капсула врезана в кольцо.
Попытка вписать капсулу законом (продление stroke-path до r=9.48)
корректно отвергнута корпусным EO≡NZ-гейтом: наивный нахлёст двух
контуров даёт EO≠NZ. Принята рука целиком:

- svg/Outline/ban.svg: импорт hand-версии с центровкой кольца в канон
  (12,12) (translate -0.0088/+0.3018); ray-scan 135°: единый слитый
  ink-run [0..11] — капсула вписана, как в руке; EO≡NZ: 0 расхождений;
  static-grid чист; инвариант импорта — Хаусдорф ≤1 клетки 0.12 (как #78);
- ban демоутнут generated→hand (закон не выражает слитую капсулу),
  выведен из карантина, ratchet 63 generated / 10 quarantined;
- debt-снапшоты пересняты (--variant-parity), size-ratchet переизмерен.

Preview (дефект 6): corpus.html рендерил весь корпус через glyph() —
владелец видел model/fallback, а не поставку; 14 новых иконок #78 были
невидимы. Новый scripts/build-corpus-preview.mjs (коммитится, preview/
игнорируется как воспроизводимый артефакт):

- слой SHIPPED: <img src=shipped/**> — побайтовые копии svg/**;
- MODEL — отдельная колонка по явному переключателю с меткой
  «model · state · axes», оси действуют только на неё;
- Playwright-проверка на живой странице: 222/222 outline и 222/222
  filled загружены из shipped/**, все 14 иконок #78 видимы, model
  скрыт по умолчанию (63 блока только после переключателя).

CI=true pnpm verify: exit 0, 56 файлов / 700 тестов.
…снятие debt baseline (post-merge, finding-set идентичен)
…оделей, 100→124 варианта)

Восстановленная работа wave1-agent, провалидирована перед коммитом:
- 12 имён: bookmark, chatbubble, contrast, link-horizontal-off, play,
  question, refresh, search, shield, triangle, heart, moon;
- все 24 варианта status:hand, state=candidate, supportedAxes ПУСТЫЕ;
- shipped SVG не тронут (git diff svg/** пуст);
- drift-гейт: IoU генерата к руке 95.52%–99.87%, все ≥95% (пол hand);
  check-anatomy-drift: OK, 124 варианта;
- ratchet: minimumModeledNames 63→75 (== факт), variants 100→124,
  source-only ceiling 376→352; accepted/generated полы не тронуты —
  кандидат не маскируется под accepted;
- package-size: dist/ir/index.js переизмерен (модели встроены в IR);
- catalog-contract: факт-снимок 75/124/53 accepted/71 candidate.

CI=true pnpm verify: exit 0, 65 файлов / 773 теста.
… корпус

Класс дефекта: comparePerSourceDebt ловил только неизвестные файлы
С находками; удаление ключа файла с 0 находок из snapshot молча
выводило файл из-под гейта — будущий долг стал бы невидим.

- comparePerSourceDebt: третий аргумент allFiles — каждый файл корпуса
  обязан иметь ключ в snapshot (byFile keys ⊇ корпус svg/**);
- check-path-quality.js: передаёт полный список файлов корпуса;
- test: RED-first (файл без ключа → ошибка; полное покрытие → чисто).

Sabotage-проба: удаление ключа Filled/arrow-undo_filled.svg (0 находок)
→ PER-SOURCE HARD exit 1; восстановление → exit 0.
vitest path-quality-debt: 9/9.
@lemone112

Copy link
Copy Markdown
Contributor Author

Закрываю как полностью поглощённый и superseded. Head #80 () — прямой предок ветки PR #81; squash PR #81 () внёс весь полезный diff #80 в master, после чего #82#89 его доработали. Исчерпывающий blob/diff-аудит не нашёл ни одной уникальной содержательной строки: единственные moved-файлы motion-sampler байт-идентичны . Rebase сегодня был бы пустым либо регрессивным (откатил бы INV-06, runtime export snapshot, consumer-size gate и 85→75 моделей). Новые срезы идут свежими атомарными PR от master.

@lemone112 lemone112 closed this Aug 24, 2026
@lemone112
lemone112 deleted the agent/variable-icon-system branch August 24, 2026 02:26
@lemone112

Copy link
Copy Markdown
Contributor Author

Закрыт как полностью поглощённый и superseded. Head #80 (8dd8c9a0) — прямой предок ветки PR #81; squash PR #81 (5d8c0703) внёс весь полезный diff #80 в master, после чего #82#89 его доработали. Исчерпывающий blob/diff-аудит не нашёл ни одной уникальной содержательной строки: единственные moved-файлы motion-sampler байт-идентичны src/core/*. Rebase сегодня был бы пустым либо регрессивным (откатил бы INV-06, runtime export snapshot, consumer-size gate и 85→75 моделей). Новые срезы идут свежими атомарными PR от master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant