CHAT_OUTPUT_BEGIN
COMMAND_ID=ERRORS-INDEX-004
STATUS=OK
RC=0
HOST=pve01
MODE=read-only
COMPONENT=error-ledger
REFERENCE_REGISTER_CHECK=OK
REFERENCE_SHA256=f6f7749fda946015116cdbbd754c7e7f20356b4a6cf4df330a1b0b8fdf59cbb2
ERROR_REGISTER_CHECK=OK
ERROR_REGISTER_SHA256=ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc
COMMAND_SHA256=a45b24ab6a161508f31ee8a6b954c045e74824b4d12adfef6f298cfac6111016
DUPLICATE_FAILED_COMMAND_BLOCKED=false
EXECUTION_STARTED=true
CHANGES_MADE=false
SANITIZED=yes
SECRETS_INCLUDED=no
PRIVATE_ADDRESSES_INCLUDED=no
RAW_EVIDENCE_SHA256=655ec287a9095e799677cb7f87b08d4119cbdf9027d7097fa610da5a022ab30b
SANITIZED_OUTPUT_SHA256=655ec287a9095e799677cb7f87b08d4119cbdf9027d7097fa610da5a022ab30b
OUTPUT_BEGIN
{
  "schema_version": 1,
  "status": "READY",
  "generated_at_utc": "2026-07-21T07:22:22.821762Z",
  "source": {
    "path": "/etc/pve/HOMELAB_ASSISTANT_ERROR_REGISTER.md",
    "sha256": "ab8308549db90d235b591af0581580c3c7ba1b287b129cc6111c2eef0a0f9ecc",
    "line_count": 934,
    "sanitized": true
  },
  "summary": {
    "entry_count": 154,
    "rule_count": 90,
    "duplicate_entry_ids": [],
    "duplicate_entry_signatures": [
      "9cd06e064f5a86be9626c4f9a2a819b8d0e71b03309c705fe0e34e1e59f81b73"
    ],
    "duplicate_rule_signatures": [
      "b936ae30ffb935118585b5a133d9c32643946a3ba5034b85f42bae000518d49a"
    ]
  },
  "entries": [
    {
      "id": "ERR-H-L1-HOMELAB-ASSISTANT-ERROR-REGISTER",
      "kind": "heading",
      "level": 1,
      "source_line": 1,
      "title": "HOMELAB ASSISTANT ERROR REGISTER",
      "summary": "Назначение: перед каждой следующей командой сверяться с этим файлом.",
      "rule_like": true,
      "signature": "cd4b4d42c26702b03e3e81a620c46dbe3d498f3f2233215024a5c2a49cc1dbfc"
    },
    {
      "id": "ERR-N-1-L6",
      "kind": "numbered",
      "source_line": 6,
      "title": "Повторно дал слишком большой интерактивный paste в shell.",
      "summary": "",
      "rule_like": false,
      "signature": "f732778534f22b73e31dead215c9c7078f48a552a5341ae3830eea618885e8a7"
    },
    {
      "id": "ERR-N-2-L7",
      "kind": "numbered",
      "source_line": 7,
      "title": "Повторно использовал here-doc/Markdown/backticks там, где нужен короткий безопасный файл или отдельный скрипт.",
      "summary": "",
      "rule_like": false,
      "signature": "b13c8946e274927bd041960c76208b6f62e22d46f07233388878c1f47afc426a"
    },
    {
      "id": "ERR-N-3-L8",
      "kind": "numbered",
      "source_line": 8,
      "title": "Дал генератор справочника прямо в терминал вместо безопасного маленького шага.",
      "summary": "",
      "rule_like": false,
      "signature": "8de70b8d263233557a33a065ee49004ef01e328de0d736eb9e328930119458be"
    },
    {
      "id": "ERR-N-4-L9",
      "kind": "numbered",
      "source_line": 9,
      "title": "Нарушил своё же правило: не давать длинные вложенные команды с кавычками.",
      "summary": "",
      "rule_like": false,
      "signature": "d1bf3e42845e6c9d25511814c1da9b21cd8001474f17fd60240194f812f06fe0"
    },
    {
      "id": "ERR-H-L5-КРИТИЧЕСКИЕ-ОШИБКИ-АССИСТЕНТА",
      "kind": "heading",
      "level": 2,
      "source_line": 5,
      "title": "Критические ошибки ассистента",
      "summary": "1. Повторно дал слишком большой интерактивный paste в shell. 2. Повторно использовал here-doc/Markdown/backticks там, где нужен короткий безопасный файл или отдельный скрипт. 3. Дал генератор справочника прямо в терминал вместо безопасного маленького шага. 4. Нарушил своё же правило: не давать длинные вложенные команды с кавычками.",
      "rule_like": false,
      "signature": "0ba065b54f515fe668143ec87d006ea8f27639e6529fe46367ac9acd6f2e28d6"
    },
    {
      "id": "ERR-H-L11-ЖЁСТКИЕ-ПРАВИЛА-ПЕРЕД-КАЖДОЙ-КОМАНДОЙ",
      "kind": "heading",
      "level": 2,
      "source_line": 11,
      "title": "Жёсткие правила перед каждой командой",
      "summary": "CHECK-1: команда не должна быть большим paste. CHECK-2: команда не должна содержать большой here-doc. CHECK-3: команда не должна смешивать Markdown, backticks и shell-логику. CHECK-4: команда не должна иметь вложенный ssh с несколькими уровнями кавычек. CHECK-5: команда не должна печатать секреты. CHECK-6: если создаётся файл, сначала маленький безопасный шаг, потом проверка. CHECK-7: если команда длиннее 8 строк, её нельзя давать в интерактивный shell. CHECK-8: для edge-vm использовать debian@[PRIVATE_IP] и sudo.",
      "rule_like": true,
      "signature": "7ac085a9df3e592d1d7d6686ca4980278110a07e296ddc0253cedb075cc08d84"
    },
    {
      "id": "ERR-H-L23-ТЕКУЩИЕ-ВАЖНЫЕ-ФАКТЫ",
      "kind": "heading",
      "level": 2,
      "source_line": 23,
      "title": "Текущие важные факты",
      "summary": "Internal network: [PRIVATE_IP]/24. Migration config: migration: secure,network=[PRIVATE_IP]/24. Corosync remains on [PRIVATE_IP]/12/13. VM160 forum-prod is not in Proxmox nightly backup. VM130 edge-vm has secondary disk backup=0 risk. 05_edge_compose_safe.tgz quarantined.",
      "rule_like": false,
      "signature": "ecd7f734df435c41c5f01c9fd7f9371a4012c6019493072741c45c6eaf3ed383"
    },
    {
      "id": "ERR-N-11-L35",
      "kind": "numbered",
      "source_line": 35,
      "title": "Ошибка: считать offhost OK после failed rsync.",
      "summary": "",
      "rule_like": false,
      "signature": "aa8a2a5cc3228d73d2feadd69fa2fb1d0f76d020280f69921ac5c00dcfc1f9fd"
    },
    {
      "id": "ERR-N-12-L40",
      "kind": "numbered",
      "source_line": 40,
      "title": "Ошибка: широкий secret-поиск по /opt/stacks дал шум.",
      "summary": "",
      "rule_like": false,
      "signature": "da33bb4eaab4ca4827c1e122c4292fa4181a7607db77ebce5d185b867bfe0ac3"
    },
    {
      "id": "ERR-N-13-L45",
      "kind": "numbered",
      "source_line": 45,
      "title": "Ошибка: SQL с одинарными кавычками внутри одинарной SSH-команды ломается.",
      "summary": "",
      "rule_like": false,
      "signature": "888cbc312835f6303e74db4c5ed2dcaef1e552e657f83ca6f3f82834eb06c23e"
    },
    {
      "id": "ERR-N-14-L50",
      "kind": "numbered",
      "source_line": 50,
      "title": "Ошибка: путать контекст входа и узел выполнения.",
      "summary": "",
      "rule_like": false,
      "signature": "97eec6b76b51ffbc78105ce4e450846c5ff1c85dfbcdfeb3e6c687addd6a1391"
    },
    {
      "id": "ERR-N-15-L57",
      "kind": "numbered",
      "source_line": 57,
      "title": "Ошибка: повторно нарушено правило №13 после его добавления.",
      "summary": "",
      "rule_like": false,
      "signature": "95160c6fda4d8bc77fc542236363b182ba66a7314a8032379b033a0feba5e3b2"
    },
    {
      "id": "ERR-N-16-L63",
      "kind": "numbered",
      "source_line": 63,
      "title": "Основополагающее правило: перед каждой командой ассистент обязан явно написать: Сверка с файлом ошибок: CHECK пройден.",
      "summary": "",
      "rule_like": true,
      "signature": "254db5ca3a2611269de7af81ececd43035134e01a8e629c34434913c49eeeb41"
    },
    {
      "id": "ERR-N-19-L68",
      "kind": "numbered",
      "source_line": 68,
      "title": "Уточнение формата: не писать отдельную строку сверки перед каждой командой в пакете.",
      "summary": "",
      "rule_like": true,
      "signature": "a77bda86805176e2dc721961454b31a5bff15fe924bd43496475c5816a8f9650"
    },
    {
      "id": "ERR-N-20-L73",
      "kind": "numbered",
      "source_line": 73,
      "title": "Ошибка: широкий XenForo SMTP-поиск по src дал шум исходников.",
      "summary": "",
      "rule_like": false,
      "signature": "bed26484549b8d47c75ee6150c206503c05074cc8a5b25dfde0735596aaf956c"
    },
    {
      "id": "ERR-N-21-L77",
      "kind": "numbered",
      "source_line": 77,
      "title": "Ошибка: nested PHP php -r дал Parse error на forum-prod.",
      "summary": "",
      "rule_like": false,
      "signature": "1198d8f0576c5dc8e3883ac7d7e423c8391e946c73bf5ac3cf6063278a68a5af"
    },
    {
      "id": "ERR-N-22-L82",
      "kind": "numbered",
      "source_line": 82,
      "title": "Ошибка: самодельный base64 PHP для SMTP auth сломан.",
      "summary": "",
      "rule_like": false,
      "signature": "c629522366fb27522bec6dd9b9b24f532bbc9c3372fbf4d706673bf283bdf498"
    },
    {
      "id": "ERR-N-23-L87",
      "kind": "numbered",
      "source_line": 87,
      "title": "Ошибка: MariaDB не поддержала cast('true' as json) в JSON_SET.",
      "summary": "",
      "rule_like": false,
      "signature": "4faccf6aa3103144040fd4efb62dd82e23d9f28ecfba72502293f6e2f060b819"
    },
    {
      "id": "ERR-N-24-L92",
      "kind": "numbered",
      "source_line": 92,
      "title": "Ошибка: exit 1 в interactive-check закрыл SSH-сессию.",
      "summary": "",
      "rule_like": false,
      "signature": "c522fbe5bc7aadfc904f1bde7328952b554884941afb3558e60cfe6a4ed378cd"
    },
    {
      "id": "ERR-N-25-L97",
      "kind": "numbered",
      "source_line": 97,
      "title": "Ошибка анализа: не надо слепо включать smtpSsl=true при smtpPort=587.",
      "summary": "",
      "rule_like": false,
      "signature": "17417cdf64e208886d887fd449a612090a81aa5a8f47944f17993821ad5e8235"
    },
    {
      "id": "ERR-N-26-L102",
      "kind": "numbered",
      "source_line": 102,
      "title": "Ошибка: openssl -crlf вместе с ручным CRLF ломает SMTP AUTH checker.",
      "summary": "",
      "rule_like": false,
      "signature": "3f83176fadcfeb2318a3e3fc75c13e3944d3fef3c0aa7aef40db3736b27e794a"
    },
    {
      "id": "ERR-N-27-L108",
      "kind": "numbered",
      "source_line": 108,
      "title": "Ошибка: Python heredoc внутри ssh сломал not_ok диагностику.",
      "summary": "",
      "rule_like": false,
      "signature": "437f761644210f81c8ca3333410a58585c7b74c54a983b9d84159d98c49b4847"
    },
    {
      "id": "ERR-N-28-L112",
      "kind": "numbered",
      "source_line": 112,
      "title": "Ошибка: docker compose ls --format json | wc -l дал ложный COMPOSE_PROJECT_COUNT=1.",
      "summary": "",
      "rule_like": false,
      "signature": "752e0913f85275527a448868cce24aa85cf06949ea82e066e4dc0fc2e857d1ad"
    },
    {
      "id": "ERR-N-29-L116",
      "kind": "numbered",
      "source_line": 116,
      "title": "Ошибка: monitoring compact status искал неверные имена health-файлов.",
      "summary": "",
      "rule_like": false,
      "signature": "61df0580bfb53db159bb0c3fced23235437e823a877822e0b8aebb7518453d4d"
    },
    {
      "id": "ERR-N-30-L121",
      "kind": "numbered",
      "source_line": 121,
      "title": "Ошибка/инцидент: Prometheus API HTTP_CODE=000 после monitoring reference.",
      "summary": "",
      "rule_like": false,
      "signature": "c0dac19354e77153d3f61d53659a8e694c57308bc564e03c833cb68831e88031"
    },
    {
      "id": "ERR-N-31-L125",
      "kind": "numbered",
      "source_line": 125,
      "title": "Строгое правило: перед каждой командой сверяться с файлом ошибок и справочником.",
      "summary": "",
      "rule_like": true,
      "signature": "fa4e49944e2caf8ba4f26a6064bfb465c035e8bc3184244c0314812e81bd6df5"
    },
    {
      "id": "ERR-N-32-L130",
      "kind": "numbered",
      "source_line": 130,
      "title": "Ошибка: 153_PROMETHEUS_TARGETS_SETTLED_PROOF содержит SyntaxError из-за вложенного Python внутри SSH.",
      "summary": "",
      "rule_like": false,
      "signature": "13b3234027d04af4194a5352dda2a5bd21ab955751eb161b953637347da8febf"
    },
    {
      "id": "ERR-N-34-L135",
      "kind": "numbered",
      "source_line": 135,
      "title": "Ошибка операционного ввода: полный терминальный лог MobaXterm попал в shell.",
      "summary": "",
      "rule_like": false,
      "signature": "9a7930e756982125ac45f47a4adf7dfdb10906bd950db4ddae3659021e01bc64"
    },
    {
      "id": "ERR-N-33-L140",
      "kind": "numbered",
      "source_line": 140,
      "title": "Security finding: root authorized_keys на PVE-нодах имел права 777.",
      "summary": "",
      "rule_like": false,
      "signature": "44a7ef2e7d0428388c9b30d12ecfad460caaca457b5d283ebf00ae87c1b2119c"
    },
    {
      "id": "ERR-N-35-L144",
      "kind": "numbered",
      "source_line": 144,
      "title": "Ошибка проверки: 160_PVE_ROOT_AUTHORIZED_KEYS_PERMISSIONS_FIXED не доказал исправление прав.",
      "summary": "",
      "rule_like": false,
      "signature": "336bfad03f4600ddc0d8996bdbbebeae51cfa0c1e2281ae1a838b8b4d8bc7dfd"
    },
    {
      "id": "ERR-N-36-L149",
      "kind": "numbered",
      "source_line": 149,
      "title": "Quality check: Storage block needs integrity and pve03 capacity coverage review.",
      "summary": "",
      "rule_like": false,
      "signature": "a11f29a5da892017620722200a35bdde4c0fe5aa591de4331bfbee0851e38266"
    },
    {
      "id": "ERR-N-37-L154",
      "kind": "numbered",
      "source_line": 154,
      "title": "Coverage gap: pve03_staging missing from disk-space health coverage.",
      "summary": "",
      "rule_like": false,
      "signature": "d2fded36e2e512289b08c31b7fc6872e4efbf61c06e2840e5a2dfef9a8e989f8"
    },
    {
      "id": "ERR-N-38-L158",
      "kind": "numbered",
      "source_line": 158,
      "title": "Quality check: Service Dependency Map block needs integrity review.",
      "summary": "",
      "rule_like": false,
      "signature": "79cffb917505e112ac9507770da4068e145c75d2278d50d1034bb594b1da5062"
    },
    {
      "id": "ERR-N-39-L162",
      "kind": "numbered",
      "source_line": 162,
      "title": "Ошибка проверки: post deep audit BAD_MARKER scan caught prose false positive.",
      "summary": "",
      "rule_like": false,
      "signature": "6dc6e561ff4c49c39a8d2f7fdac4fbfd9d2b6295212185791fb9091f644a2824"
    },
    {
      "id": "ERR-N-40-L166",
      "kind": "numbered",
      "source_line": 166,
      "title": "Ошибка/вывод: VM150 Mail cloud single-file upload failed with 413.",
      "summary": "",
      "rule_like": false,
      "signature": "d7e096eac4cbc2b602601af11ce42bb716d28142a278203aefb65699b5fd4b9c"
    },
    {
      "id": "ERR-N-41-L170",
      "kind": "numbered",
      "source_line": 170,
      "title": "Ошибка: post-backup-pass-slo-reconciliation checked pve01 router health on edge-vm.",
      "summary": "",
      "rule_like": false,
      "signature": "44ceb289e0963e0e97c6e8019e93120ad638f5648e8596b987e7d413eaeb59aa"
    },
    {
      "id": "ERR-H-L31-ПРАВИЛО-ДЛЯ-СПРАВОЧНИКА",
      "kind": "heading",
      "level": 2,
      "source_line": 31,
      "title": "Правило для справочника",
      "summary": "Не генерировать большой справочник через интерактивную вставку. Следующий справочник делать только маленькими append-блоками или через файл, созданный вне интерактивного shell. 11. Ошибка: считать offhost OK после failed rsync. Если rsync/scp упал, нельзя проверять latest offhost-файл без сверки имени. Проверка должна подтвердить именно новый архив, например дату 2026_06_30-00_20_59. Старый OFFHOST_ZSTD_OK не закрывает новый backup. 12. Ошибка: широкий secret-поиск по /opt/stacks дал шум. Не искать Cloudflare/SMTP сразу по /opt/stacks, MkDocs site, backups и HTML-отчётам.",
      "rule_like": true,
      "signature": "2c69d96d9f906cc88ab3158941ba041ca57ddee1e03554fad588eea96041930a"
    },
    {
      "id": "ERR-H-L174-ASSISTANT-COMMAND-BATCHING-RULE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 174,
      "title": "ASSISTANT_COMMAND_BATCHING_RULE_20260630",
      "summary": "- Rule: combine related infrastructure checks, proof generation, reference updates and validation scans into one safe batch command when practical. - Do not split work into many small commands when the operations are low-risk, read-only, or naturally part of one evidence block. - Still keep mandatory preflight checks, short bounded commands, no large paste, no here-docs, no interactive shell traps, and no sensitive values in output. - Split into smaller commands only when safety, rollback, quoting risk, or troubleshooting clarity requires it.",
      "rule_like": true,
      "signature": "73ef09d525c334b9874616d5bafdf17d4898a815d35e736e33df0b83790f6539"
    },
    {
      "id": "ERR-H-L180-HOME-PORTAL-BASE64-APPLY-FAILURE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 180,
      "title": "HOME_PORTAL_BASE64_APPLY_FAILURE_20260630",
      "summary": "- Prior home-portal apply attempt 312 failed because a large base64 Python payload decoded as invalid UTF-8. - Rule: avoid large opaque base64 script payloads for homelab changes; use transparent bounded shell/perl/python commands instead.",
      "rule_like": true,
      "signature": "4964d3ff556bb377d7c9d0cb4d487afabc0d8ac69c7fb2a5fccb5ff077a8a478"
    },
    {
      "id": "ERR-H-L184-HOMELAB-COMMAND-SAFETY-HARDENING-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 184,
      "title": "HOMELAB_COMMAND_SAFETY_HARDENING_20260630",
      "summary": "- Rule: do not use large opaque payloads, generated base64 scripts, or hidden multi-line script blobs for homelab changes. - Use transparent bounded shell/perl/python commands, or create a temporary readable script and validate it before execution. - Any script used for changes must pass a syntax/parse check before it is allowed to modify files or restart services. - Any Traceback, UnicodeDecodeError, SyntaxError, Permission denied, command-not-found, nonzero critical RC, or failed write marker must force REVIEW or FAILED, never OK. - Success requires both runtime health and content-specific post-checks: expected files changed, expected cards exist, duplicates absent, service/container alive, and no new critical log pattern. - HTTP 200 alone is not a success condition for configuration changes. - If an apply step fails, the next command must explicitly record the failed attempt and then run a corrected transparent apply command.",
      "rule_like": true,
      "signature": "463e648fd9e6d671debe81d12b455563460bc669983e083ef5f0f3b4df2f208b"
    },
    {
      "id": "ERR-H-L193-HOME-PORTAL-API-ERROR-CLOSURE-RULE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 193,
      "title": "HOME_PORTAL_API_ERROR_CLOSURE_RULE_20260630",
      "summary": "- Rule: home portal closure requires current Homepage API-error logs to be zero after restart/reload, not only HTTP 200. - If Homepage UI shows API error or logs contain current httpProxy/API timeout errors, the portal is REVIEW until the widget/API cause is disabled or fixed. - External informational widgets such as weather/Open-Meteo must not block the service launcher portal.",
      "rule_like": true,
      "signature": "1a011c6be8607fdacb9571baa017241dcb441a0025f985ecce3d0937a4f7e3b5"
    },
    {
      "id": "ERR-H-L198-HOME-PORTAL-COMPLEX-QUOTING-FAILURE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 198,
      "title": "HOME_PORTAL_COMPLEX_QUOTING_FAILURE_20260630",
      "summary": "- Previous home portal cleanup command failed in local bash before execution: syntax error near unexpected token `('. - Cause: command was too complex and fragile due to nested shell/perl/python quoting. - Rule: do not use deeply nested one-liners for config edits; first run short state checks, then apply small transparent edits.",
      "rule_like": true,
      "signature": "862820180d4dbc46a845af7c9ec8847aeb21cda3fd7634c0d2afd9b3d14d5d8b"
    },
    {
      "id": "ERR-H-L203-HOME-PORTAL-ACTIVE-CONFIG-SCOPE-RULE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 203,
      "title": "HOME_PORTAL_ACTIVE_CONFIG_SCOPE_RULE_20260630",
      "summary": "- Rule: Homepage cleanup validation must check active config files only, not logs or backup files. - Active files are services.yaml, bookmarks.yaml, widgets.yaml, settings.yaml and docker.yaml under /opt/stacks/homepage/config. - Matches in /logs/ or *.bak* are historical evidence only and must not make cleanup REVIEW unless the active config still contains the target.",
      "rule_like": true,
      "signature": "6bfd4c200f43d77ecd18a1c61c377b4c0588a028fb0ca6d565b2361d16a74967"
    },
    {
      "id": "ERR-H-L208-HOME-PORTAL-LINK-VALIDATION-NPMPLUS-DEFAULT-RULE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 208,
      "title": "HOME_PORTAL_LINK_VALIDATION_NPMPLUS_DEFAULT_RULE_20260630",
      "summary": "- Rule: Homepage link validation must not treat HTTP 200 as success if the body is the NPMPlus default “Congratulations” / host-not-set-up page. - Green siteMonitor dots can be false positives when NPMPlus returns its default vhost page. - A portal card is openable only when DNS, reverse-proxy host mapping, and target content are all valid.",
      "rule_like": true,
      "signature": "90363c457faef01bbe65d3e246073788fb426212371de346152c7776d246413b"
    },
    {
      "id": "ERR-H-L213-ROUTER-CLI-STDIN-APPLY-FAILURE-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 213,
      "title": "ROUTER_CLI_STDIN_APPLY_FAILURE_20260630",
      "summary": "- Context: attempted Netcraze router ACL apply through SSH stdin/multiline for Homepage Moscow Router monitor fix. - Evidence: 391_MOSCOW_ROUTER_EDGE_VM_WEBADMIN_ACL_APPLY_PROOF.txt. - Failure: STDIN_SHOW_OK=no, APPLY_SKIPPED=stdin_show_failed, CONFIG_HAS_EDGE_5080=no, CONFIG_HAS_EDGE_5083=no. - Rule: do not apply Netcraze config through stdin/multiline SSH; prove CLI input mode first.",
      "rule_like": true,
      "signature": "7a151b61221247f7e5ecc6488283190d82b489caa9309675c08baded25d7fc6f"
    },
    {
      "id": "ERR-H-L219-ROUTER-CLI-PROBE-STDIN-CONSUMPTION-ERROR-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 219,
      "title": "ROUTER_CLI_PROBE_STDIN_CONSUMPTION_ERROR_20260630",
      "summary": "- Context: ACL syntax read-only probe loop executed only one command because ssh consumed the loop stdin. - Evidence: 393_ROUTER_CLI_ACL_SYNTAX_READONLY_PROBE_PROOF.txt showed COMMAND_COUNT=1. - Fix pattern: use ssh -n or redirect SSH stdin away from the command-list loop for all future SSH-in-loop probes.",
      "rule_like": false,
      "signature": "c801838e0f61bbd4f111bc30b21d530e69124b4448859e0e5f28a93cf0f01fec"
    },
    {
      "id": "ERR-H-L224-ROUTERBACKUP-READONLY-CLI-PERMISSION-LIMIT-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 224,
      "title": "ROUTERBACKUP_READONLY_CLI_PERMISSION_LIMIT_20260630",
      "summary": "- Context: routerbackup user can run show running-config but cannot execute configuration commands such as access-list. - Evidence: 394_ROUTER_CLI_ACL_SYNTAX_READONLY_PROBE_FIXED_PROOF.txt. - Observed: CMD_TEXT=access-list ? returned Core::Configurator error execute denied [cli]. - Rule: do not attempt ACL/config changes as routerbackup; use admin/operator router access or router Web UI for write changes.",
      "rule_like": true,
      "signature": "1d8075c128dbd4b8f34929055e7c4b7c8cfda8f44f0b7874f04afbcc745d5d82"
    },
    {
      "id": "ERR-H-L230-PROOF-SUMMARY-EXTRACTION-BLANK-20260630",
      "kind": "heading",
      "level": 2,
      "source_line": 230,
      "title": "PROOF_SUMMARY_EXTRACTION_BLANK_20260630",
      "summary": "- Context: proof 395 tried to read COMMAND_COUNT/ZERO_RC_COUNT from proof files 393/394, but those summary fields were printed to console and were not persisted inside the proof files. - Evidence: 395_ROUTER_CLI_ERROR_REGISTER_UPDATE_PROOF.txt has blank P393_COMMAND_COUNT/P394_COMMAND_COUNT/P394_ZERO_RC_COUNT fields. - Rule: when creating follow-up proof summaries, derive counts from persisted proof body lines such as CMD_INDEX and CMD_RC, or write summary fields into the proof file itself.",
      "rule_like": false,
      "signature": "1fd6a207e921a706e6d8d3e80984d0436e67192bb56d03c91d20f6b33eba15cd"
    },
    {
      "id": "ERR-H-L235-ROUTER-ACL-UI-RULE-DELETE-AND-RESTORE-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 235,
      "title": "ROUTER_ACL_UI_RULE_DELETE_AND_RESTORE_20260701",
      "summary": "- Context: while moving Moscow Router monitor rules to _WEBADMIN_Bridge1, two _WEBADMIN_Bridge0 Nextcloud Talk TURN rules were accidentally removed. - Deleted/restored rules: Home [PRIVATE_IP]/24 to Nextcloud [PRIVATE_IP] TCP/UDP 3478. - Verification proof: 399_ROUTER_ACL_RESTORE_AFTER_UI_PROOF.txt. - Rule: after manual router Web UI ACL edits, compare _WEBADMIN_Bridge0, _WEBADMIN_Bridge1 and _WEBADMIN_GigabitEthernet1 before continuing.",
      "rule_like": false,
      "signature": "973a47bd8e8f388786157b6d50b53de9fc96b31e582a9645f3cf4189b74ac4fe"
    },
    {
      "id": "ERR-H-L241-ROUTER-MONITOR-NODE-LOOKUP-PROBE-BUG-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 241,
      "title": "ROUTER_MONITOR_NODE_LOOKUP_PROBE_BUG_20260701",
      "summary": "- Context: proof 400 tested Moscow router HTTPS monitor from Homepage container with custom Node lookup callback. - Failure: NODE_HTTPS_RESOLVE_ERROR=ERR_INVALID_IP_ADDRESS and NODE_HTTPS_RESOLVE_INSECURE_ERROR=ERR_INVALID_IP_ADDRESS. - Impact: Node HTTPS resolve subtest is invalid; curl --resolve results remain valid. - Rule: do not rely on custom Node lookup callback until separately tested; prefer curl --resolve or simple direct Node HTTP checks.",
      "rule_like": true,
      "signature": "97151454d5ffda69d45b802ed15dc82057b02632e141b3729fa8ad47a959c556"
    },
    {
      "id": "ERR-H-L247-ROUTER-HTTP-SERVICE-FORBIDDEN-FROM-EDGE-VM-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 247,
      "title": "ROUTER_HTTP_SERVICE_FORBIDDEN_FROM_EDGE_VM_20260701",
      "summary": "- Context: Moscow Router Homepage monitor after ACL fix. - Evidence: proofs 399, 400, 401. - Observed: edge-vm TCP to [PRIVATE_IP]:5080/5083 is OK, but router HTTP/HTTPS returns 403 for all tested paths. - Impact: Homepage siteMonitor cannot be made green via direct router HTTP URL until router web service allows the Bridge1/Proxmox/protected segment or an explicit safe monitor endpoint is used. - Rule: do not keep changing ACL for this symptom; ACL is already correct. Fix service access scope or use a dedicated monitor endpoint.",
      "rule_like": true,
      "signature": "b0de3053a46b06841cbaf6ea5945be49ad8d451c7c8453d899d01a9f520cc384"
    },
    {
      "id": "ERR-H-L254-PY-COMPILE-PYC-PERMISSION-ERROR-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 254,
      "title": "PY_COMPILE_PYC_PERMISSION_ERROR_20260701",
      "summary": "- Context: installing edge-vm Moscow router health endpoint. - Mistake: assistant used `python3 -m py_compile` on a script installed under /usr/local/sbin as root. - Actual impact: non-root syntax check attempted to create /usr/local/sbin/__pycache__ and failed with Permission denied. - Recovery: validate syntax with `ast.parse` instead of py_compile, because it does not write .pyc files. - Rule: do not use py_compile against root-owned system paths from an unprivileged user.",
      "rule_like": true,
      "signature": "683a661513a8d46a89bf6492c8f12564f6f9fe262ce16b0f6487f63c0f2666f0"
    },
    {
      "id": "ERR-H-L261-ROUTER-HOME-IP-DOCKER-ROUTE-CONFLICT-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 261,
      "title": "ROUTER_HOME_IP_DOCKER_ROUTE_CONFLICT_20260701",
      "summary": "- Context: Moscow Router Homepage siteMonitor attempt using http://[PRIVATE_IP]:5080. - Evidence: proof 405 showed route to [PRIVATE_IP] via Docker bridge and Homepage container ECONNRESET. - Impact: do not use [PRIVATE_IP] directly as Homepage siteMonitor from edge-vm. - Mitigation: use dedicated edge-vm health endpoint that checks router TCP [PRIVATE_IP]:5080 and returns HTTP 200/503 for Homepage.",
      "rule_like": true,
      "signature": "5daeb3cf0b7d3bf64ddf6ca87e958f73d0715672617706dee233fd739fb08c2c"
    },
    {
      "id": "ERR-H-L267-HOMEPAGE-ROUTER-MOSCOW-YAML-TITLE-SHAPE-MISMATCH-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 267,
      "title": "HOMEPAGE_ROUTER_MOSCOW_YAML_TITLE_SHAPE_MISMATCH_20260701",
      "summary": "- Context: applying Moscow Router Homepage siteMonitor health endpoint. - Mistake: assistant apply script matched only property-style cards with `title`, but active Homepage YAML can use service-name-key style like `- Service Name:`. - Evidence: proof 407 returned YAML_CHANGE_COUNT=0 and APPLY_SKIPPED=unexpected_match_count while health endpoint proof 406 was OK. - Recovery: use YAML-aware updater that handles both property-style and service-name-key style cards.",
      "rule_like": false,
      "signature": "d7ed6a0ecfb19584319e97626e76661541c675e9125e4bf4ff0a407c9718cdd0"
    },
    {
      "id": "ERR-H-L273-HOMEPAGE-ROUTER-MOSCOW-APPLY-SCRIPT-SYNTAX-ERROR-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 273,
      "title": "HOMEPAGE_ROUTER_MOSCOW_APPLY_SCRIPT_SYNTAX_ERROR_20260701",
      "summary": "- Context: applying Moscow Router Homepage health endpoint. - Mistake: assistant generated Python script with invalid f-string escaping. - Evidence: proof 408 showed SyntaxError in /tmp/homepage-router-moscow-apply-fixed.py. - Actual impact: YAML was not changed, so Homepage green dot could not appear. - Recovery: stop applying blind updater; first discover exact active YAML shape, then apply minimal YAML-aware edit. - Rule: validate generated Python syntax locally before scp/remote execution; avoid f-strings in generated maintenance scripts.",
      "rule_like": true,
      "signature": "057b7e036316f6f240cac06b9f46e98d8399df689f82b903cc9ef630afe09f94"
    },
    {
      "id": "ERR-H-L281-FORUM-PROD-VM160-FIRST-BOOT-SSH255-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 281,
      "title": "FORUM_PROD_VM160_FIRST_BOOT_SSH255_20260701",
      "summary": "- Context: clean rebuilt VM160 first boot. - Symptom: PING_OK=yes and TCP22_OK=yes, but SSH_RC=255. - Rule: do not proceed with OS baseline until SSH failure is diagnosed; likely old known_hosts key or cloud-init/root-key issue.",
      "rule_like": true,
      "signature": "5e0359dc6b12e39f533e9060876f5c5f830b43cf2dbbe0441d6876fee5f410a3"
    },
    {
      "id": "ERR-H-L286-FORUM-PROD-VM160-NESTED-HOSTNAME-PROOF-QUOTING-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 286,
      "title": "FORUM_PROD_VM160_NESTED_HOSTNAME_PROOF_QUOTING_20260701",
      "summary": "- Context: VM160 first SSH proof after rebuild. - Issue: command substitution $(hostname) inside nested ssh was expanded on pve02 before entering VM160. - Impact: VM_HOSTNAME=pve02 in proof 426 is not valid VM identity evidence. - Rule: for nested SSH identity checks, run literal hostname commands without local command substitution.",
      "rule_like": false,
      "signature": "c7174e4703945abd82004fea3fa9da0ff3efa1f2a89e6d2315cacf9c27b6d761"
    },
    {
      "id": "ERR-H-L292-FORUM-PROD-VM160-SWAP-PROOF-QUOTING-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 292,
      "title": "FORUM_PROD_VM160_SWAP_PROOF_QUOTING_20260701",
      "summary": "- Context: VM160 swapfile proof 429. - Issue: nested command substitution expanded on pve02, producing invalid SWAPFILE/FSTAB proof lines. - Impact: proof 429 is not valid closure evidence even though swap was active. - Rule: avoid nested $(...) in VM SSH proofs; use literal remote commands and clean proof.",
      "rule_like": true,
      "signature": "3dc2ea2b0a54adb8886979de4e01cb5f23704446f5b2198af9bdacc951359e4c"
    },
    {
      "id": "ERR-H-L298-FORUM-UPLOAD-ARCHIVES-VM-VERIFY-QUOTING-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 298,
      "title": "FORUM_UPLOAD_ARCHIVES_VM_VERIFY_QUOTING_20260701",
      "summary": "- Context: proof 446 copy/check archives inside VM160. - Issue: nested SSH quoting expanded shell variables incorrectly, producing gzip checks against empty .gz and blank TAR_TOP lines. - Impact: SCP_TO_VM_RC=0 and file size list are valid, but GZIP/TAR proof lines in 446 are invalid. - Rule: verify VM archive integrity locally inside forum-prod, not through nested quoted loops.",
      "rule_like": false,
      "signature": "0e4a3a2f7c179f46c235e1ffa176b57485c88b4f7e7314bb89b6acb6683fe448"
    },
    {
      "id": "ERR-H-L304-FORUM-PROD-BULK-IMPORT-PHP85-EMPTY-FRONTEND-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 304,
      "title": "FORUM_PROD_BULK_IMPORT_PHP85_EMPTY_FRONTEND_20260701",
      "summary": "- Context: rebuilt VM160, five XenForo forums imported in bulk on PHP 8.5.7. - Symptom: all five forums returned HTTP 200 with empty body; XenForo logged Template public:PAGE_CONTAINER/forum_list is unknown. - Additional failed approach: proof 480 ran xf-dev:rebuild-caches on CodeVipe and returned RC=1, not a valid fix. - Decision: stop bulk repair, treat current VM160 as lab, find a working CodeVipe recipe first, then destroy/rebuild cleanly for all five forums. - Rule: no more mass rebuild/repair commands across all forums until single-forum CodeVipe recipe is proven.",
      "rule_like": false,
      "signature": "ea3c74648eece7c2a94559d8f7385ea4a126b399e3a161c5113e69a7b8d05f93"
    },
    {
      "id": "ERR-H-L311-XENFORO-REQUIREMENTS-DIRECT-DOWNLOAD-403-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 311,
      "title": "XENFORO_REQUIREMENTS_DIRECT_DOWNLOAD_403_20260701",
      "summary": "- Context: Tried to download XenForo requirements ZIP directly from xenforo.com inside forum-prod. - Issue: curl returned HTTP 403; unzip then failed because the ZIP was not downloaded. - Evidence: proof 491 showed DOWNLOAD_RC=22, ZIP_LIST_RC=9, UNZIP_RC=9. - Impact: proof 491 is not a valid server compatibility test. - Rule: use operator-uploaded xenforo23-requirements-test.zip from /root/forum-upload instead of relying on direct curl download.",
      "rule_like": false,
      "signature": "13164e6750f679553f0bb7cb13f4b895f345dc3f76c978349dfd30b93294206b"
    },
    {
      "id": "ERR-H-L318-PVE01-UNZIP-MISSING-FOR-XF-REQUIREMENTS-VERIFY-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 318,
      "title": "PVE01_UNZIP_MISSING_FOR_XF_REQUIREMENTS_VERIFY_20260701",
      "summary": "- Context: proof 492 tried to inspect uploaded xenforo23-requirements-test.zip on pve01. - Issue: pve01 does not have unzip installed, so ZIP_TEST_RC=127 was not a ZIP integrity result. - Impact: proof 492 confirmed file presence only, not archive validity. - Rule: verify ZIP with Python zipfile or inside forum-prod rather than installing unzip on Proxmox just for inspection.",
      "rule_like": false,
      "signature": "341e814b6c0aab80d0bde631353118c8c04593d918a2ed7b60551d5bc3043148"
    },
    {
      "id": "ERR-H-L324-FRESH5-DEPLOY-SUCCESS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 324,
      "title": "FRESH5_DEPLOY_SUCCESS_20260701",
      "summary": "- Context: VM160 was destroyed/recreated, then five XenForo forums were deployed from fresh ZIP backups. - Result: proof 513 confirms all five forums locally healthy. - Note: proof 511 initially failed dsmods path detection; proof 512 fixed dsmods by locating public_html via src/XF.php marker. - Rule: future restores should preserve internal_data/code_cache or rebuild compiled template cache before smoke testing.",
      "rule_like": false,
      "signature": "f46e9779cb65eebc42015f7042a4f55f0a8aa1e59e647eb29d728e48ed6069b3"
    },
    {
      "id": "ERR-H-L330-NPMPLUS-SQLITE-PASTE-FAILURE-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 330,
      "title": "NPMPLUS_SQLITE_PASTE_FAILURE_20260701",
      "summary": "- Context: NPMplus SQLite inspection command was too complex and a Python fragment was pasted into bash. - Issue: shell entered multiline prompt and produced syntax errors. - Impact: do not trust that SQLite inspection attempt. - Rule: avoid nested SSH heredoc/Python for this task; use simpler commands, generated nginx configs, or uploaded/local script files.",
      "rule_like": true,
      "signature": "d1f2dda1b36bc12803264780b71545d7d9b00c4bdb88ef2fa85405e9d27cdf6f"
    },
    {
      "id": "ERR-H-L336-NPMPLUS-PUBLISH-SCRIPT-WRONG-ENV-SOURCE-525-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 336,
      "title": "NPMPLUS_PUBLISH_SCRIPT_WRONG_ENV_SOURCE_525_20260701",
      "summary": "- Context: proof 525 tried to create forum proxy hosts in NPMplus. - Issue: script sourced /opt/npmplus/.env, but INITIAL_ADMIN_EMAIL/PASSWORD were not present there; they are in the npmplus container env. - Impact: no forum proxy hosts were created by proof 525. - Rule: read NPMplus API login values from docker inspect env internally, never print them.",
      "rule_like": true,
      "signature": "58aa4eeee2841a42e270226a408177af9a099631e2a47857c825707b2ac523ff"
    },
    {
      "id": "ERR-H-L342-NPMPLUS-API-LOGIN-MISSED-HTTPS81-AND-JSON-ESCAPE-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 342,
      "title": "NPMPLUS_API_LOGIN_MISSED_HTTPS81_AND_JSON_ESCAPE_20260701",
      "summary": "- Context: NPMplus API login attempts in proofs 526/527 failed. - Issue: commands missed the likely HTTPS admin endpoint on port 81 and built JSON by string interpolation instead of jq escaping. - Impact: no proxy hosts were created by 526/527. - Rule: for NPMplus API use jq-generated JSON and test https://127.0.0.1:81/api/tokens with -k before publish.",
      "rule_like": false,
      "signature": "141ce6d2777003569c531b5308e56cffaea92dc113fdb0c9e9ebe45815805101"
    },
    {
      "id": "ERR-H-L348-NPMPLUS-API-AUTH-UNAVAILABLE-MANUAL-ROUTES-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 348,
      "title": "NPMPLUS_API_AUTH_UNAVAILABLE_MANUAL_ROUTES_20260701",
      "summary": "- Context: NPMplus API login attempts failed even though the forum VM and edge-to-forum connectivity are healthy. - Issue: API credentials from container initial env are not accepted by current NPMplus API. - Impact: do not use NPMplus API for this publish path. - Rule: publish forum routes through backed-up manual nginx proxy_host configs, then issue proper certificates after DNS points to edge.",
      "rule_like": true,
      "signature": "d5a4f4c6312277e9e2c1487b38c4bf554f4b9618fad9e2c5328fe8324e26a555"
    },
    {
      "id": "ERR-H-L354-EDGE-CERTBOT-TOKEN-FILE-PERMISSION-535-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 354,
      "title": "EDGE_CERTBOT_TOKEN_FILE_PERMISSION_535_20260701",
      "summary": "- Context: proof 535 attempted DNS-01 certificate issue for five forum domains. - Issue: /tmp/forum_cf_token.env was root-owned mode 600, so debian user could not source it directly. - Impact: certbot ran without token and all certificate attempts failed; DNS cutover must not proceed from proof 535. - Rule: read Cloudflare token via sudo without printing it, then create temporary certbot credentials file and remove it after use.",
      "rule_like": true,
      "signature": "4566b4ef97181c31efcb32eefd3ed52633883fc929e98d4607a6c48671434582"
    },
    {
      "id": "ERR-H-L360-EDGE-CERTBOT-FAILED-DNS-CUTOVER-ABORTED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 360,
      "title": "EDGE_CERTBOT_FAILED_DNS_CUTOVER_ABORTED_20260701",
      "summary": "- Context: edge manual routes are working locally, but certbot DNS-01 certificate issue failed in proof 538. - Issue: all five certbot attempts returned RC=1; proof 539 correctly aborted DNS cutover because edge cert precheck was not ready. - Impact: Cloudflare DNS records were not changed by proof 539; public DNS still points to old IP 87.236.18.45. - Rule: inspect certbot logs and Cloudflare token zone permissions before retrying certificate issuance or DNS cutover.",
      "rule_like": false,
      "signature": "7fc8883c0f715d038cc77e38a9088d6cb18290d2456232e9e0a13b1ba094d256"
    },
    {
      "id": "ERR-H-L366-CLOUDFLARE-TOKEN-NO-ZONE-ACCESS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 366,
      "title": "CLOUDFLARE_TOKEN_NO_ZONE_ACCESS_20260701",
      "summary": "- Context: Cloudflare token discovered on edge verified as a token but could not access forum zones. - Evidence: proof 542 showed all five zones missing and DNS record create probes failed. - Impact: certbot DNS-01 failed and DNS cutover was correctly aborted; public DNS still points to old hosting. - Rule: install a new Cloudflare token with Zone Read and DNS Edit for the five forum zones before retrying certs/DNS.",
      "rule_like": false,
      "signature": "3ba4fdbb1dcc5d0beeb215153e4471c19e29f4080ff3d061e77154f44de9a898"
    },
    {
      "id": "ERR-H-L372-FORUM-PUBLICATION-FINAL-SUCCESS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 372,
      "title": "FORUM_PUBLICATION_FINAL_SUCCESS_20260701",
      "summary": "- Context: five XenForo forums were rebuilt on VM160, routed through edge NPMplus, issued Let’s Encrypt certs using a corrected Cloudflare token, and cut over in DNS. - Result: final public proof 546 passed. - Important corrections: initial NPMplus API route failed; manual Nginx routes were used. Initial Cloudflare token had no zone access; corrected token passed zone/DNS probe.",
      "rule_like": false,
      "signature": "bec7980ce2230b8ecf69cfc694cc56bbd3dedc45da55ed2df567e8f41b228638"
    },
    {
      "id": "ERR-H-L377-FINAL-SNAPSHOT-NAME-TOO-LONG-556-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 377,
      "title": "FINAL_SNAPSHOT_NAME_TOO_LONG_556_20260701",
      "summary": "- Context: proof 556 final health gate passed for all five public forums. - Issue: Proxmox snapshot failed because generated snapshot name exceeded 40 characters. - Evidence: qm snapshot returned snapname value may only be 40 characters long. - Impact: forum health was OK, but proof 556 snapshot step was not completed. - Fix: rerun snapshot with short name.",
      "rule_like": false,
      "signature": "c3d65c37278c1e067af0f6dc1d7eb36fa6e75b03113162e66adf06c483bedc76"
    },
    {
      "id": "ERR-H-L384-CLOUDFLARE-DNS-AUDIT-TXT-SUBSTRING-GAP-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 384,
      "title": "CLOUDFLARE_DNS_AUDIT_TXT_SUBSTRING_GAP_20260701",
      "summary": "- Context: prior Cloudflare DNS audit checked web A/AAAA/CNAME state for root/www records. - Issue: it did not deeply inspect TXT/SPF contents for old provider IP substrings. - Evidence: user screenshot showed SPF TXT containing old 87.236.18.* value. - Impact: web routing is OK, but mail-related DNS may still contain stale provider data. - Rule: perform full DNS record audit including TXT/SPF/DKIM/DMARC/MX/CDN and substring search for old IPs before DNS cleanup.",
      "rule_like": false,
      "signature": "260c1f016ae9bf9efb5888bb54809e742681b512223781efac0a4cc8d8cdf2c7"
    },
    {
      "id": "ERR-H-L391-CLOUDFLARE-DNS-CLEANUP-DRYRUN-QUOTE-563-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 391,
      "title": "CLOUDFLARE_DNS_CLEANUP_DRYRUN_QUOTE_563_20260701",
      "summary": "- Context: SPF cleanup dry-run command 563 had broken shell quoting and left the terminal at multiline prompt. - Impact: proof 563 is invalid and no DNS cleanup was performed by it. - Rule: use copied scripts for Cloudflare DNS mutation instead of fragile inline nested quotes.",
      "rule_like": false,
      "signature": "3837def1e15ccdc91af766587f44bd7e98885fb25b88ca3d62e65d13698f62d1"
    },
    {
      "id": "ERR-H-L396-SPF-DUPLICATE-AFTER-565-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 396,
      "title": "SPF_DUPLICATE_AFTER_565_20260701",
      "summary": "- Context: SPF cleanup command 565 attempted to replace stale SPF records. - Issue: backup write permission problem caused record detection failure, so new v=spf1 -all records were created while old v=spf1 ip4:87.236.18.45 records remained. - Impact: domains temporarily had duplicate SPF records, which is invalid for mail validation. - Fix: delete all SPF TXT records for the five forum zones, then create exactly one v=spf1 -all TXT per zone.",
      "rule_like": false,
      "signature": "4a4b1cba0ac945aef40797821d7a9a774728f30fb5e56c34599575e1b5b40ba8"
    },
    {
      "id": "ERR-H-L402-MSMTP-SECRET-SOURCE-PARSE-ERROR-576-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 402,
      "title": "MSMTP_SECRET_SOURCE_PARSE_ERROR_576_20260701",
      "summary": "- Context: proof 576 installed msmtp but sendmail auth test failed. - Issue: [SENSITIVE_PATH] was sourced as shell, but SMTP password contained shell-special characters; source failed and auth used an invalid/truncated secret path. - Impact: msmtp package installed, but mail sending was not proven working. - Rule: never source SMTP secret files containing arbitrary passwords; store password base64 and use msmtp passwordeval helper accessible to www-data.",
      "rule_like": true,
      "signature": "6288f1fcabcdeda1884be858bea052ff9dbb35a65054f5cff6315a350dd7ef12"
    },
    {
      "id": "ERR-H-L408-FORUM-MSMTP-MAIL-TRANSPORT-STILL-FAILING-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 408,
      "title": "FORUM_MSMTP_MAIL_TRANSPORT_STILL_FAILING_20260701",
      "summary": "- Context: attempted to fix msmtp config with passwordeval helper. - Issue: one or more sendmail/PHP mail tests still failed. - Impact: XenForo mail sending is not yet proven. - Next step: verify SMTP credential/password and provider policy for noreply@pvepro.ru.",
      "rule_like": false,
      "signature": "31d614a3bb69ec05a33a5be14388347a438e13f1e28a7ec58207273159e00eae"
    },
    {
      "id": "ERR-H-L414-SMTP-PASSWORD-EXPOSED-ROTATE-REQUIRED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 414,
      "title": "SMTP_PASSWORD_EXPOSED_ROTATE_REQUIRED_20260701",
      "summary": "- Context: SMTP password was exposed in terminal output during failed msmtp setup. - Cause: secret file with raw SMTP_PASS was later parsed/sourced by shell; shell error printed the raw assignment line. - Impact: treat that SMTP password as compromised. - Required action: rotate the mailbox/app password in the mail provider panel before any further SMTP testing. - Rule: never store arbitrary passwords as shell-sourceable KEY=value files; use non-printing secret storage only.",
      "rule_like": true,
      "signature": "88ae0aeb13d948e6f2cb2d83dc2a9464e4de1805cdb4271cf78f3d315f7095c9"
    },
    {
      "id": "ERR-H-L421-NETBIRD-MAILCOW-REPORTED-DOWN-AFTER-SMTP-INCIDENT-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 421,
      "title": "NETBIRD_MAILCOW_REPORTED_DOWN_AFTER_SMTP_INCIDENT_20260701",
      "summary": "- Context: user reported NetBird and Mailcow became unavailable after forum mail-transport work. - Impact: treat as active incident until service reachability and container/VM state are proven. - Rule: do not change DNS, firewall, proxy, mail, NetBird, or Mailcow configs before read-only triage proof.",
      "rule_like": true,
      "signature": "b0eef26c0503e63d8a8c5a294ea1e124ab5e6ac8936d2ccaf23cd9b74fc5ebf3"
    },
    {
      "id": "ERR-H-L426-FORUM-SMTP-FAILED-CONFIG-DISABLED-AFTER-MAILCOW-BAN-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 426,
      "title": "FORUM_SMTP_FAILED_CONFIG_DISABLED_AFTER_MAILCOW_BAN_20260701",
      "summary": "- Context: forum-prod SMTP/msmtp test used invalid or compromised credentials and triggered Mailcow netfilter warnings/ban for 95.84.154.183. - Impact: forum mail transport is not proven and must remain disabled until SMTP password is rotated and tested once carefully. - Action: remove forum-prod msmtp secret/config files so XenForo cannot keep retrying broken SMTP auth. - Rule: do not test SMTP auth again until a fresh rotated mailbox/app password is available.",
      "rule_like": true,
      "signature": "70dc70d8c15e3f30611c45123bf2c3b27c86bf66d703df3e4232057d380801d6"
    },
    {
      "id": "ERR-H-L432-FORUM-OLD-CODEVIPE-CLOUD-BACKUP-STALE-AFTER-FRESH5-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 432,
      "title": "FORUM_OLD_CODEVIPE_CLOUD_BACKUP_STALE_AFTER_FRESH5_20260701",
      "summary": "- Context: pve02 codevipe-cloud-backup.timer existed before fresh five-forum rebuild. - Issue: old script points to /root/backups/codevipe and old remote path app/codevipe/forum-prod/local. - Evidence: latest service run failed with missing /root/backups/codevipe; remote inventory contains old codevipe-public/codevipe.sql files, not current five-forum archive. - Impact: old timer must not be treated as valid current backup for all five forums. - Fix: create a new forum-prod fresh5 cloud backup using existing pve02 Mail-cloud rclone crypt remotes.",
      "rule_like": true,
      "signature": "3baaa9a93ec07abd251041838b9602a52b143ef8e07a03dde6928db27eb2b44f"
    },
    {
      "id": "ERR-H-L439-FORUM-RESTORE-DRILL-610-STATUS-FLAG-BUG-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 439,
      "title": "FORUM_RESTORE_DRILL_610_STATUS_FLAG_BUG_20260701",
      "summary": "- Context: restore drill proof 610 downloaded Mail.ru Cloud backup, verified split part SHA256, reconstructed archive, matched full archive SHA256, and confirmed all five forum DB/file archives in tar. - Issue: final RESTORE_DRILL_OK was set to no because the script tested an unassigned shell variable RCLONE_REMOTE_PVE02_MAIL_01_RC. - Impact: proof 610 final status flag is invalid, but its detailed integrity checks are valid. - Fix: rerun corrected restore validation against the downloaded cloud backup artifacts and produce proof 612.",
      "rule_like": false,
      "signature": "fd5f6f353d76ced7d2deb5e924a183c516c80e7329d8b9d5c4be0627b5b92d41"
    },
    {
      "id": "ERR-H-L445-FORUM-XENFORO-MAIL-SMOKE-TEST-FAILED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 445,
      "title": "FORUM_XENFORO_MAIL_SMOKE_TEST_FAILED_20260701",
      "summary": "- XenForo-level mail smoke test did not return success for all five forums. - Check proof 623 and msmtp log before retrying.",
      "rule_like": false,
      "signature": "c48950d0c079bf801e38017835444fdd04a5354397908bbd864f625e7a917b3d"
    },
    {
      "id": "ERR-H-L449-XENFORO-MAIL-SMOKE-623-INVALID-ROOT-PATH-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 449,
      "title": "XENFORO_MAIL_SMOKE_623_INVALID_ROOT_PATH_20260701",
      "summary": "- Context: custom XenForo mail smoke test 623 failed with Could not open input file /root/scripts/623_xenforo_mail_smoke.php. - Cause: the script was run as www-data from /root/scripts; /root is not traversable by www-data. - Impact: proof 623 is invalid and should not be used to judge mail delivery. - Superseding evidence: user ran XenForo built-in outgoing email test; message was delivered to Yandex but placed in Spam. - Next step: investigate deliverability/authentication headers and pvepro.ru SPF/DKIM/DMARC/PTR.",
      "rule_like": false,
      "signature": "ad9141334766e53e58aef9b0fb014ebfbfe556d3d36615b5f4754e2b29cf36fd"
    },
    {
      "id": "ERR-H-L456-FORUM-NEXT-CHAT-KNOWN-ERRORS-AND-CLOSED-INCIDENTS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 456,
      "title": "FORUM_NEXT_CHAT_KNOWN_ERRORS_AND_CLOSED_INCIDENTS_20260701",
      "summary": "",
      "rule_like": false,
      "signature": "d4866c37a32090d3456bbb2b44824481a232211fdef57833b7f7bec57c065690"
    },
    {
      "id": "ERR-H-L458-CLOSED-CLASSIFIED-INCIDENTS",
      "kind": "heading",
      "level": 3,
      "source_line": 458,
      "title": "Closed / classified incidents",
      "summary": "- SMTP_PASSWORD_EXPOSED_ROTATE_REQUIRED_20260701: - A previous bad command sourced a raw SMTP secret and printed it. - Treat old password as compromised. - Later persistent SMTP was rebuilt using safe files and verified. - Never print or package secrets. - XENFORO_MAIL_SMOKE_623_INVALID_ROOT_PATH_20260701: - Custom mail proof 623 failed with \"Could not open input file\". - Cause: PHP was run as www-data from /root/scripts, but /root is not traversable by www-data.",
      "rule_like": true,
      "signature": "eafc804db9ff3f7b3b68c10318b53db82160fe7951c6e68c4301703c2b7abe41"
    },
    {
      "id": "ERR-H-L491-CURRENT-NON-BLOCKING-ITEMS",
      "kind": "heading",
      "level": 3,
      "source_line": 491,
      "title": "Current non-blocking items",
      "summary": "- Yandex placed built-in XenForo test mail in Spam despite SPF pass and DKIM pass. - Classification: deliverability/reputation/content filtering, not server failure. - Recommended manual action: click “Не спам!” and add/check pvepro.ru in Yandex Postmaster.",
      "rule_like": false,
      "signature": "bc80a4393b6c0958e5753d3114c2ff3e0554b960a6fed7b7829625f02a9d595a"
    },
    {
      "id": "ERR-H-L496-SAFETY-RULES-FOR-NEXT-CHAT",
      "kind": "heading",
      "level": 3,
      "source_line": 496,
      "title": "Safety rules for next chat",
      "summary": "- Do not print secrets. - Do not download or upload: - [SENSITIVE_PATH] - /etc/msmtprc - /etc/msmtp/* - rclone configs - Cloudflare tokens - DB dumps",
      "rule_like": true,
      "signature": "e95262ee083c9c3d1b8587e182598186f1be23e5db9229b5aece7ea336fa6ca5"
    },
    {
      "id": "ERR-H-L509-PARKED-DOMAINS-STAGE4-DNS01-PREFLIGHT-FAILED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 509,
      "title": "PARKED_DOMAINS_STAGE4_DNS01_PREFLIGHT_FAILED_20260701",
      "summary": "- Context: parked-domain public apply proof 634. - Issue: DNS-01 based apply was attempted before a clear CAN_APPLY=yes preflight result was visible in terminal output. - Observed: stage4 stopped safely with preflight_failed; CERTBOT_PRESENT=no, CERTBOT_DNS_CLOUDFLARE_PLUGIN=no, and CF_ZONE_ACCESS=no for newfi.ru, hapusya.ru and kingofwolk.ru. - Impact: no parked-domain NPMplus route/certificate changes were applied by stage4; public HTTPS validation still failed certificate verification. - Rule: do not use Cloudflare DNS-01 for these parked domains unless a token with explicit zone access is installed; because DNS already points to edge, prefer a copied-script HTTP-01 flow with temporary ACME challenge routes and content-specific validation.",
      "rule_like": true,
      "signature": "9a52142ed4109dad52b9391a4a479df84e258ff703bfd8904e4ec5947589cd35"
    },
    {
      "id": "ERR-H-L516-PARKED-DOMAINS-STAGE5-BASH-LOCAL-SETU-BUG-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 516,
      "title": "PARKED_DOMAINS_STAGE5_BASH_LOCAL_SETU_BUG_20260701",
      "summary": "- Context: parked-domain HTTP-01 apply proof 635. - Issue: edge script used Bash `local id=\"$1\" ... conf=\"$WORK/.../$id.conf\"` and `local host=\"$1\" ... tmp=\"$WORK/.../$host.html\"` under `set -u`; dependent variables are not safe inside the same local assignment command. - Observed: `id: unbound variable`, `source can not be empty`, then `host: unbound variable`; certbot did not run, final HTTPS routes were not installed. - Impact: local parked page remained OK; public HTTPS remained not closed. - Rule: split dependent Bash local assignments into separate statements, syntax-check copied scripts before execution, and keep content-specific validation for parked routes.",
      "rule_like": false,
      "signature": "c5a97f3f8380803056dcbb2abd47a72f37609e59655bbd63dec7695e4b5e86e0"
    },
    {
      "id": "ERR-H-L523-PARKED-DOMAINS-STAGE6-TEMP-HTTP-ROOT-VALIDATION-BUG-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 523,
      "title": "PARKED_DOMAINS_STAGE6_TEMP_HTTP_ROOT_VALIDATION_BUG_20260701",
      "summary": "- Context: parked-domain HTTP-01 fixed apply proof 636. - Issue: temporary HTTP-01 route validation checked HTTP `/` for parked marker, but expected behavior before certificates is root HTTP 301 to HTTPS. - Observed: LOCAL_HTTP_VALIDATE returned HTTP_CODE=301 MARKER_OK=no, script rolled back its temporary route configs and did not run certbot. - Impact: local parked page remained OK; public HTTPS remained not closed. - Rule: validate `/.well-known/acme-challenge/<token>` for HTTP-01 readiness, not HTTP `/`; also scan all active NPMplus nginx config dirs for unmanaged domain conflicts before writing routes.",
      "rule_like": false,
      "signature": "dfb310ca0c4e8c9d03ea17ec7e7c5821f16f7dde5c3b0d217d1ed361f61a8740"
    },
    {
      "id": "ERR-H-L530-PARKED-DOMAINS-STAGE7-NEWFI-ROOT-ACME-REDIRECT-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 530,
      "title": "PARKED_DOMAINS_STAGE7_NEWFI_ROOT_ACME_REDIRECT_20260701",
      "summary": "- Context: parked domains HTTP-01 Stage7 stopped before certbot because ACME validation path returned 301 for root newfi.ru while www.newfi.ru, hapusya.ru, www.hapusya.ru, kingofwolk.ru and www.kingofwolk.ru returned 200. - Impact: no final parked-domain HTTPS routes/certificates were applied by Stage7; temporary route configs were rolled back. - Rule: do not run another apply stage until active NPMplus nginx route selection for newfi.ru is diagnosed with nginx -T and Host-header probes.",
      "rule_like": true,
      "signature": "65ab366077a7e1afb168ff94f8b1b7a8b788e1bdca76e6484300ce76c03431f9"
    },
    {
      "id": "ERR-H-L535-PARKED-DOMAINS-STAGE8-DEFAULT-HTTP-TO-HTTPS-REDIRECT-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 535,
      "title": "PARKED_DOMAINS_STAGE8_DEFAULT_HTTP_TO_HTTPS_REDIRECT_20260701",
      "summary": "- Context: parked-domain route autopsy proof 638. - Finding: no active NPMplus config contains newfi.ru/hapusya.ru/kingofwolk.ru, but unknown HTTP hosts and ACME paths return 301 to HTTPS from NPMplus default routing. - Impact: HTTP-01 cannot work through the default server. A dedicated temporary ACME server block must be proven before certbot; do not run another certbot apply without a successful ACME-path probe for all six hostnames. - Rule: after every parked-domain temp route attempt, rollback the route and prove rollback before continuing.",
      "rule_like": true,
      "signature": "fb5de90cf615657074d466c74a748ae250def09fa8ad84bedafdc765a6682be2"
    },
    {
      "id": "ERR-H-L541-PARKED-DOMAINS-STAGE9-NEWFI-EXACT-PROBE-NEEDED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 541,
      "title": "PARKED_DOMAINS_STAGE9_NEWFI_EXACT_PROBE_NEEDED_20260701",
      "summary": "- Context: parked domains HTTP-01 probe stage9 installed one combined temporary server block for all six hostnames. - Issue: five hostnames returned ACME probe 200, but root newfi.ru still returned 301. - Impact: do not continue certbot/apply until exact newfi.ru route behavior is isolated. - Rule: run a temporary exact newfi.ru probe block and roll it back before any HTTP-01 apply.",
      "rule_like": true,
      "signature": "b6335dafc9b854cc8c775bb66b93a4f927e15e4d1b50bb1b33eed285902c43ac"
    },
    {
      "id": "ERR-H-L547-PARKED-DOMAINS-STAGE10-EXACT-NEWFI-PROBE-OK-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 547,
      "title": "PARKED_DOMAINS_STAGE10_EXACT_NEWFI_PROBE_OK_20260701",
      "summary": "- Context: parked-domain Stage10 proof 640. - Result: exact server block for newfi.ru on NPMplus intercepted both root and ACME paths with HTTP 200 and custom X-Parked-Probe markers. - Stage9 combined server_name block passed 5/6 but failed bare newfi.ru, so final HTTP-01 cutover must use separate exact per-host server blocks. - Rule: do not use one combined parked-domain server_name block for these domains; use exact host blocks and content-specific validation.",
      "rule_like": true,
      "signature": "6c28ff0028785a70150cd3be2574ea8ed678ed0e5d8838b93feef353f8d26e7b"
    },
    {
      "id": "ERR-H-L553-PARKED-DOMAINS-STAGE11-LOW-ID-EXACT-ROUTE-FAILED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 553,
      "title": "PARKED_DOMAINS_STAGE11_LOW_ID_EXACT_ROUTE_FAILED_20260701",
      "summary": "- Context: parked-domain Stage11 proof 641. - Result: exact per-host route files 700-705 passed nginx -t but bare newfi.ru still hit HTTP->HTTPS redirect on ACME path. - Earlier Stage10 proof showed exact newfi.ru route works when placed at high id 998. - Hypothesis: file/include priority or NPMplus generated ordering makes low id 700 unsuitable for bare newfi.ru in this runtime. - Rule: do not retry parked-domain HTTP-01 with low id 700-705. First prove fixed high-id exact blocks, then apply with those exact ids.",
      "rule_like": true,
      "signature": "11a87298deaf78b478c1a13abcfbfb1cc91f2540eb4fc5fd7daaf3a53b426cf8"
    },
    {
      "id": "ERR-H-L560-PARKED-DOMAINS-SWITCH-TO-DNS01-AFTER-HTTP01-FAILURES-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 560,
      "title": "PARKED_DOMAINS_SWITCH_TO_DNS01_AFTER_HTTP01_FAILURES_20260701",
      "summary": "- Context: parked domains newfi.ru, hapusya.ru and kingofwolk.ru. - HTTP-01 attempts hit NPMplus default HTTP-to-HTTPS redirect/include-order problems. - Stage12 cleanup confirmed temp files 980-985 absent and nginx reload OK. - Rule: do not continue HTTP-01 for these parked domains; use DNS-01 with a dedicated Cloudflare token that has Zone Read and DNS Edit for the three zones.",
      "rule_like": true,
      "signature": "f4fbcb9b2153251384025a82debe093241f087f381f31e999e5de5c66b60d748"
    },
    {
      "id": "ERR-H-L566-PARKED-DOMAINS-STAGE15-FALSE-LOCAL-VALIDATE-ROLLBACK-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 566,
      "title": "PARKED_DOMAINS_STAGE15_FALSE_LOCAL_VALIDATE_ROLLBACK_20260701",
      "summary": "- Context: Stage15 DNS-01 succeeded and public validation showed parked page for most hosts, but local edge curl --resolve validation returned CURL_RC=60 and triggered rollback. - Impact: do not rerun Stage15 as-is. - Rule: for parked domains finalization, use existing DNS-01 certificates and validate from pve01 public HTTPS with content marker; do not use the failed local --resolve TLS check as the closure gate.",
      "rule_like": true,
      "signature": "3b69e4a267b02bdaf0f69e8af7da2cf979ae13223acd5a1eed82f9de1736399d"
    },
    {
      "id": "ERR-H-L571-DOMAIN-ACTIONS-STAGE18-PLACEHOLDER-AND-RENEWAL-PROOF-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 571,
      "title": "DOMAIN_ACTIONS_STAGE18_PLACEHOLDER_AND_RENEWAL_PROOF_20260701",
      "summary": "- Context: after parked domains and portfolio audit, operator requested removing domain enumeration from placeholder and checking certificate renewal paths. - Rule: Stage18 must not change DNS records or NPMplus route files. It may update placeholder HTML, run existing forum renewal script once for runtime proof, and run certbot dry-run for parked certificates using existing DNS-01 hooks.",
      "rule_like": true,
      "signature": "ff7fb700a503f6caecb02ad5f31ed4eefe5678fd9217ef289fc3c906cd291e70"
    },
    {
      "id": "ERR-H-L575-GRAM1-ROOT-WWW-PLACEHOLDER-CLOSE-STAGE19-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 575,
      "title": "GRAM1_ROOT_WWW_PLACEHOLDER_CLOSE_STAGE19_20260701",
      "summary": "- Context: operator requested gram1.ru root/www to use the existing placeholder page. - Rule: only root gram1.ru and www.gram1.ru may be routed. Existing gram1.ru subdomain NPMplus routes must not be changed. - Rule: use DNS-01 with a dedicated gram1 Cloudflare token; do not use HTTP-01. - Stage18 parked dry-run note: kingofwolk.ru dry-run hit Let's Encrypt rateLimited/service-busy after active certificate issuance and public HTTPS were already OK. Treat as transient external dry-run failure, not route/cert closure blocker.",
      "rule_like": true,
      "signature": "9e2adc97128fdb5159168a0ebad49e1808253dcf358cdc908303a1a29d9277b8"
    },
    {
      "id": "ERR-H-L581-PVEPRO-EDGE-LANDING-STAGE21-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 581,
      "title": "PVEPRO_EDGE_LANDING_STAGE21_20260701",
      "summary": "- Context: pvepro.ru root/www currently point to the Mailcow VPS and return Mailcow with TLS hostname mismatch. - Decision: because pve01 has no SSH key access to the external VPS, do not modify the VPS. Instead move only pvepro.ru and www.pvepro.ru A records to edge and serve an edge landing page. - Safety rule: do not change mail.pvepro.ru, nb.pvepro.ru, MX, SPF, DKIM, DMARC or forum SMTP configuration. - Use a dedicated Cloudflare token for pvepro.ru. Do not print token values.",
      "rule_like": true,
      "signature": "da430a8849bebcbd0a5dc55d4282ae916ffb348af757e797b50a144a35c00e29"
    },
    {
      "id": "ERR-H-L587-PVEPRO-STAGE21-LANDING-SERVICE-FAILED-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 587,
      "title": "PVEPRO_STAGE21_LANDING_SERVICE_FAILED_20260701",
      "summary": "- Context: Stage21 stopped safely before DNS/cert/NPMplus route because local pvepro landing service returned CURL_RC=7 on 127.0.0.1:18089. - Impact: DNS root/www pvepro.ru remained on 185.139.214.215 and no route/cert changes were applied. - Rule: Stage22 must fix and prove the local landing service first, then proceed to DNS-01 cert, NPMplus route and root/www DNS only after local marker PVEPRO_LANDING_OK is reachable.",
      "rule_like": false,
      "signature": "6c8c70fd8a9af6ca28d7c2848c52dae8dd0405dfb2758b3341fb7d3c0f24131f"
    },
    {
      "id": "ERR-H-L592-PVEPRO-STAGE22-VALIDATION-AND-COPY-GUARD-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 592,
      "title": "PVEPRO_STAGE22_VALIDATION_AND_COPY_GUARD_20260701",
      "summary": "- Context: Stage22 succeeded on edge-side landing, cert, NPMplus route and Cloudflare A upserts, but wrapper had a non-critical scp wildcard failure due __pycache__ and public validation used cached DNS for root pvepro.ru. - Mistakes recorded: do not scp scripts/* after local py_compile because __pycache__ may be present; do not continue after critical scp failure in future apply stages; do not classify PVEPro landing as NetBird just because it contains a link text; use Cloudflare authoritative A records and --resolve validation when DNS propagation may lag. - Stage23 must be read-only for infrastructure and may close only if edge route, Cloudflare A records, HTTPS landing marker, and mail/nb unchanged checks pass.",
      "rule_like": true,
      "signature": "39b7838d47fba86c663aac0896edc9db58fa614ebdcdbae09fb3054a061ba44f"
    },
    {
      "id": "ERR-H-L597-TAFTAUTO-CERT-AUTORENEW-BLOCKED-NO-PRIVATE-ACCESS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 597,
      "title": "TAFTAUTO_CERT_AUTORENEW_BLOCKED_NO_PRIVATE_ACCESS_20260701",
      "summary": "- Context: taftauto.ru points to the dacha router public IP. Operator confirmed the router model is Netcraze-like, same family as Moscow router. - Current limitation: there is no safe private remote access path to the dacha router yet. - Rule: do not expose router admin publicly and do not attempt certificate deployment to the router over the public internet. - Closure status: certificate autodeploy is intentionally blocked until VPN/NetBird/WireGuard or another private management path exists.",
      "rule_like": true,
      "signature": "9cd06e064f5a86be9626c4f9a2a819b8d0e71b03309c705fe0e34e1e59f81b73"
    },
    {
      "id": "ERR-H-L603-TAFTAUTO-CERT-AUTORENEW-BLOCKED-NO-PRIVATE-ACCESS-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 603,
      "title": "TAFTAUTO_CERT_AUTORENEW_BLOCKED_NO_PRIVATE_ACCESS_20260701",
      "summary": "- Context: taftauto.ru points to the dacha router public IP. Operator confirmed the router model is Netcraze-like, same family as Moscow router. - Current limitation: there is no safe private remote access path to the dacha router yet. - Rule: do not expose router admin publicly and do not attempt certificate deployment to the router over the public internet. - Closure status: certificate autodeploy is intentionally blocked until VPN/NetBird/WireGuard or another private management path exists.",
      "rule_like": true,
      "signature": "9cd06e064f5a86be9626c4f9a2a819b8d0e71b03309c705fe0e34e1e59f81b73"
    },
    {
      "id": "ERR-H-L609-TAFTAUTO-WG-STAGE24-SECRET-AND-IMPORT-RULE-20260701",
      "kind": "heading",
      "level": 2,
      "source_line": 609,
      "title": "TAFTAUTO_WG_STAGE24_SECRET_AND_IMPORT_RULE_20260701",
      "summary": "- Context: configuring private management path for taftauto.ru dacha router. - Rule: do not print WireGuard private keys, PSK, or client config body in proof/chat. - Rule: generated client config may be stored only as root/debian mode 600 file and must be imported into the Netcraze router UI manually. - Rule: do not open router public admin or certificate deployment until WireGuard handshake is proven.",
      "rule_like": true,
      "signature": "6722ad8ce177f600b198fb48046657acb926330bfbd8bbe1c810de69015f5a87"
    },
    {
      "id": "ERR-H-L615-TAFTAUTO-WG-PSK-ROTATION-SCRIPT-BROKE-DATAPLANE-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 615,
      "title": "TAFTAUTO_WG_PSK_ROTATION_SCRIPT_BROKE_DATAPLANE_20260702",
      "summary": "- Assistant gave unsafe PSK rotation flow; after profile reimport dacha interface changed from Wireguard0 to Wireguard1 and data-plane broke until security-level, ACL and routes were restored on Wireguard1. - Proof 666 is superseded because it showed PUBLIC_SSH_22_STILL_OPEN. - Final fixed proof: /root/evidence/667_TAFTAUTO_WG_PSK_ROTATED_PUBLIC_CLOSED_OK_20260702_PROOF.txt",
      "rule_like": false,
      "signature": "4b046c53fa6020f8c97bd7e00a8a7d78280d3ab2bf337816cb559083ba9c7495"
    },
    {
      "id": "ERR-H-L620-TAFTAUTO-CERTBOT-CLOUDFLARE-PLUGIN-MISSING-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 620,
      "title": "TAFTAUTO_CERTBOT_CLOUDFLARE_PLUGIN_MISSING_20260702",
      "summary": "- Certbot on edge rejected --dns-cloudflare-* arguments: dns-cloudflare plugin missing/not loaded. - No taftauto certificate issued in failed run; no DNS A records or NPMplus routes changed.",
      "rule_like": false,
      "signature": "3e7ad3e9ef090a52212334ce2d8c5b65fa7dae68481c01068ec7845bef14a07f"
    },
    {
      "id": "ERR-H-L624-HOMEPAGE-SERVICES-YAML-BAD-INDENT-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 624,
      "title": "HOMEPAGE_SERVICES_YAML_BAD_INDENT_20260702",
      "summary": "- Assistant inserted Homepage services.yaml entries with wrong indentation; YAMLException bad indentation at line 227. - Fix: rebuild SmartApe under External Homelabs with 2-space item indentation, remove Router Moscow, rebuild Sites category.",
      "rule_like": false,
      "signature": "da1ecebf560240f444c3df39efb068b9d7c8ab7ceda2053516e8d82c8567bd7b"
    },
    {
      "id": "ERR-H-L628-HOMEPAGE-SERVICES-YAML-BAD-INDENT-REPAIR-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 628,
      "title": "HOMEPAGE_SERVICES_YAML_BAD_INDENT_REPAIR_20260702",
      "summary": "- Previous apply broke services.yaml indentation and did not follow YAML-aware rule. - Repair uses latest valid services.yaml backup, PyYAML parse/dump, validates before restart.",
      "rule_like": false,
      "signature": "7b789067b17326a4ead14942608a43b86b4ae971896eff93eb8658684e657e22"
    },
    {
      "id": "ERR-H-L632-TAFTAUTO-CERTBOT-DRYRUN-BAD-FLAG-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 632,
      "title": "TAFTAUTO_CERTBOT_DRYRUN_BAD_FLAG_20260702",
      "summary": "- Assistant used unsupported certbot flag --manual-public-ip-logging-ok with certbot 4.0.0 during taftauto renew dry-run. - No certificate, DNS, NPMplus route, or deploy state changed by the failed dry-run.",
      "rule_like": false,
      "signature": "518dbc241694b2eb7c4631a1b147a8262af537e6f4eaf082d1f092cc0a8087cb"
    },
    {
      "id": "ERR-H-L636-TAFTAUTO-CERT-RENEW-DRYRUN-RATE-LIMITED-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 636,
      "title": "TAFTAUTO_CERT_RENEW_DRYRUN_RATE_LIMITED_20260702",
      "summary": "- Proof 672 is superseded/partial: it printed STATUS=OK even though certbot dry-run failed. - Failure reason: Let's Encrypt staging/service returned rateLimited / Service busy; retry later. - Cloudflare manual auth and cleanup hooks did run successfully. - Deploy hook was installed and manually invoked successfully before the dry-run. - Do not retry immediately.",
      "rule_like": true,
      "signature": "15452c7cc7d791a7cc8324a5f2d39544b8a0971fce736a46afdfce7ec8eea5b3"
    },
    {
      "id": "ERR-H-L643-HOMEPAGE-XENFORO-INFO-INLINE-PYTHON-SYNTAX-ERROR-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 643,
      "title": "HOMEPAGE_XENFORO_INFO_INLINE_PYTHON_SYNTAX_ERROR_20260702",
      "summary": "- Assistant used invalid inline Python syntax with def after semicolon while adding XenForo.info to Homepage External. - Failed before services.yaml write.",
      "rule_like": false,
      "signature": "3620b01df453a95e2e7fbd121ee954690344e32ed12c71b84739e1a554b316d2"
    },
    {
      "id": "ERR-H-L647-HOMEPAGE-USEFUL-ROUTER-NAME-MISMATCH-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 647,
      "title": "HOMEPAGE_USEFUL_ROUTER_NAME_MISMATCH_20260702",
      "summary": "- Previous apply looked for Russian Router card name, but active Homepage Useful group contains Router and Public Domain. - Failed before write. Fix uses YAML-aware edit with exact names Router/Public Domain.",
      "rule_like": false,
      "signature": "49212250bc5eea53887790a1782ea3acdc2fa2e7769d89ee79f603883533adf8"
    },
    {
      "id": "ERR-H-L651-HOMEPAGE-USEFUL-ROUTER-CARD-NOT-FOUND-20260702",
      "kind": "heading",
      "level": 2,
      "source_line": 651,
      "title": "HOMEPAGE_USEFUL_ROUTER_CARD_NOT_FOUND_20260702",
      "summary": "- Apply expected Useful -> Router in active services.yaml, but YAML parse did not find it. No write was performed. - Need verify active Homepage config path, docker mount, and any alternate services.yaml before next apply.",
      "rule_like": false,
      "signature": "ce9a93f7876b27d64e76cddc59b220166fa0e82ab7265d3373352906d6e10264"
    },
    {
      "id": "ERR-H-L655-20260702-CF-TOKEN-AUDIT-BROKEN-COMMAND",
      "kind": "heading",
      "level": 2,
      "source_line": 655,
      "title": "20260702_CF_TOKEN_AUDIT_BROKEN_COMMAND",
      "summary": "- Ошибка: ассистент дал хрупкую команду аудита Cloudflare-токенов с вложенным heredoc/Python через ssh. - Факт: bash начал интерпретировать Python-код, появились syntax error и вторичный prompt >. - Правило: не давать nested heredoc/сложные кавычки через ssh; для длинной логики сначала класть скрипт отдельным файлом, затем запускать его. - Статус: аудит токенов не выполнен, инфраструктурные изменения не должны считаться применёнными.",
      "rule_like": false,
      "signature": "00c3ff174b7bd8af54a071aad893fc3e28aae0f79b8cba6babffa67869d0d7c3"
    },
    {
      "id": "ERR-H-L661-LESSON-20260702-CROWDSEC-NETBIRD-EXIT-ROUTE",
      "kind": "heading",
      "level": 2,
      "source_line": 661,
      "title": "LESSON_20260702_CROWDSEC_NETBIRD_EXIT_ROUTE",
      "summary": "- Do not treat e3qxxx.netbird.selfhosted / 100.100.125.70 and e3qxxx-183-106.netbird.selfhosted / 100.100.183.106 as VPS egress nodes; they are user mobile peers. - Correct egress candidates are relay.pvepro.ru / relay.netbird.selfhosted / 100.100.19.1 for Moldova and mail.pvepro.ru / mail.netbird.selfhosted / 100.100.147.204 for USA. - Access Policy edge-vm -> relay/mail only proves peer reachability. It is not an internet exit route. - Required NetBird fix for CrowdSec CAPI was Network Routing exit route 0.0.0.0/0 distributed to edge-vm group, routing peer relay, masquerade/NAT enabled. - Before attempting CrowdSec CAPI, verify on edge-vm: ip route get 1.1.1.1 uses wt0, wg allowed-ips contains 0.0.0.0/0, and Cloudflare trace no longer shows home IP 95.84.154.183.",
      "rule_like": true,
      "signature": "651aa75a20b1bad649519711690f3a5d4f944e71649c31414c966c56c2647f83"
    },
    {
      "id": "ERR-H-L668-LESSON-20260702-HOMEPAGE-MONITORS-REPAIR-NOT-DELETE",
      "kind": "heading",
      "level": 2,
      "source_line": 668,
      "title": "LESSON_20260702_HOMEPAGE_MONITORS_REPAIR_NOT_DELETE",
      "summary": "- Do not delete or disable Homepage siteMonitor fields to hide red badges. - Fix the underlying monitor path, DNS, proxy route, or local health-wrapper instead. - Do not touch Cloudflare Homepage card when operator says it is green and opens correctly. - Before changing Homepage, identify the live config host; in this state live Homepage is on core-apps [PRIVATE_IP], not edge-vm.",
      "rule_like": true,
      "signature": "2bc16e142d368e59b2b176de6779445c152df7e8bae2b1be79a2f4d075985c7c"
    },
    {
      "id": "ERR-H-L674-LESSON-20260702-KUMA-ADD-ONE-MONITOR-ONLY",
      "kind": "heading",
      "level": 2,
      "source_line": 674,
      "title": "LESSON_20260702_KUMA_ADD_ONE_MONITOR_ONLY",
      "summary": "- When asked to fix one missing Uptime Kuma service, add only one monitor, then generate proposals separately. - Before direct Kuma DB mutation, stop the container and create a DB backup. - Verify DB integrity before starting Kuma again. - Do not touch Cloudflare when operator says it is green and opens correctly.",
      "rule_like": true,
      "signature": "b268d57842a9dd192fd72dd940879f3e7cf714ae2928a68a23b5e1ed347262f0"
    },
    {
      "id": "ERR-H-L680-LESSON-20260702-DOCKGE-STALE-STACKS-NOT-CONTAINERS",
      "kind": "heading",
      "level": 2,
      "source_line": 680,
      "title": "LESSON_20260702_DOCKGE_STALE_STACKS_NOT_CONTAINERS",
      "summary": "- Dockge inactive items after migration can be stale compose folders, not stopped containers. - First classify runtime projects across all Docker hosts before deleting or archiving anything. - For remote Dockge visibility, projects must be under /opt/stacks or bind-mounted there. - Do not delete containers to fix Dockge inactive UI; connect agents and archive only confirmed stale moved stack definitions.",
      "rule_like": true,
      "signature": "4249f24d95f2186959586b18fa083cdac97ceddb1cc65b0bd0fd1eeddfdea50d"
    },
    {
      "id": "ERR-H-L686-STAGE4B-MASKED-SQL-QUERY-FAILURE-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 686,
      "title": "STAGE4B_MASKED_SQL_QUERY_FAILURE_20260714",
      "summary": "- Previous Stage4B read-only audit returned empty observation-summary, metadata-key, trigger and function blocks because nested SSH/SQL quoting broke queries while stderr was hidden. - Empty blocks are query failures, not proof that metadata, triggers or functions are absent. - Future PostgreSQL audits must use ON_ERROR_STOP, visible stderr, independent query RC and quote-safe dollar literals.",
      "rule_like": false,
      "signature": "9786689ba74fed176972e94389a37a7664ee81618201ff24852199ed754b694f"
    },
    {
      "id": "ERR-H-L691-STAGE4C-SCHEMA-MIGRATIONS-ID-ASSUMPTION-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 691,
      "title": "STAGE4C_SCHEMA_MIGRATIONS_ID_ASSUMPTION_20260714",
      "summary": "- Stage4C read-only preflight incorrectly assumed schema_migrations has a column named id. - PostgreSQL returned column id does not exist and ON_ERROR_STOP correctly stopped all following SQL checks. - Production DB and application were not changed. Future migration audits must inspect information_schema first and query migration rows without assuming column names.",
      "rule_like": false,
      "signature": "f166dab2251442df1f0e77fb70dfa166f1e681a94567cc0d17eb7e5000466a11"
    },
    {
      "id": "ERR-H-L696-STAGE4C-PGDUMP-DEV-NULL-FSYNC-INVALID-PROBE-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 696,
      "title": "STAGE4C_PGDUMP_DEV_NULL_FSYNC_INVALID_PROBE_20260714",
      "summary": "- Stage4C custom-format pg_dump capability probe incorrectly used /dev/null as the output file. - pg_dump failed only because fsync on /dev/null returned Invalid argument. - Production database and application were not changed. - Future custom dump capability checks must use a real temporary regular file, validate it with pg_restore --list, and remove it afterward.",
      "rule_like": false,
      "signature": "c565b0d0dc04089e68783eb6f6b52e7e66fd66b73eb84b37ea6d7f324ca2142c"
    },
    {
      "id": "ERR-H-L702-STAGE4C-REMOTE-PREP-THIRD-SUBSTEP-FAILED-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 702,
      "title": "STAGE4C_REMOTE_PREP_THIRD_SUBSTEP_FAILED_20260714",
      "summary": "- Isolated migration dry-run did not start: REMOTE_PREP_RC=0|0|1. - Remote directory creation and SCP succeeded; the third ownership/mode preparation substep returned RC=1. - Production database and application were not changed. - Exact cause requires read-only residual directory and permission inspection before cleanup or retry.",
      "rule_like": false,
      "signature": "c048e5da3ee1467f7b4742ae2ef671944078c985979c4c75f87aefe2b13c54ad"
    },
    {
      "id": "ERR-H-L708-STAGE4C-REMOTE-PREP-GLOB-AFTER-CHMOD-ROOT-CAUSE-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 708,
      "title": "STAGE4C_REMOTE_PREP_GLOB_AFTER_CHMOD_ROOT_CAUSE_20260714",
      "summary": "- Exact cause of REMOTE_PREP_RC=0|0|1: after the directory became postgres-owned mode 0700, the unprivileged debian shell could not traverse it and could not expand the wildcard used by chmod. - Candidate files and SHA256 verification were valid; no temporary database was created and production was unchanged. - Future preparation must use exact privileged paths or a verified shared group, never an unprivileged wildcard after restrictive chmod.",
      "rule_like": true,
      "signature": "a174131243eb8db72dedb89bc3e917cb45e7c7c8107e40c90b476d46bf4d2718"
    },
    {
      "id": "ERR-H-L713-STAGE4C-MIGRATION003-FINGERPRINT-DOLLAR-QUOTE-FAILURE-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 713,
      "title": "STAGE4C_MIGRATION003_FINGERPRINT_DOLLAR_QUOTE_FAILURE_20260714",
      "summary": "- Migration 003 isolated dry-run failed at fingerprint CHECK because the regex end anchor collided with the SQL dollar-quote delimiter and produced an unterminated dollar-quoted string. - The migration transaction rolled back, the temporary database was removed, and production remained unchanged. - Fingerprint validation must use length plus translate without a regex dollar anchor; corrected candidate must pass a fresh isolated dry-run before production consideration.",
      "rule_like": false,
      "signature": "adc9a3f845fb0a58a7d9e0196174f3a9c0b5eb905bbd2c2eb456a92eb9f2d37b"
    },
    {
      "id": "ERR-H-L718-STAGE4C-SEAL-OUTER-RC-MASKING-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 718,
      "title": "STAGE4C_SEAL_OUTER_RC_MASKING_20260714",
      "summary": "- Stage4C final seal correctly refused creation because overall health was WARN, returning internal RC=40. - The former wrapper then printed a second misleading COMMAND_RC=0 because top-level true masked the internal result. - Future commands must capture the guarded subshell through PIPESTATUS and print exactly one authoritative COMMAND_RC before the mandatory final true. - Production database, application and services were unchanged.",
      "rule_like": false,
      "signature": "9f71003c157467e9b2bc0405d3137b6f0f7d14e053cb3053e9323219494287a9"
    },
    {
      "id": "ERR-H-L724-STAGE4C-DIAGNOSIS-EXPECTED-DERIVED-FILE-ON-VM180-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 724,
      "title": "STAGE4C_DIAGNOSIS_EXPECTED_DERIVED_FILE_ON_VM180_20260714",
      "summary": "- Previous diagnosis incorrectly treated cluster-admin-restricted-probes.txt as a required source file on VM180. - The file is a central derived health artifact on pve01; VM180 supplies full-observer evidence instead. - This false expectation caused diagnostic RC=32 without any infrastructure failure or production change.",
      "rule_like": false,
      "signature": "d032d83a642f525e4ef82024ba9f810d8b9cbb88a6351da3dbed17391e14056d"
    },
    {
      "id": "ERR-H-L729-STAGE4C-NO-JOBS-RUNNING-TEXT-COUNTED-AS-JOB-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 729,
      "title": "STAGE4C_NO_JOBS_RUNNING_TEXT_COUNTED_AS_JOB_20260714",
      "summary": "- Stage4C seal preflight incorrectly counted the systemctl informational text No jobs running as one active job. - The parser counted every nonempty output line instead of accepting only rows beginning with a numeric systemd Job ID. - No service was started and no production state changed during the blocked attempt. - Future job counts must match a numeric first field only.",
      "rule_like": false,
      "signature": "619b903f2e18ba4251959e200fe4511995e56c4cd9a3e63b5fc8d77134df5ade"
    },
    {
      "id": "ERR-H-L735-STAGE4D-ADAPTER-SOURCE-AUDIT-ABORTED-BEFORE-FINAL-MARKERS-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 735,
      "title": "STAGE4D_ADAPTER_SOURCE_AUDIT_ABORTED_BEFORE_FINAL_MARKERS_20260714",
      "summary": "- The evidence-only Stage4D adapter source audit returned RC=1 before emitting its VM180 completion and final audit markers. - The six wrapper validation failures are cascading missing-marker checks and do not prove six independent infrastructure faults. - Production, database, application, services, timers, health and desired-state were unchanged. - Inspect the preserved runner stdout, stderr and result JSON before correcting or rerunning the task.",
      "rule_like": true,
      "signature": "cc2c72b5d6eb7a0a1f3258050f4509d2e08f538236449d0100a9ee45193da58d"
    },
    {
      "id": "ERR-H-L741-STAGE4D-LOCAL-EXPANSION-OF-REMOTE-Q-UNDER-NOUNSET-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 741,
      "title": "STAGE4D_LOCAL_EXPANSION_OF_REMOTE_Q_UNDER_NOUNSET_20260714",
      "summary": "- The first Stage4D adapter source audit embedded a large remote program inside a locally double-quoted SSH argument. - Local shell expansion reached the remote variable Q while nounset was enabled, causing Q: unbound variable before SSH execution. - VM180 and PostgreSQL audits did not start; production state was unchanged. - Remote shell and SQL programs must be separate syntax-checked files passed through SSH stdin.",
      "rule_like": false,
      "signature": "bdae2b3ceda91c697838a15c35c327d73583cc201d5fc77c2f7355f45c2e340a"
    },
    {
      "id": "ERR-H-L747-STAGE4D-REMOTE-PYTHON311-ADAPTER-SELFTEST-RC1-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 747,
      "title": "STAGE4D_REMOTE_PYTHON311_ADAPTER_SELFTEST_RC1_20260714",
      "summary": "- Canonical adapter candidate passed local Python 3.13 self-tests with 48 envelopes but returned RC=1 on VM180 Python 3.11.2. - Remote upload and cleanup succeeded, and production database remained 0|0|OK. - The exact traceback and failing assertion must be captured by an isolated identical reproduction before changing the candidate.",
      "rule_like": false,
      "signature": "6111533bbd96a60d54d47dff09af497c706e31b90977efb40efc8d5c24bf5ada"
    },
    {
      "id": "ERR-H-L752-STAGE4D-PYTHON311-FSTRING-COMPATIBILITY-AND-REPRO-VALIDATOR-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 752,
      "title": "STAGE4D_PYTHON311_FSTRING_COMPATIBILITY_AND_REPRO_VALIDATOR_20260714",
      "summary": "- Adapter candidate passed Python 3.13 but failed Python 3.11 parsing at Path(row[\"path\"]) inside a double-quoted f-string. - Exact fix is Python 3.11-compatible quoting: Path(row['path']). - The reproduction command captured the expected compile failure but incorrectly returned CAPTURE_BAD=1 because its validator required compile RC=0. - Production database remained 0|0|OK and desired-state remained clean. - Future adapter candidates must pass syntax and full self-tests on both pve01 and VM180 runtimes before acceptance.",
      "rule_like": false,
      "signature": "5aec931e3d68bbdfd0f84e397c8ad1aea4fe7e9d239f066d7186823aaf5a0c33"
    },
    {
      "id": "ERR-H-L759-STAGE4E-DESIGN-VALIDATOR-ABORTED-BEFORE-LOCAL-VALIDATION-MARKER-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 759,
      "title": "STAGE4E_DESIGN_VALIDATOR_ABORTED_BEFORE_LOCAL_VALIDATION_MARKER_20260714",
      "summary": "- Restricted probe-agent design candidate compiled successfully but its local validator exited before producing LOCAL_VALIDATION_RC. - The wrapper validation count is cascading missing-marker evidence and does not represent twenty-one independent faults. - Active external probes were not executed and production state was unchanged. - Inspect the preserved validator traceback and exact assertion before modifying the candidate.",
      "rule_like": false,
      "signature": "fa47b366640ad2b68b1efc2d897808d7e75322d7aa9e3c9af89013e4eecc5a26"
    },
    {
      "id": "ERR-H-L765-STAGE4E-VALIDATOR-SCANNED-PYC-AND-OWN-FORBIDDEN-LITERALS-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 765,
      "title": "STAGE4E_VALIDATOR_SCANNED_PYC_AND_OWN_FORBIDDEN_LITERALS_20260714",
      "summary": "- Stage4E design validator recursively read every candidate file as UTF-8 after py_compile had created binary __pycache__ bytecode. - This caused UnicodeDecodeError before any design assertion failed. - The phase-wide forbidden-pattern scan also included validate_design.py itself, which necessarily contains the prohibited literals it verifies. - Validators must use an explicit text-file allowlist and scans must exclude validator implementation and binary artifacts. - Production, database, services, timers and desired-state were unchanged; active external probes were not executed.",
      "rule_like": true,
      "signature": "76b2888a1b774c0f7e8c0fe3aab0e3beb73c78f9b46ef3694f4708cc3431230c"
    },
    {
      "id": "ERR-H-L772-STAGE4E-V2-VALIDATOR-FAILED-STATIC-COMPILE-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 772,
      "title": "STAGE4E_V2_VALIDATOR_FAILED_STATIC_COMPILE_20260714",
      "summary": "- Stage4E design candidate v2 failed static compilation before local design validation started. - The reported validation count is cascading missing-marker evidence, not twenty-five independent design defects. - The generated validator must be inspected at the exact SyntaxError line before another candidate is created. - Production, database, application, services, timers, health and desired-state were unchanged; active external probes were not executed.",
      "rule_like": false,
      "signature": "0acd318ec69af959e23233f89a6b26cefcce1dbb2c85908fcc2406c51c85db72"
    },
    {
      "id": "ERR-H-L778-STAGE4E-PIPESTATUS-LOST-AFTER-FIRST-ASSIGNMENT-20260714",
      "kind": "heading",
      "level": 2,
      "source_line": 778,
      "title": "STAGE4E_PIPESTATUS_LOST_AFTER_FIRST_ASSIGNMENT_20260714",
      "summary": "- VM180 validation copy used a pipeline and read PIPESTATUS in two separate assignments. - The first assignment reset PIPESTATUS, leaving COPY_RC empty and preventing remote validation and cleanup. - Retry must use explicit SCP operations without pipeline status parsing. - Production remained unchanged and active external probes were not executed.",
      "rule_like": false,
      "signature": "29d5bdfa098352aeceb9defce46f9845d100c46dfc1758f17ad8c5eb56859787"
    },
    {
      "id": "ERR-H-L784-STAGE4F-VALIDATOR-EXPECTED-HEALTH-REFRESH-FROM-NONWRITING-BACKUP-SCRIPT-",
      "kind": "heading",
      "level": 2,
      "source_line": 784,
      "title": "STAGE4F_VALIDATOR_EXPECTED_HEALTH_REFRESH_FROM_NONWRITING_BACKUP_SCRIPT_20260714",
      "summary": "- The controlled backup service completed with Result=success and ExecMainStatus=0. - Validation incorrectly required cluster-admin-incident-engine-backup.txt to receive a fresh timestamp. - The executed backup script does not write that health file; therefore the stale health timestamp was not evidence of backup failure. - Stage4F backup proof must use the service result, fresh latest JSON, produced artifacts and off-host/restore evidence instead. - The backup must not be rerun merely to satisfy the invalid health-file freshness assertion.",
      "rule_like": true,
      "signature": "92516bc4ce3841e809e6ec04f2ec77458c1c472c146af37a577bbeffbce5d19d"
    },
    {
      "id": "ERR-H-L791-STRICT-RULE-20260714-CLOSE-TAILS-IMMEDIATELY",
      "kind": "heading",
      "level": 2,
      "source_line": 791,
      "title": "STRICT_RULE_20260714_CLOSE_TAILS_IMMEDIATELY",
      "summary": "- Failure class: переход к следующей задаче при наличии незакрытого хвоста. - Mandatory anti-regression: после ошибки запрещено продолжать другой scope до точной root cause, исправления, повторной проверки, очистки, proof и seal. - Closure gate: UNRESOLVED_TAIL_COUNT=0; BLOCKER_COUNT=0; TEMPORARY_ARTIFACT_COUNT=0; ROLLBACK_VERIFIED=YES; HEALTH=OK; PROOF_UPDATED=YES; REFERENCE_UPDATED=YES. - Если закрытие невозможно из-за внешней зависимости, опасной неоднозначности или обязательного решения пользователя, фиксировать BLOCKED/OPEN и не заявлять CLOSED. - Proof: /var/lib/homelab-change-sets/operator-rule-close-tails-immediately-v1/20260714T171631Z/report.txt",
      "rule_like": true,
      "signature": "951ac14651bd989701f8f0cdf4a6f683fdbe957a357523c878cef0081fdc8dd7"
    },
    {
      "id": "ERR-H-L798-ERROR-20260714-DEPENDENCY-FACT-QUERY-GLOBAL-LIMIT",
      "kind": "heading",
      "level": 2,
      "source_line": 798,
      "title": "ERROR_20260714_DEPENDENCY_FACT_QUERY_GLOBAL_LIMIT",
      "summary": "- Symptom: dependency audit вернул только latest_collector_status. - Root cause: ORDER BY 1 LIMIT 1 в конце UNION ALL применился ко всему набору фактов. - Correction: latest collector status извлекается скалярным подзапросом внутри CTE facts; глобальный LIMIT отсутствует. - Anti-regression: multi-fact SQL audit обязан проверять точное ожидаемое число строк до извлечения значений. - Closure proof: /var/lib/homelab-cluster-admin-stage4/stage4h-collector-provenance-readiness-scope-v1/report.txt - Status: CLOSED.",
      "rule_like": false,
      "signature": "8078498e8c9915662cb7bbe485ccb1e71099a6a8d39849830721e2bcd69fde70"
    },
    {
      "id": "ERR-H-L806-ERROR-20260714-COLLECTOR-PATCH-REQUIREMENT-PATTERN-MISMATCH",
      "kind": "heading",
      "level": 2,
      "source_line": 806,
      "title": "ERROR_20260714_COLLECTOR_PATCH_REQUIREMENT_PATTERN_MISMATCH",
      "summary": "- Symptom: MIGRATION_PLAN_COLLECTOR_PATCH_REQUIRED_COUNT=1 при ожидаемых двух подтверждениях. - Root cause: узкий шаблон учитывал collector_patch_required, но не collector_patch_required_after_stage4c. - Correction: оба поля проверяются отдельно, затем складываются как semantic requirement count. - Anti-regression: связанные JSON-контракты проверяются по собственным точным именам полей, а не одним унифицированным grep-шаблоном. - Closure proof: /var/lib/homelab-cluster-admin-stage4/stage4h-collector-provenance-readiness-scope-v1/report.txt - Status: CLOSED; PRIOR_TAIL_UNRESOLVED_COUNT=0.",
      "rule_like": false,
      "signature": "a136bb3af68dd9abefd3054a299cbe72d1ee4435d755299318badd0469472379"
    },
    {
      "id": "ERR-H-L814-ERROR-20260714-PRIVILEGED-FILE-REDIRECTION-BEFORE-SUDO",
      "kind": "heading",
      "level": 2,
      "source_line": 814,
      "title": "ERROR_20260714_PRIVILEGED_FILE_REDIRECTION_BEFORE_SUDO",
      "summary": "- Symptom: bash reported Permission denied while counting collector.py lines. - Root cause: `< collector.py` was opened by the unprivileged caller shell before sudo executed wc. - Correction: run sudo wc -l collector.py without caller-side input redirection. - Anti-regression: never combine sudo with `< protected-file`; pass the protected path as an argument to the privileged command. - Closure proof: /var/lib/homelab-cluster-admin-stage4/stage4h-collector-baseline-audit-final-v1/report.txt - Status: CLOSED.",
      "rule_like": true,
      "signature": "4510dad88d4639e1987b83002657a7c5c4580d0b4ae4b398c376b2ca49aa394d"
    },
    {
      "id": "ERR-H-L822-ERROR-20260714-COLLECTOR-RUN-COLUMN-ASSUMPTION",
      "kind": "heading",
      "level": 2,
      "source_line": 822,
      "title": "ERROR_20260714_COLLECTOR_RUN_COLUMN_ASSUMPTION",
      "summary": "- Symptom: SQL failed because completed_at did not exist. - Root cause: collector_runs columns were assumed instead of read from information_schema; actual names are finished_at and error_text. - Correction: assert required and forbidden column counts before querying recent runs. - Anti-regression: every schema-sensitive audit must discover and validate exact column names before the main query. - Closure proof: /var/lib/homelab-cluster-admin-stage4/stage4h-collector-baseline-audit-final-v1/report.txt - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": true,
      "signature": "01f861530e0df5cbb6814d21fe0caa2043d2819342012f74d4299c45bfcf08cd"
    },
    {
      "id": "ERR-H-L830-ERROR-20260714-REMOTE-STDIN-ARGUMENT-SHIFT",
      "kind": "heading",
      "level": 2,
      "source_line": 830,
      "title": "ERROR_20260714_REMOTE_STDIN_ARGUMENT_SHIFT",
      "summary": "- Symptom: remote harness выполнил chmod для пути bash. - Root cause: дополнительный positional token bash был передан после SSH command и стал первым аргументом stdin-скрипта. - Correction: remote command вызывает bash -s -- с ровно тремя явно встроенными аргументами: candidate path, SHA256 и byte count. - Anti-regression: stdin-скрипт обязан валидировать $1 по разрешённому path pattern и затем подтверждать точные SHA256 и byte count; запрещено добавлять отдельный bash-token после remote command. - Closure proof: /var/lib/homelab-change-sets/stage4h-collector-provenance-patch-candidate-v1/20260714T174131Z/report.txt - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": true,
      "signature": "a808754bcc91814fedf38578e86631a14e4964c3ab9571e205b6b65afa951389"
    },
    {
      "id": "ERR-H-L838-ERROR-20260714-OVERSIZED-COMMAND-PARSE-FAILURE",
      "kind": "heading",
      "level": 2,
      "source_line": 838,
      "title": "ERROR_20260714_OVERSIZED_COMMAND_PARSE_FAILURE",
      "summary": "- Symptom: Bash получил одиночный фрагмент и сообщил syntax error near unexpected token `(`. - Root cause: чрезмерно длинная интерактивная команда была обрезана или повреждена при передаче до полного разбора Bash. - Impact: side-effect audit подтвердил production state 0|0|OK, неизменный collector, активный timer и отсутствие временных БД, каталогов и процессов. - Correction: монолитные команды запрещены; сложные действия выполняются через отдельный проверяемый task-скрипт и короткий launcher. - Anti-regression: interactive payload target <= 8000 bytes; syntax-check перед запуском; remote output сохраняется независимо от RC. - Closure proof: /var/lib/homelab-change-sets/operator-command-size-guard-v1/20260714T182653Z/report.txt - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": false,
      "signature": "16b0958121cab8d184e16e7ea30930971bc785f4385caaeacea19bb35a8e8908"
    },
    {
      "id": "ERR-H-L847-ERROR-20260714-HOMELAB-ADMIN-HELP-RC-ASSUMPTION",
      "kind": "heading",
      "level": 2,
      "source_line": 847,
      "title": "ERROR_20260714_HOMELAB_ADMIN_HELP_RC_ASSUMPTION",
      "summary": "- Symptom: template audit завершился HOMELAB_ADMIN_HELP_FAILED. - Root cause: был ошибочно ожидаем RC=0 от неподдерживаемого аргумента --help. - Actual contract: неизвестный аргумент печатает usage и возвращает RC=64. - Correction: проверять version либо считать точную usage-строку вместе с RC=64 штатным результатом. - Anti-regression: перед проверкой CLI сначала фиксировать поддерживаемые команды; не предполагать GNU-style --help. - Closure proof: /var/lib/homelab-change-sets/homelab-admin-cli-contract-fix-v1/20260714T183338Z/report.txt - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": false,
      "signature": "346a85cc8494d14ed401b27e57d0b73a8d53078fdd91575ccb4dc89e833d4e44"
    },
    {
      "id": "ERR-H-L856-ERROR-20260715-EXPECTED-NEGATIVE-RC-TRAP-CLASSIFICATION",
      "kind": "heading",
      "level": 2,
      "source_line": 856,
      "title": "ERROR_20260715_EXPECTED_NEGATIVE_RC_TRAP_CLASSIFICATION",
      "summary": "- Symptom: schema-v2 task v3 build stopped while intentionally testing the --invalid argument path. - Root cause: an expected RC64 was executed while the generic ERR trap remained active. - Correction: expected failures are evaluated only through an explicit if/else branch with immediate RC capture. ANTI_REGRESSION_TOKEN=EXPECTED_RC64_INTERCEPTED_ERR_TRAP - Required pattern: if command; then rc=0; else rc=$?; fi, followed by a separate exact expected-RC assertion. - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": false,
      "signature": "99b412bad31ee6959b473695ef09baa07b43e09ded03a4a254d60c25715f6059"
    },
    {
      "id": "ERR-H-L864-ERROR-20260715-DUPLICATE-MACHINE-MARKER-COUNT",
      "kind": "heading",
      "level": 2,
      "source_line": 864,
      "title": "ERROR_20260715_DUPLICATE_MACHINE_MARKER_COUNT",
      "summary": "- Symptom: error-register candidate construction stopped with RC1 before applying the candidate. - Root cause: the same marker text appeared in both a heading and a descriptive line, while the guard expected one substring occurrence. - Correction: headings and prose do not contain machine-token values; validation counts only complete exact token lines. ANTI_REGRESSION_TOKEN=DUPLICATE_MACHINE_TOKEN_IN_HEADING_AND_BODY - Required pattern: grep -Fxc against a complete machine line, never grep -Foc against an unrestricted substring. - Production impact: none. - Temporary artifacts: removed and verified. - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": true,
      "signature": "356ff9228e7bb1e236f72584597d788a4dc65367b0a1e4af38209a9b24be13ac"
    },
    {
      "id": "ERR-H-L875-ERROR-20260715-OUTER-WRAPPER-SHELL-SYNTAX",
      "kind": "heading",
      "level": 2,
      "source_line": 875,
      "title": "ERROR_20260715_OUTER_WRAPPER_SHELL_SYNTAX",
      "summary": "- Symptom: the operator shell rejected the generated one-line wrapper with syntax error near the final subshell parenthesis. - Root cause: the outer wrapper contained an unbalanced compound shell construct before the closing subshell delimiter. - Impact: parsing failed before the start marker, guards and homelab-admin invocation; no task phase or infrastructure action executed. - Correction: use a linear wrapper with explicitly closed if/case blocks and avoid nested brace groups in conditional expressions. ANTI_REGRESSION_TOKEN=OUTER_WRAPPER_UNBALANCED_SUBSHELL_SYNTAX - Anti-regression: after any parser-level failure, treat the attempted phase as not executed and verify package and run-state before retrying. - Production impact: none. - Task package impact: none.",
      "rule_like": true,
      "signature": "208c08a253c47c1ad448f2282eacc0af231ebeb3443f3f888206b3d7926d9eef"
    },
    {
      "id": "ERR-H-L888-ERROR-20260715-INLINE-REMOTE-SHELL-IN-APPLY-PHASE",
      "kind": "heading",
      "level": 2,
      "source_line": 888,
      "title": "ERROR_20260715_INLINE_REMOTE_SHELL_IN_APPLY_PHASE",
      "summary": "- Symptom: homelab-admin lint rejected Stage4H task v6 with ERR-002-INLINE-REMOTE-SHELL. - Root cause: apply.sh invoked bash -s through an inline quoted SSH program and streamed the remote script through stdin. - Correction: copy the declared remote script to an isolated fixed path on VM180 and invoke that file directly with positional arguments. ANTI_REGRESSION_TOKEN=INLINE_REMOTE_SHELL_REPLACED_BY_STAGED_REMOTE_SCRIPT_PATH - Anti-regression: phase scripts may perform transport orchestration, but remote shell program bodies must exist as separately declared, syntax-checked remote script files. - Remote stdout and stderr must be preserved before evaluating the remote return code. - Task v6 status: REJECTED_BY_LINT; never executed and never mutated. - Production database impact: none.",
      "rule_like": true,
      "signature": "5c6fc9cd51fa9d557d4c8aee96a26b3d08bb3e86c6bd4813a7336a87c433b8bc"
    },
    {
      "id": "ERR-H-L902-ERROR-20260715-OPTIONAL-RUN-DIRECTORY-FIND-UNDER-PIPEFAIL",
      "kind": "heading",
      "level": 2,
      "source_line": 902,
      "title": "ERROR_20260715_OPTIONAL_RUN_DIRECTORY_FIND_UNDER_PIPEFAIL",
      "summary": "- Symptom: task-v7 builder stopped while counting formal runs for rejected task v6. - Root cause: find was executed against an optional directory that did not exist; under pipefail the assignment returned RC1. - Correction: test directory existence first and assign zero without invoking find when it is absent. ANTI_REGRESSION_TOKEN=MISSING_OPTIONAL_RUN_DIRECTORY_TREATED_AS_ZERO - Anti-regression: optional paths must have an explicit existence branch before find under pipefail. - Production impact: none. - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0. - Registered at: 2026-07-15T04:44:06Z",
      "rule_like": false,
      "signature": "f5b1a0f37dcba3d28adda172281f4f4b53abc87f36ec3ed7fe35cf51845f0794"
    },
    {
      "id": "ERR-H-L912-ERROR-20260715-PG-CONSTRAINT-CONTYPE-CHAR-CONCAT",
      "kind": "heading",
      "level": 2,
      "source_line": 912,
      "title": "ERROR_20260715_PG_CONSTRAINT_CONTYPE_CHAR_CONCAT",
      "summary": "- Symptom: isolated Stage4H acceptance failed during schema baseline capture. - Root cause: pg_constraint.contype uses the internal PostgreSQL char type and was concatenated without an explicit text cast. - Correction: use contype::text or CAST(contype AS text). ANTI_REGRESSION_TOKEN=PG_CATALOG_CHAR_CONCAT_REQUIRES_EXPLICIT_TEXT_CAST - Machine rule: ERR-014-PG-CATALOG-CHAR-CONCAT. - Negative self-test: uncast expression rejected with RC64. - Positive self-test: explicit text cast accepted with RC0. - Task v7 mutated: no.",
      "rule_like": false,
      "signature": "d269f342ff6fb119a139a91810419aa2a51ca9d24dcd0797ea65318c54ef10c7"
    },
    {
      "id": "ERR-H-L925-ERROR-20260715-LIVE-COLLECTOR-VERIFIER-WRONG-PATH",
      "kind": "heading",
      "level": 2,
      "source_line": 925,
      "title": "ERROR_20260715_LIVE_COLLECTOR_VERIFIER_WRONG_PATH",
      "summary": "- Symptom: verified linter installation was rolled back after the final collector integrity guard returned UNKNOWN. - Root cause: verifier queried a nonexistent path instead of the canonical live collector path declared by the task acceptance script. - Canonical path: /opt/cluster-admin-incident-engine/collector.py. - Incorrect path: /opt/homelab-cluster-admin/cluster-admin-collector.py. ANTI_REGRESSION_TOKEN=LIVE_COLLECTOR_VERIFIER_PATH_MUST_MATCH_TASK_CANONICAL_PATH - Correction: derive and guard the live collector path from the immutable task contract before executing the hash query. - Production impact: none; the canonical collector hash remained unchanged. - Status: CLOSED; UNRESOLVED_TAIL_COUNT=0.",
      "rule_like": false,
      "signature": "91e0cfb58ea5cec63e264f63f6ee65c13efe04ab4d4ab1f70a1d1f285f7b8ad2"
    }
  ],
  "rules": [
    {
      "id": "RULE-L3",
      "source_line": 3,
      "text": "Назначение: перед каждой следующей командой сверяться с этим файлом.",
      "signature": "910453eea5d379e1b055671660d8278df9d4565c580ecb6a036cfc1af83067f0"
    },
    {
      "id": "RULE-L11",
      "source_line": 11,
      "text": "## Жёсткие правила перед каждой командой",
      "signature": "3c373d80054bd99fe70a2f6e16e772fdaa67a63def211411b41f18d3123f91d0"
    },
    {
      "id": "RULE-L18",
      "source_line": 18,
      "text": "CHECK-7: если команда длиннее 8 строк, её нельзя давать в интерактивный shell.",
      "signature": "a9fa3d2bb79aa917672955c9adfcb227c45317ed178acb1a55b0c78505d7f10d"
    },
    {
      "id": "RULE-L36",
      "source_line": 36,
      "text": "Если rsync/scp упал, нельзя проверять latest offhost-файл без сверки имени.",
      "signature": "eabdf9a86e3c2ea3141f16847eb903bd516e6f12129d5e45df4f95c485794a2a"
    },
    {
      "id": "RULE-L46",
      "source_line": 46,
      "text": "Нельзя писать sqlite SQL вида j.type in ('object','array') внутри ssh '...'.",
      "signature": "668eb11f0a93aadac3d70e8792e586314755975dc4ac505ac13169cf55af6244"
    },
    {
      "id": "RULE-L55",
      "source_line": 55,
      "text": "Перед каждой командой явно понимать: где выполняется локальная часть, где remote-часть, каким пользователем.",
      "signature": "c8948237e08243f20cafec02cd56c65a19073cbef147809f7d16a7a1b027bfde"
    },
    {
      "id": "RULE-L59",
      "source_line": 59,
      "text": "Запрещено использовать в remote sqlite SQL фрагменты вида '$.key' или '.key'.",
      "signature": "10d38e69e3690c6892ab2aa8ddef391d5a3bdb71c9c1d80b23ebe30661d92071"
    },
    {
      "id": "RULE-L61",
      "source_line": 61,
      "text": "Команду с ошибкой char(36)||.dns_provider считать битой и не использовать.",
      "signature": "0ea37a4f0c68fcbd9d07b6ce4cc184b4152c5c247b1f31a1a70efa8cebeaacb9"
    },
    {
      "id": "RULE-L63",
      "source_line": 63,
      "text": "16. Основополагающее правило: перед каждой командой ассистент обязан явно написать: Сверка с файлом ошибок: CHECK пройден.",
      "signature": "ddd85ef9ebb1cbc0cb2d0cda3c70a5f4d23814cb7146f72672f99e2cbc8f82a0"
    },
    {
      "id": "RULE-L65",
      "source_line": 65,
      "text": "Нельзя выдавать команды без предварительной сверки с этим файлом ошибок.",
      "signature": "bd94bb5f40d6442f833a3ec028dfaf34c02e6d69701a7397257f9857202c2c5b"
    },
    {
      "id": "RULE-L66",
      "source_line": 66,
      "text": "Нельзя продолжать после собственной ошибки без записи ошибки в этот файл.",
      "signature": "f03bf4ecbe116c3281dd16b6ed1f80b16dc7a9ecd410a08244b5c41677fe5581"
    },
    {
      "id": "RULE-L68",
      "source_line": 68,
      "text": "19. Уточнение формата: не писать отдельную строку сверки перед каждой командой в пакете.",
      "signature": "4087c4bd5e52ca8c6f65e39b077c4bda9ae4a43aafac5857d5f33cc3f25683f0"
    },
    {
      "id": "RULE-L84",
      "source_line": 84,
      "text": "Не использовать больше сгенерированные большие base64 PHP для SMTP/XenForo.",
      "signature": "4db02d79ff907fa767992151c4a00ccff168749fa69417a6bcbeb6108656f2c0"
    },
    {
      "id": "RULE-L93",
      "source_line": 93,
      "text": "Нельзя использовать { echo RULE_CHECK_FAIL; exit 1; } в командах, выполняемых прямо в интерактивном root@pve01 shell.",
      "signature": "074e8d6400e50fd4a166ae4f67cf2da00d64051999145e17bbdaa432470ba64f"
    },
    {
      "id": "RULE-L105",
      "source_line": 105,
      "text": "Не использовать -crlf, если команды уже отправляются с явным \\r\\n.",
      "signature": "d8ee091ee5bb14359082cbc1e1b371d8128ddcff586b52516fed69b743f29cdf"
    },
    {
      "id": "RULE-L110",
      "source_line": 110,
      "text": "Не использовать heredoc Python внутри вложенного ssh; для dashboard verification использовать cat/grep JSON или простые команды.",
      "signature": "006d36b18ca6c52314d1e1d138393caf26b0d0e47533bf59ccba30210d12cbd7"
    },
    {
      "id": "RULE-L123",
      "source_line": 123,
      "text": "Нельзя считать monitoring слой runtime-closed только по secret scan/reference proof; нужно отдельно проверить Prometheus container/API и обновить справочник по факту.",
      "signature": "6744f09160604e98d226f32ef123dd61ee08278f0321c63edd729728300115e7"
    },
    {
      "id": "RULE-L125",
      "source_line": 125,
      "text": "31. Строгое правило: перед каждой командой сверяться с файлом ошибок и справочником.",
      "signature": "29acbf555ad438e9e4125ecb8c53e0ce38f6bce98ed3716e639afbb4c0eff3d7"
    },
    {
      "id": "RULE-L128",
      "source_line": 128,
      "text": "Если сверка не пройдена, команда не должна выполнять основное действие. Не использовать exit 1, чтобы не закрывать SSH-сессию.",
      "signature": "f20bbb59669123830e2c65160a0b67bf287cdcf82e94b17992919942fd715698"
    },
    {
      "id": "RULE-L132",
      "source_line": 132,
      "text": "Файл 153_PROMETHEUS_TARGETS_SETTLED_PROOF.txt нельзя считать валидным settled proof.",
      "signature": "8d28bb1cb84636890872e04ff869c76a8690936fed3b82af881bf1f2f83961e0"
    },
    {
      "id": "RULE-L133",
      "source_line": 133,
      "text": "Для таких проверок не использовать вложенный Python; использовать curl + grep/sed/awk либо отдельный локальный файл-скрипт.",
      "signature": "0d986ea7ea4c0cb4231812d5cf50fee6da9bdb43df2e61e2958c4536a56003b3"
    },
    {
      "id": "RULE-L176",
      "source_line": 176,
      "text": "- Do not split work into many small commands when the operations are low-risk, read-only, or naturally part of one evidence block.",
      "signature": "4ab34705facd281e3ea1b0cd209248f3b4087f623cf38f8b8622cbf511e67dea"
    },
    {
      "id": "RULE-L182",
      "source_line": 182,
      "text": "- Rule: avoid large opaque base64 script payloads for homelab changes; use transparent bounded shell/perl/python commands instead.",
      "signature": "3b1b7b83b7ed141a8dbf54ea052b6a3c6fb5701e0bc056939fa523727f996097"
    },
    {
      "id": "RULE-L185",
      "source_line": 185,
      "text": "- Rule: do not use large opaque payloads, generated base64 scripts, or hidden multi-line script blobs for homelab changes.",
      "signature": "386eaacb52062f19dc9f3fb9581a03ee50d5fbf08e19daba33f7271778b9b8e3"
    },
    {
      "id": "RULE-L188",
      "source_line": 188,
      "text": "- Any Traceback, UnicodeDecodeError, SyntaxError, Permission denied, command-not-found, nonzero critical RC, or failed write marker must force REVIEW or FAILED, never OK.",
      "signature": "540af43a414bcfd4a26cf5304ca785fbd5a127d99423647298862c964c3f493d"
    },
    {
      "id": "RULE-L196",
      "source_line": 196,
      "text": "- External informational widgets such as weather/Open-Meteo must not block the service launcher portal.",
      "signature": "74d434993ef9b2957b7beb9d50b3b35a30b84407a845454229c05e548bc91096"
    },
    {
      "id": "RULE-L201",
      "source_line": 201,
      "text": "- Rule: do not use deeply nested one-liners for config edits; first run short state checks, then apply small transparent edits.",
      "signature": "20fada2b3e3a676500b865a15816c133eb784aa10f4c6d7dab66220e573a8615"
    },
    {
      "id": "RULE-L206",
      "source_line": 206,
      "text": "- Matches in /logs/ or *.bak* are historical evidence only and must not make cleanup REVIEW unless the active config still contains the target.",
      "signature": "62de758aaf9d70c35a2f1fe076f80a8308e46d3b13d23c38665ab9f8ccc1cba4"
    },
    {
      "id": "RULE-L209",
      "source_line": 209,
      "text": "- Rule: Homepage link validation must not treat HTTP 200 as success if the body is the NPMPlus default “Congratulations” / host-not-set-up page.",
      "signature": "04b9d2a779820f3461cbb102ac47f6874198613575464488f460a9d287a089af"
    },
    {
      "id": "RULE-L217",
      "source_line": 217,
      "text": "- Rule: do not apply Netcraze config through stdin/multiline SSH; prove CLI input mode first.",
      "signature": "308cfe5fb5cfe99cb8c114de48913256103645e6a9e81ae9bc7ff446a484de9a"
    },
    {
      "id": "RULE-L228",
      "source_line": 228,
      "text": "- Rule: do not attempt ACL/config changes as routerbackup; use admin/operator router access or router Web UI for write changes.",
      "signature": "ce0e7d2dc85078db0180beacc1df838f3fe02f41db5de2d31573fe08f54a0f29"
    },
    {
      "id": "RULE-L245",
      "source_line": 245,
      "text": "- Rule: do not rely on custom Node lookup callback until separately tested; prefer curl --resolve or simple direct Node HTTP checks.",
      "signature": "25ad4cb5f7058222a9b5f59e98067f1db67a272e35cdd8292ca98eb050b3a0dc"
    },
    {
      "id": "RULE-L252",
      "source_line": 252,
      "text": "- Rule: do not keep changing ACL for this symptom; ACL is already correct. Fix service access scope or use a dedicated monitor endpoint.",
      "signature": "43f30d594ffb8d392ebc4b7defd82321e0f844a8b789a9b1d148a13df593d18c"
    },
    {
      "id": "RULE-L259",
      "source_line": 259,
      "text": "- Rule: do not use py_compile against root-owned system paths from an unprivileged user.",
      "signature": "ccf2aa95504d6021f9abcd8fecd6409a7a5efc61e8123fbbe7e03f539e979f89"
    },
    {
      "id": "RULE-L264",
      "source_line": 264,
      "text": "- Impact: do not use [PRIVATE_IP] directly as Homepage siteMonitor from edge-vm.",
      "signature": "8dfd3ac2d710c3ec6c9a2ec45f12611f948b98b8fe89d4d24b509a8d96082073"
    },
    {
      "id": "RULE-L279",
      "source_line": 279,
      "text": "- Rule: validate generated Python syntax locally before scp/remote execution; avoid f-strings in generated maintenance scripts.",
      "signature": "d7c5d3ed72ee048311445f823a903a5ecb6d22b03afec741f277aca10cd77858"
    },
    {
      "id": "RULE-L284",
      "source_line": 284,
      "text": "- Rule: do not proceed with OS baseline until SSH failure is diagnosed; likely old known_hosts key or cloud-init/root-key issue.",
      "signature": "9831f60cb061cc933e8c6688faad3f6b352d37d6237a03eefb5917a8facee420"
    },
    {
      "id": "RULE-L296",
      "source_line": 296,
      "text": "- Rule: avoid nested $(...) in VM SSH proofs; use literal remote commands and clean proof.",
      "signature": "65c4ac8d2adf3259f26e14c7d971b1ac68082da836bc807214901e58db59fdfa"
    },
    {
      "id": "RULE-L333",
      "source_line": 333,
      "text": "- Impact: do not trust that SQLite inspection attempt.",
      "signature": "23045ac8610d67d57dc1a7eb430578d6468f19d1acea9c7584f2a6f471393418"
    },
    {
      "id": "RULE-L334",
      "source_line": 334,
      "text": "- Rule: avoid nested SSH heredoc/Python for this task; use simpler commands, generated nginx configs, or uploaded/local script files.",
      "signature": "f4b937e830b202a02596fd60dbc1f2213645be3c5a70d929c8ff536f4adceb2c"
    },
    {
      "id": "RULE-L340",
      "source_line": 340,
      "text": "- Rule: read NPMplus API login values from docker inspect env internally, never print them.",
      "signature": "520fdfde2e139662a8738bb691cfe1929b9b9023a4dcc274e88a2b32ef1bfc5b"
    },
    {
      "id": "RULE-L351",
      "source_line": 351,
      "text": "- Impact: do not use NPMplus API for this publish path.",
      "signature": "dfce8b1c71b656add085d5c068f621a441177281533d880c8dc230b17b0e22dd"
    },
    {
      "id": "RULE-L357",
      "source_line": 357,
      "text": "- Impact: certbot ran without token and all certificate attempts failed; DNS cutover must not proceed from proof 535.",
      "signature": "a0e59d5dfd804bc63152339e3d2a3b0b288578d976b2039231a6272cbd9d002b"
    },
    {
      "id": "RULE-L406",
      "source_line": 406,
      "text": "- Rule: never source SMTP secret files containing arbitrary passwords; store password base64 and use msmtp passwordeval helper accessible to www-data.",
      "signature": "429fad764756578d3ed22029b6c2d6af0912b9b28d4a2263969cc53a090ba938"
    },
    {
      "id": "RULE-L419",
      "source_line": 419,
      "text": "- Rule: never store arbitrary passwords as shell-sourceable KEY=value files; use non-printing secret storage only.",
      "signature": "d04ca47a86fba60c038b212f585230b1bbb335493d583def83cbc3993bd7d88f"
    },
    {
      "id": "RULE-L424",
      "source_line": 424,
      "text": "- Rule: do not change DNS, firewall, proxy, mail, NetBird, or Mailcow configs before read-only triage proof.",
      "signature": "cdb23b83af44d2282df9c2a08be575953d61f280cc9abd53a4cc43b3e653cd1c"
    },
    {
      "id": "RULE-L430",
      "source_line": 430,
      "text": "- Rule: do not test SMTP auth again until a fresh rotated mailbox/app password is available.",
      "signature": "6694ecf8bef00c902be47d179510d8d4aadc93961c0164598e99350a41982f73"
    },
    {
      "id": "RULE-L436",
      "source_line": 436,
      "text": "- Impact: old timer must not be treated as valid current backup for all five forums.",
      "signature": "30e78abd678ecee3a63ddbd2d71e7caa390229722dab892fbfae0c891b725f13"
    },
    {
      "id": "RULE-L463",
      "source_line": 463,
      "text": "- Never print or package secrets.",
      "signature": "a0014dc66b74aee1b458771d46c51c80e582ee1bb69a1316aa6b63c91a3a40b2"
    },
    {
      "id": "RULE-L468",
      "source_line": 468,
      "text": "- Impact: proof 623 is invalid and must not be used to judge mail delivery.",
      "signature": "cdbba5ccdd73b604f4f3970ae1d69868a9d409f74ead6be3d60c031f26afa3f5"
    },
    {
      "id": "RULE-L497",
      "source_line": 497,
      "text": "- Do not print secrets.",
      "signature": "59a6b5bdc55ccb782cc5fa6d93fdf3d734c32d8250a2c21a57c6c7d4f6f3cd24"
    },
    {
      "id": "RULE-L498",
      "source_line": 498,
      "text": "- Do not download or upload:",
      "signature": "3de6b008bc8038faf7099e21fb98ec4280a409c05d3ae3453ec80bcd81e62240"
    },
    {
      "id": "RULE-L514",
      "source_line": 514,
      "text": "- Rule: do not use Cloudflare DNS-01 for these parked domains unless a token with explicit zone access is installed; because DNS already points to edge, prefer a copied-script HTTP-01 flow with temporary ACME challenge routes and content-specific validation.",
      "signature": "92d2f5a1cd4f344851af8d7a716dc24d8bc6a7738a47f6aa05013676f2200d61"
    },
    {
      "id": "RULE-L533",
      "source_line": 533,
      "text": "- Rule: do not run another apply stage until active NPMplus nginx route selection for newfi.ru is diagnosed with nginx -T and Host-header probes.",
      "signature": "039b358ea5679e61f5d7d8806ee36af893c18627bf8d26c434e902fb4c0e217f"
    },
    {
      "id": "RULE-L538",
      "source_line": 538,
      "text": "- Impact: HTTP-01 cannot work through the default server. A dedicated temporary ACME server block must be proven before certbot; do not run another certbot apply without a successful ACME-path probe for all six hostnames.",
      "signature": "2f1d3fca5be936885c2fff63166f1f43925dd8097efc48cfd46b54ff135f235a"
    },
    {
      "id": "RULE-L544",
      "source_line": 544,
      "text": "- Impact: do not continue certbot/apply until exact newfi.ru route behavior is isolated.",
      "signature": "8710aaa42a28a4c0a5598837af21305c7f4f17db34832d9e09cde58db0cf5a4b"
    },
    {
      "id": "RULE-L551",
      "source_line": 551,
      "text": "- Rule: do not use one combined parked-domain server_name block for these domains; use exact host blocks and content-specific validation.",
      "signature": "84a57025900a3fc4d1ba84dbcf54cf84b30875b82060e1e0dc66f9ccdf3da5ca"
    },
    {
      "id": "RULE-L558",
      "source_line": 558,
      "text": "- Rule: do not retry parked-domain HTTP-01 with low id 700-705. First prove fixed high-id exact blocks, then apply with those exact ids.",
      "signature": "527144862f28a73814ccc2c95fc4a380202a207f194962f9a7fb0a8d34833c2f"
    },
    {
      "id": "RULE-L564",
      "source_line": 564,
      "text": "- Rule: do not continue HTTP-01 for these parked domains; use DNS-01 with a dedicated Cloudflare token that has Zone Read and DNS Edit for the three zones.",
      "signature": "0c171db0e19ad3ff56b66bc5623f05867010cd7b2b87f98ef9cbe4d08a5c84f1"
    },
    {
      "id": "RULE-L568",
      "source_line": 568,
      "text": "- Impact: do not rerun Stage15 as-is.",
      "signature": "23033da11bdfd30e8d3405fa448f9e0a2516c09a778b90693eb8c01304e0b534"
    },
    {
      "id": "RULE-L569",
      "source_line": 569,
      "text": "- Rule: for parked domains finalization, use existing DNS-01 certificates and validate from pve01 public HTTPS with content marker; do not use the failed local --resolve TLS check as the closure gate.",
      "signature": "a6fd815206627d21af51c6ca97a5b2a03681011e1ad80eb0e6b29044cbce0323"
    },
    {
      "id": "RULE-L573",
      "source_line": 573,
      "text": "- Rule: Stage18 must not change DNS records or NPMplus route files. It may update placeholder HTML, run existing forum renewal script once for runtime proof, and run certbot dry-run for parked certificates using existing DNS-01 hooks.",
      "signature": "09d9b1e72b0bb7016128313662cddfa540a7a7908e768f8d4d22e5efd62168a0"
    },
    {
      "id": "RULE-L577",
      "source_line": 577,
      "text": "- Rule: only root gram1.ru and www.gram1.ru may be routed. Existing gram1.ru subdomain NPMplus routes must not be changed.",
      "signature": "dab2f98b8be402bac17f88d4e39ecfea8bdd5726097272cff11f7558f55eed0e"
    },
    {
      "id": "RULE-L578",
      "source_line": 578,
      "text": "- Rule: use DNS-01 with a dedicated gram1 Cloudflare token; do not use HTTP-01.",
      "signature": "40b057e3fd9659583584a2e3d4167a256076e134c4fbaa3678fcfc2503e55d2d"
    },
    {
      "id": "RULE-L583",
      "source_line": 583,
      "text": "- Decision: because pve01 has no SSH key access to the external VPS, do not modify the VPS. Instead move only pvepro.ru and www.pvepro.ru A records to edge and serve an edge landing page.",
      "signature": "17a7cd4c3204ffc9a003a8a6cef3e9be96599674d54f0f1a217eb1f3f0dad811"
    },
    {
      "id": "RULE-L584",
      "source_line": 584,
      "text": "- Safety rule: do not change mail.pvepro.ru, nb.pvepro.ru, MX, SPF, DKIM, DMARC or forum SMTP configuration.",
      "signature": "20b36bae3948a0b947ad12ca12a28cf8041c2ee4dd71dd71479c523fbeeaab0f"
    },
    {
      "id": "RULE-L585",
      "source_line": 585,
      "text": "- Use a dedicated Cloudflare token for pvepro.ru. Do not print token values.",
      "signature": "028d50fb2fef9139bce5a6a16069b7cdd69ab88719f640f0f0858034931d3d9b"
    },
    {
      "id": "RULE-L594",
      "source_line": 594,
      "text": "- Mistakes recorded: do not scp scripts/* after local py_compile because __pycache__ may be present; do not continue after critical scp failure in future apply stages; do not classify PVEPro landing as NetBird just because it contains a link text; use Cloudflare authoritative A records and --resolve validation when DNS propagation may lag.",
      "signature": "25354a6b9b469385d2300d5d321f8415312bb2392f2e96b37250717afce13b34"
    },
    {
      "id": "RULE-L600",
      "source_line": 600,
      "text": "- Rule: do not expose router admin publicly and do not attempt certificate deployment to the router over the public internet.",
      "signature": "b936ae30ffb935118585b5a133d9c32643946a3ba5034b85f42bae000518d49a"
    },
    {
      "id": "RULE-L606",
      "source_line": 606,
      "text": "- Rule: do not expose router admin publicly and do not attempt certificate deployment to the router over the public internet.",
      "signature": "b936ae30ffb935118585b5a133d9c32643946a3ba5034b85f42bae000518d49a"
    },
    {
      "id": "RULE-L611",
      "source_line": 611,
      "text": "- Rule: do not print WireGuard private keys, PSK, or client config body in proof/chat.",
      "signature": "d97ed9abb2447ca6786e0f266db8fe55e6071f854727ed9feb01a333d4fdd68e"
    },
    {
      "id": "RULE-L613",
      "source_line": 613,
      "text": "- Rule: do not open router public admin or certificate deployment until WireGuard handshake is proven.",
      "signature": "c44dbc849473687dad5c7756ca31b9b1366190f1f974a009ab8a2b2a175887cc"
    },
    {
      "id": "RULE-L641",
      "source_line": 641,
      "text": "- Do not retry immediately.",
      "signature": "e1208fd6483129c1940ee58ac88c1b14996e9906f5854c93a893423378ef3f63"
    },
    {
      "id": "RULE-L662",
      "source_line": 662,
      "text": "- Do not treat e3qxxx.netbird.selfhosted / 100.100.125.70 and e3qxxx-183-106.netbird.selfhosted / 100.100.183.106 as VPS egress nodes; they are user mobile peers.",
      "signature": "d025d035ce8bfa83c9f7b3605f86af415bded0a45359ee22348ce089f0a4de9f"
    },
    {
      "id": "RULE-L669",
      "source_line": 669,
      "text": "- Do not delete or disable Homepage siteMonitor fields to hide red badges.",
      "signature": "b101de9840659abe0fdbc458eeb21988b1ca2ef12a7cdce01e42f57991281d98"
    },
    {
      "id": "RULE-L671",
      "source_line": 671,
      "text": "- Do not touch Cloudflare Homepage card when operator says it is green and opens correctly.",
      "signature": "e55eaa04a75332bfdad36576da5425a956fa6e84f1003a1c606e64b0701737a5"
    },
    {
      "id": "RULE-L678",
      "source_line": 678,
      "text": "- Do not touch Cloudflare when operator says it is green and opens correctly.",
      "signature": "df725b9f5c7198ce4c04f9c37c1a2705147a9b2547a8f6de89800c03c68ea37f"
    },
    {
      "id": "RULE-L684",
      "source_line": 684,
      "text": "- Do not delete containers to fix Dockge inactive UI; connect agents and archive only confirmed stale moved stack definitions.",
      "signature": "b31b7d7bef8b7f89787850a3c73ce500918e256710e16626efa0d983dbec9b31"
    },
    {
      "id": "RULE-L711",
      "source_line": 711,
      "text": "- Future preparation must use exact privileged paths or a verified shared group, never an unprivileged wildcard after restrictive chmod.",
      "signature": "ffadddc535d9f6ef63a2e5f96282815f86c2844d21a1302af925758271cf605f"
    },
    {
      "id": "RULE-L737",
      "source_line": 737,
      "text": "- The six wrapper validation failures are cascading missing-marker checks and do not prove six independent infrastructure faults.",
      "signature": "edb305f9a22332f3efb7192d483e3f067a1c50b93b0152596c8bbdff8b58fcd3"
    },
    {
      "id": "RULE-L768",
      "source_line": 768,
      "text": "- The phase-wide forbidden-pattern scan also included validate_design.py itself, which necessarily contains the prohibited literals it verifies.",
      "signature": "c9daa84dfbc8edb3cc4046148bdcb08f590a765c069f15dccba110171dcfefee"
    },
    {
      "id": "RULE-L789",
      "source_line": 789,
      "text": "- The backup must not be rerun merely to satisfy the invalid health-file freshness assertion.",
      "signature": "85bc7d2eeb4c251bf6db4f1e1e9be31bd05c124e10a3c3d0c9b671f1c2aea449"
    },
    {
      "id": "RULE-L793",
      "source_line": 793,
      "text": "- Mandatory anti-regression: после ошибки запрещено продолжать другой scope до точной root cause, исправления, повторной проверки, очистки, proof и seal.",
      "signature": "3506ad8423c9306313c4995dffee63340e95a4e1b0fdab450b433570927d3602"
    },
    {
      "id": "RULE-L818",
      "source_line": 818,
      "text": "- Anti-regression: never combine sudo with `< protected-file`; pass the protected path as an argument to the privileged command.",
      "signature": "8b5571b92d02dbe05c3f8a9f7637776e8665b44ce49521d4fe408fa977c13e39"
    },
    {
      "id": "RULE-L825",
      "source_line": 825,
      "text": "- Correction: assert required and forbidden column counts before querying recent runs.",
      "signature": "598215d2c23dd83bd6faa1beedb94f32b94ac3fd7e0573ea754db0f680b415ab"
    },
    {
      "id": "RULE-L834",
      "source_line": 834,
      "text": "- Anti-regression: stdin-скрипт обязан валидировать $1 по разрешённому path pattern и затем подтверждать точные SHA256 и byte count; запрещено добавлять отдельный bash-token после remote command.",
      "signature": "333aa0cc07135a79e5044a85b773ad5a7f0e655fd0a0d6422ec994230ce8112a"
    },
    {
      "id": "RULE-L867",
      "source_line": 867,
      "text": "- Correction: headings and prose do not contain machine-token values; validation counts only complete exact token lines.",
      "signature": "d820b99656b281c428c56ed8698a3fe4295e435e708263040de0136d1a42a18d"
    },
    {
      "id": "RULE-L869",
      "source_line": 869,
      "text": "- Required pattern: grep -Fxc against a complete machine line, never grep -Foc against an unrestricted substring.",
      "signature": "bab78eea9eadb2614ae16292dec8fda0bead38c2223efd2c79b512d1ad965836"
    },
    {
      "id": "RULE-L879",
      "source_line": 879,
      "text": "- Correction: use a linear wrapper with explicitly closed if/case blocks and avoid nested brace groups in conditional expressions.",
      "signature": "e6627533fc6f44113d3e3fb415ee4807a46afce3393d2457ca87649f3e95a338"
    },
    {
      "id": "RULE-L895",
      "source_line": 895,
      "text": "- Task v6 status: REJECTED_BY_LINT; never executed and never mutated.",
      "signature": "eaa651690a1e2b17124007e55346be7ad52d4d9f6172746262bc0698c376396d"
    }
  ],
  "command_ledger": {
    "present": true,
    "entry_count": 10,
    "failed_entry_count": 0,
    "failed_command_hashes": []
  },
  "privacy": {
    "sanitized": true,
    "secrets_included": false,
    "private_addresses_included": false
  }
}

OUTPUT_END
CHAT_OUTPUT_END
