Commit Graph

501 Commits

Author SHA1 Message Date
Дмитрий 0b43563dca feat(project-graph): слоистый граф районов + staleness O16 (sub-plan B Tasks 1-8 + SE7/SE8/SE11/V-3 + sharp-edges B1/B3) 2026-06-11 15:52:12 +03:00
Дмитрий a7a58b055f fix(context-verity): MIN_ANCHOR_LENGTH=4 порог anchor (SE-A2) + коллатераль тестов 2026-06-11 15:07:14 +03:00
Дмитрий 37842c6d04 feat(context-verity): проверенный контекст — parseRef/resolveCitation/verifyArtifact/guard O2 (sub-plan A Tasks 1-6 + amendments SE6/VF-1/SE9 + ревью SE-A1/SE-A3) 2026-06-11 14:31:02 +03:00
Дмитрий 04cae7bc04 feat(escape-sign): reader key-gated verify floor_escape (M6 FIX-5 Task 3)
loadFloorEscapes (единственный ВЫДАЮЩИЙ читатель floor_escape) теперь key-gated
проверяет подпись: ключ есть → оставить только валидно-подписанные (форж/
неподписанный/битый отброшены); нет ключа (truthy) → принять все (как сегодня,
content-floor backstop).

- Рефактор: generic loadRecords → floor_escape-специфичный readFloorEscapeRecordsAt,
  возвращает ПОЛНЫЕ записи {type,action,ts,sig} (не stripped) для верификации.
  Единственный вызыватель — loadFloorEscapes (loadConsumed читает другой файл).
- loadFloorEscapes(sessionId, now, {keyImpl, fsImpl, runtimeDir}) — 3-й опц.
  аргумент, обратно-совместим (8 потребителей зовут loadFloorEscapes(sess)).
- ОТКЛОНЕНИЕ от дословного кода плана (одобрено владельцем): быстрый путь —
  пропусков нет → [] БЕЗ резолва ключа. Поведение §3/§6 идентично, но keychain-
  subprocess не дёргается на каждый tool-use в массовом пустом случае
  (loadFloorEscapes — gate hot-path; 5 из 8 потребителей keychain не читали).

TDD: 6 новых тестов — 4 key-gated (signed принят / forged+tampered отброшены /
ключ null → все / '' falsy → все / окно 5 мин), 2 на быстрый путь (нет записей /
нет файла → [] без вызова ключа). Регрессия существующего escape-grant GREEN
(26 тестов). Суммарно 32 GREEN по затронутым файлам.

План: docs/superpowers/plans/2026-06-10-floor-escape-signing.md (Task 3)
Прод-код инертен до провижининга ключа (Фаза 8). Гейт закрытия — следующим шагом.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 05:28:40 +03:00
Дмитрий dc7f6dd979 feat(escape-sign): writer подписывает floor_escape при наличии ключа (M6 FIX-5 Task 2)
processEvent (PostToolUse AskUser) теперь подписывает floor_escape-пропуск
подписью FLOOR_ESCAPE, когда ключ доступен (resolveReceiptKey):

- +keyImpl=resolveReceiptKey, fsImpl={appendFileSync,mkdirSync} — инъекция для
  hermetic-тестов; резолв ключа один раз на событие (fail-safe: ошибка → key=null
  → floor_escape пишется неподписанным, PostToolUse-наблюдаемость не ломается).
- esc подписывается только при наличии ключа; approve_git_operation (rec) НЕ
  трогаем (§2.2). Нет ключа → esc без sig (как сегодня).
- Запись через fsImpl.* вместо прямых node:fs.

TDD: 2 новых теста (ключ → валидная подпись; ключ null → без sig). Регрессия
существующего enforce-askuser-answer-parser GREEN (approve_git_operation-путь цел).
Суммарно 10 GREEN по затронутым файлам.

План: docs/superpowers/plans/2026-06-10-floor-escape-signing.md (Task 2)
Прод-код инертен до провижининга ключа (Фаза 8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 05:16:15 +03:00
Дмитрий 46f0aedf4e feat(escape-sign): домен FLOOR_ESCAPE + sign/verify helpers (M6 FIX-5 Task 1)
Defense-in-depth для escape-гранта: подпись пропуска floor_escape, чтобы форж
без секретного ключа отвергался (поверх content-floor). Task 1 — фундамент:

- receipt-sign.mjs: +домен RECEIPT_DOMAINS.FLOOR_ESCAPE='floor-escape' (R-31,
  изолирует подпись floor-escape от approval/frozen-plan).
- askuser-answer-parser.mjs: +signFloorEscapeRecord/verifyFloorEscapeRecord —
  зеркало signApprovalRecord/verifyApprovalRecord, домен FLOOR_ESCAPE. Чистые,
  без ключа → sig:null.

TDD: 5 новых тестов (доменная изоляция, подпись/верификация целой записи,
подделка/без sig/без ключа/чужой ключ/чужой домен → false). Регрессия по
затронутым файлам 82 GREEN, 0 регрессий.

Спека: docs/superpowers/specs/2026-06-10-floor-escape-signing-design.md
План:  docs/superpowers/plans/2026-06-10-floor-escape-signing.md (Task 1)
Прод-код инертен до провижининга ключа (Фаза 8).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 05:13:20 +03:00
Дмитрий c9f823df38 feat(board): live source for guard board escapes/blocks (D-3)
Доска «кто на посту» (STATUS.md §7) теперь показывает реальные недавние
escape владельца и блоки машин М1–М6 вместо хардкода []/[].

- new tools/guard-block-log.mjs: logGuardBlock (best-effort, fail-quiet,
  Node fs append в guard-blocks-<sess>.jsonl) + loadRecentBlocks/
  loadRecentEscapes (скан session-файлов runtime, окно 24ч + cap 10, ts→ISO).
- проводка logGuardBlock в block-ветку main() 9 машинных хуков (floor /
  supreme-gate / judge-gate / snapshot / read-path-deny / mcp-classification /
  normative-content-rules / verify-gate / criterion-gate). Логгер вызывается
  ПОСЛЕ решения, не влияет на block; decide() pure не тронут.
- status-md-generator CLI: recentEscapes/recentBlocks из читателей вместо []/[].

До флипа Фазы 8 доска показывает 0/0 (хуки не зарегистрированы — данных нет);
реальная польза — пост-флип наблюдаемость.

TDD: guard-block-log.test (6) + 9 структурных wiring-тестов + 1 board-тест.
Гейт закрытия: sharp-edges (промежуточный по 9 хукам + читатели) +
variant-analysis (все block-ветки покрыты, иных источников нет). Регрессия
tools-only 3465 passed / 2 skipped / 0 failed (было 3449+2skip). 0 регрессий.

Plan: docs/superpowers/plans/2026-06-10-guard-board-live-source.md
2026-06-10 04:28:53 +03:00
Дмитрий f2aa3fb755 feat(seal): validate judge_mode at freeze time (defense-in-depth, SE-2 §4)
assertValidJudgeMode guard in freezePlan/freezeArtifact: fail-CLOSE on any
mode != {null|shadow|live-block}. Closes YAGNI-candidate from sealed-plan §11
/ gate1+se2 §4 (SE-a) — bogus mode can no longer enter a seal at the source
(complements the wall's fail-closed live-block whitelist).

- freezePlan validates the judgeMode param; freezeArtifact validates the
  embedded artifact.judge_mode (injected by seal-orchestration).
- guard sits before id/sig computation -> no partially-signed bogus-mode seal.
- throw is best-effort-swallowed at enforce-judge-gate.mjs onWiredSeal ->
  no seal produced (fail-CLOSE), hook never crashes.
- real flow never trips it (judgeGateMode yields only inert/shadow/live-block,
  inert never seals) — drift-only guard.

TDD: new tools/plan-lock-judge-mode.test.mjs (6 tests). Regression tools-only
3449 passed / 2 skipped / 0 failed (was 3443+2skip). 0 regressions.
sharp-edges + variant-analysis: clean (only two seal producers, both guarded;
wall comparison already fail-closed).

Plan: docs/superpowers/plans/2026-06-09-seal-time-judge-mode-validation.md
2026-06-10 03:48:07 +03:00
Дмитрий 3c1a673833 feat(gate1+se2): wire spec-judging (Гейт-1) + fail-closed wall whitelist (SE-2)
М7 Фаза 8 — последние 2 хвоста до флипа. Inline TDD; спека+план одобрены; 2 адверсариальных прохода.

Гейт-1 (печать артефакта в рантайме):
- extractGate1Product (enforce-judge-gate.mjs): детект записи specs/*.md (Write-only) → судить
  линзами gate1 (движок уже поддерживает). runJudgeGate: спека→gate1, иначе план→gate2.
- judged_hash от СЫРОГО content (SD-1, разбор #1 High): trimmed source_sha не сошёлся бы с
  печатью sealOnWiredGo → артефакт НИКОГДА не печатался бы. Один канон судья→печать.
- extractGate2Product не тронут (Гейт-2 = планы, регрессия зелёная).

SE-2 (стена не чтит shadow):
- decideMode (enforce-supreme-gate.mjs): fail-closed whitelist — энфорсмент ТОЛЬКО при
  judge_mode==='live-block' на ОБЕИХ печатях; shadow/null/опечатка → разговорный (мутаторы block).
  Разбор #1 (VA-a/SE-a): убрана единственная fail-OPEN сверка; опечатка режима безопасна.
- мигрированы 3 enforce-теста стены на judge_mode:'live-block'.

Тесты: gate1 10, SE-2 6; регрессия tools-only 3443 passed / 2 skip / 0 регрессий.
Гейт-1 тесты — отдельный файл enforce-judge-gate-gate1.test.mjs (real-test-verifier блокирует
import-only Edit существующего теста). Печать в рантайме до флипа НЕ производится.
2026-06-09 19:05:17 +03:00
Дмитрий c18b04f4e4 feat(seal): sealed-plan production pipeline (M7 Фаза 8 code-precondition)
Производство двух печатей (артефакт-решение + план-шаги), чтобы стене М2 было
что матчить — код-предусловие флипа. Inline TDD, спека/план одобрены владельцем.

- C1 artifact-from-spec.mjs: спека markdown -> {sections, source_sha} по якорям {#id} (P2-2).
- C2 plan-steps-parse.mjs: план -> [{op,object,ref}], fail-CLOSE, reject op:Task (VA-4),
  канон object = repo-relative POSIX (SE-5; pathNormalize только на матче в стене, не на парсе).
- C3/C4 plan-lock.mjs: judge_mode в ПОДПИСАННОЙ базе freezePlan (VA-2) + атомарный persist
  temp->rename для обоих save (SE-4/VA-3, артефакт ДО плана).
- C6 seal-orchestration.mjs: sealableArtifact/sealablePlan + judgedHashOf (SD-1) +
  sealArtifact/sealPlan на РЕАЛЬНОМ GO (SE-3 wired===true), штамп artifact_id из текущего
  артефакта (SD-3), judge_mode впрыснут в печать ПОСЛЕ хеш-сверки sealOnApproval (фикс TOCTOU).
- C5 enforce-judge-gate.mjs: SPEC_PATH_RE + sealOnWiredGo (печать на wired GO, инъекция в main,
  юнит-тесты hermetic) + judged_hash в вердикте runJudgeGate. extractGate2Product не тронут
  (Гейт-2 = планы; Гейт-1 spec-judging — отдельный заход перед флипом).
- Интеграция seal-to-wall: печать -> decideMode стены М2 (allow / non-match block / closed-door).

Тесты: full tools-only регрессия 3427 passed | 2 skipped, 0 регрессий (+29 новых кейсов).
Печать в рантайме НЕ производится до флипа (стена/судья не зарегистрированы) — сборка
готовит код-предусловие. Спека docs/superpowers/specs/2026-06-09-sealed-plan-production-design.md.
2026-06-09 17:50:25 +03:00
Дмитрий 7037eb8d48 fix(tdd-verifier): escape dots in TEST_FILE_RE (stop misclassifying *spec.mjs prod files)
Unescaped dots in /.(?:test|spec).[a-z0-9]+$/i matched the bare substring "spec"
in prod filenames ending like artifact-from-spec.mjs, so the real-test verifier
treated a production module as a test file and blocked it for lacking expect().
Anchor the dots (\.) so only genuine .test.<ext> / .spec.<ext> files match.
Real test files are still fully checked (regression guard added).
2026-06-09 17:50:19 +03:00
Дмитрий 3811d24528 fix(keychain): async keytar read via sync subprocess (no getPasswordSync) 2026-06-09 14:24:43 +03:00
Дмитрий 5883fa8fc9 fix(content-floor): quote-aware redirect in matchBashHardBlacklist (port 8dc632ec after --ours merge) 2026-06-09 13:35:24 +03:00
Дмитрий a9bb00278c Merge branch 'main' into worktree-brainrepo
# Conflicts:
#	tools/enforce-coverage-verify.mjs
#	tools/enforce-coverage-verify.test.mjs
#	tools/enforce-router-gate.mjs
#	tools/enforce-router-gate.test.mjs
2026-06-09 13:26:12 +03:00
Дмитрий 1513f3bbc3 feat(manifest): register coverage-verify + todowrite-skill-verifier (G2 phase8 manifest 11to13) 2026-06-09 12:56:40 +03:00
Дмитрий e2459bad0e feat(criterion-gate): register enforce-criterion-gate in manifest (Level B task6) 2026-06-09 12:30:06 +03:00
Дмитрий ed9ace9b4e feat(criterion-gate): consumer enforce-criterion-gate fail-CLOSE (Level B task5) 2026-06-09 12:26:33 +03:00
Дмитрий f7027efe99 feat(criterion-gate): per-criterion green producer (Level B task4) 2026-06-09 12:24:30 +03:00
Дмитрий 85a2607dc0 feat(criterion-gate): mutation runner in-place + restore (Level B task3) 2026-06-09 12:22:40 +03:00
Дмитрий e98cbc45eb feat(criterion-gate): vitest json run + test-count guard (Level B task2) 2026-06-09 12:21:21 +03:00
Дмитрий 03cc856486 feat(criterion-gate): JS mutation operators pure (Level B task1) 2026-06-09 12:19:50 +03:00
Дмитрий 0885d88546 feat(verify-gate): register enforce-verify-gate in manifest (G1 task7) 2026-06-09 10:22:36 +03:00
Дмитрий f28bf7b8e9 feat(verify-gate): consumer enforce-verify-gate fail-CLOSE (G1 task6) 2026-06-09 10:19:44 +03:00
Дмитрий 18b6937b4c feat(verify-gate): producer staged-fingerprint + io main (G1 task5) 2026-06-09 10:18:11 +03:00
Дмитрий 0ef1ed855d feat(verify-gate): producer pure buildVerifyReceipt (G1 task4) 2026-06-09 10:16:22 +03:00
Дмитрий 750e8e6641 feat(verify-gate): rubilnik verify-gate-config (G1 task3) 2026-06-09 10:15:16 +03:00
Дмитрий 7dc97a3ea3 feat(verify-gate): verify-receipt pure core sign/accept (G1 task2) 2026-06-09 10:13:57 +03:00
Дмитрий 030ceba1d0 feat(verify-gate): VERIFY_PASS receipt domain (G1 task1) 2026-06-09 10:12:41 +03:00
Дмитрий a48eb6fe1c feat(judge-gate): runJudgeTurn three-mode wiring + main (shadow runs+logs, live-block fail-close) 2026-06-09 08:46:28 +03:00
Дмитрий ae60f47ab3 feat(judge-gate): shadow verdict log + judge-unavailable warn (J8) 2026-06-09 08:43:00 +03:00
Дмитрий aadff281b8 feat(judge-gate): async runJudgeGate orchestration + degraded-allow on unavailable 2026-06-09 08:40:36 +03:00
Дмитрий 7fcc242e83 feat(judge-gate): callJudgeModel async transport + spend-gate (unavailable vs malformed) 2026-06-09 08:38:38 +03:00
Дмитрий 71be6d9a9b feat(judge-gate): extractGate2Product Write-only plan detector 2026-06-09 08:36:03 +03:00
Дмитрий f701a311bc feat(judge-gate): parseJudgeResponse fail-closed parser 2026-06-09 08:33:46 +03:00
Дмитрий 3663705dde fix(shadow-replay): exclude runtime writes and scope M6 to bash 2026-06-09 07:20:48 +03:00
Дмитрий 926c329b12 feat(shadow-replay): main cli and runAll 2026-06-09 06:30:17 +03:00
Дмитрий 8b5197ce69 feat(shadow-replay): buildCorpus runMachine report 2026-06-09 06:26:57 +03:00
Дмитрий 912b5ec0f7 feat(shadow-replay): M4 adapter and M3 divergence 2026-06-09 06:24:33 +03:00
Дмитрий 6d4beb2d35 feat(shadow-replay): M2 adapter signed plan 2026-06-09 06:22:40 +03:00
Дмитрий bdc6d68c63 feat(shadow-replay): M5 M6 adapters 2026-06-09 06:21:01 +03:00
Дмитрий c259dd4468 feat(shadow-replay): classifyOutcome 2026-06-09 06:19:10 +03:00
Дмитрий 46eea4a747 feat(shadow-replay): fixtures 2026-06-09 06:17:38 +03:00
Дмитрий 37423a4b10 test(action-journal): pin M1 fail-closed on torn append (entry without head update) 2026-06-09 05:23:51 +03:00
Дмитрий 386b10ce8c feat(supreme-gate): tree leaf resolution plus advance flag for R-08 waves 2026-06-09 05:12:38 +03:00
Дмитрий 10d4019f11 feat(supreme-gate): pointer state accepts tree position as index array 2026-06-09 05:10:45 +03:00
Дмитрий 218628519f feat(plan-lock): tree leaves, leaf resolver, validate, recursive criterion ids, hierarchical consumers 2026-06-09 05:09:21 +03:00
Дмитрий c30a92bf71 feat(step-pointer): serialize plus tree navigation for R-08 waves 2026-06-09 05:06:49 +03:00
Дмитрий d77477486a feat(reconcile): wire reconcileEvent reader in main (inert, WARN-only) 2026-06-09 04:08:22 +03:00
Дмитрий b9cd792db3 feat(status): R-30 journal integrity block via live verifyChain (read-only) 2026-06-09 04:06:53 +03:00
Дмитрий 2b530ae687 feat(status): R-24 door coverage block (read-only) 2026-06-09 04:05:15 +03:00