{
  "schema": "factorywager.telegram-handshake-catalog.v1",
  "generatedAt": "2026-08-09T03:02:22.340Z",
  "docs": {
    "runbook": "docs/harness/tenants/partner-package-group-handshake.md",
    "factoryBot": "docs/harness/tenants/telegram-factory.md",
    "seatCapitalDesk": "docs/harness/tenants/seat-capital-desk.md",
    "deskAdr": "docs/adr/0003-telegram-handshake-desk.md",
    "moduleIndex": "lib/telegram/README.md",
    "softPlane": "toc-ops-repo/docs/system/TELEGRAM.md"
  },
  "constants": {
    "partnerCodeRegex": "^[A-Z]{3,6}$",
    "callSignRegex": "^[A-Z]{3,6}-\\d{3}(?:-SUB\\d{2}){0,2}$",
    "membersHouseOnly": 2,
    "membersWithPartner": 3,
    "membershipCells": [
      "2·house",
      "2·house!",
      "3·OK",
      "N·ext"
    ],
    "jsonlPath": "reports/telegram/pending-package-groups.jsonl",
    "forumsMetaDir": "reports/telegram/forums",
    "registryTable": "package_group_registry",
    "knownChatsTable": "ops_telegram_known_chats"
  },
  "packageForumTopics": {
    "kind": "partner-package-forum",
    "plan": [
      "General",
      "Ops",
      "Alerts",
      "Liquidity/Outs",
      "Accounting"
    ],
    "mtproto": [
      "Ops",
      "Alerts",
      "Liquidity/Outs",
      "Accounting"
    ],
    "rows": [
      {
        "title": "General",
        "mapKey": "general",
        "role": "Implicit thread 1",
        "botCreated": false
      },
      {
        "title": "Ops",
        "mapKey": "ops",
        "role": "House ops posts",
        "botCreated": true,
        "icon": {
          "iconColor": 4,
          "emoji": "📋"
        }
      },
      {
        "title": "Alerts",
        "mapKey": "alerts",
        "role": "Outbox alerts routing",
        "botCreated": true,
        "icon": {
          "iconColor": 2,
          "emoji": "🚨"
        }
      },
      {
        "title": "Liquidity/Outs",
        "mapKey": "liquidity/outs",
        "role": "Pinned seat capital desk + rails pipe intake",
        "botCreated": true,
        "icon": {
          "iconColor": 3,
          "emoji": "💧"
        }
      },
      {
        "title": "Accounting",
        "mapKey": "accounting",
        "role": "Deposit/withdraw/bet-slip proof (screenshots)",
        "botCreated": true,
        "icon": {
          "iconColor": 1,
          "emoji": "📸"
        }
      }
    ],
    "threadMapKeys": {
      "general": "general",
      "ops": "ops",
      "alerts": "alerts",
      "liquidityOuts": "liquidity/outs",
      "accounting": "accounting"
    },
    "mapKeyRule": "title.toLowerCase()",
    "deskTopicKey": "liquidity/outs",
    "accountingTopicKey": "accounting"
  },
  "houseForumTopics": {
    "kind": "house-surface",
    "surfaces": {
      "hq": {
        "groupTitle": "TOC Ops · HQ",
        "topicSlugs": [
          "alerts",
          "day-ops",
          "aar",
          "identity"
        ],
        "groupDescription": "TOC Ops · HQ — Production ops desk — alerts, day-ops, AAR. Humans + pager traffic only.",
        "topicIcons": {
          "alerts": {
            "iconColor": 2,
            "emoji": "🚨"
          },
          "day-ops": {
            "iconColor": 4,
            "emoji": "📋"
          },
          "aar": {
            "iconColor": 0,
            "emoji": "📝"
          },
          "identity": {
            "iconColor": 5,
            "emoji": "🪪"
          }
        }
      },
      "ash-staging": {
        "groupTitle": "TOC Ops · ASH · staging",
        "topicSlugs": [
          "plays",
          "balances",
          "onboard",
          "alerts"
        ],
        "groupDescription": "TOC Ops · ASH · staging — ASH partner desk (staging) — Soft balances, plays, onboard. Not production alerts.",
        "topicIcons": {
          "plays": {
            "iconColor": 3,
            "emoji": "🎯"
          },
          "balances": {
            "iconColor": 1,
            "emoji": "💰"
          },
          "onboard": {
            "iconColor": 4,
            "emoji": "👋"
          },
          "alerts": {
            "iconColor": 2,
            "emoji": "🚨"
          }
        }
      },
      "all-accounting": {
        "groupTitle": "TOC Ops · Accounting",
        "topicSlugs": [
          "deposits",
          "withdrawals",
          "reconcile"
        ],
        "groupDescription": "TOC Ops · Accounting — Cross-partner deposit/withdraw proof rollup — ops mirror; partners post in package forum Accounting topic.",
        "topicIcons": {
          "deposits": {
            "iconColor": 3,
            "emoji": "⬇️"
          },
          "withdrawals": {
            "iconColor": 2,
            "emoji": "⬆️"
          },
          "reconcile": {
            "iconColor": 0,
            "emoji": "✅"
          }
        }
      },
      "sandbox": {
        "groupTitle": "TOC Ops · sandbox",
        "topicSlugs": [
          "scratch",
          "experiments"
        ],
        "groupDescription": "TOC Ops · sandbox — Bot / brand / template experiments. Disposable. Never route partner Soft here.",
        "topicIcons": {
          "scratch": {
            "iconColor": 5,
            "emoji": "🧪"
          },
          "experiments": {
            "iconColor": 6,
            "emoji": "🔬"
          }
        }
      }
    },
    "allAccountingForumTitles": [
      "Deposits",
      "Withdrawals",
      "Reconcile"
    ]
  },
  "seatDeskTemplates": {
    "partner": {
      "confirm-active": {
        "id": "confirm-active",
        "label": "Partner confirm (DM paste)",
        "thread": "dm-paste",
        "purpose": "Consolidated active-out confirm + missing fields + reply examples.",
        "cli": "bun run seat:desk:partner-message CALL-SIGN",
        "postable": false,
        "dynamicFields": [
          "callSign",
          "activeOutCount",
          "deferredOuts",
          "commonMissing",
          "outSpecificMissing"
        ]
      },
      "todo": {
        "id": "todo",
        "label": "Todo list",
        "thread": "desk-copy",
        "purpose": "One line per incomplete out — mirrors Copy todo button.",
        "cli": "bun run seat:desk:partner-message CALL-SIGN --template todo",
        "postable": false,
        "dynamicFields": [
          "perOutMissing"
        ]
      },
      "reply-hint": {
        "id": "reply-hint",
        "label": "Reply hint line",
        "thread": "desk-copy",
        "purpose": "Single pipe line for first incomplete out.",
        "cli": "bun run seat:desk:partner-message CALL-SIGN --template reply-hint",
        "postable": false,
        "dynamicFields": [
          "firstIncompleteOutNum"
        ]
      },
      "topic-intake": {
        "id": "topic-intake",
        "label": "Liquidity/Outs · deposit rails",
        "thread": "liquidity/outs",
        "purpose": "First rails ask on pinned desk — pipe format + no-password note.",
        "cli": "bun run seat:desk:partner-message CALL-SIGN --template topic-intake [--post]",
        "postable": true,
        "dynamicFields": [
          "partnerCode",
          "deskPin",
          "activeOutRange",
          "deferredSkip"
        ]
      },
      "topic-rails": {
        "id": "topic-rails",
        "label": "Liquidity/Outs · pay rails nudge",
        "thread": "liquidity/outs",
        "purpose": "Short follow-up when rails still missing on desk.",
        "cli": "bun run seat:desk:partner-message CALL-SIGN --template topic-rails [--post]",
        "postable": true,
        "dynamicFields": [
          "partnerCode",
          "deskPin",
          "railsNeedLine",
          "deferredSkip"
        ]
      },
      "topic-accounting": {
        "id": "topic-accounting",
        "label": "Accounting · proof thread",
        "thread": "accounting",
        "purpose": "Deposit/withdraw screenshots + optional bet slips — not parsed by desk pipe.",
        "cli": "bun run seat:desk:accounting-prompt CALL-SIGN [--post] [--thread-id N]",
        "postable": true,
        "dynamicFields": [
          "partnerCode",
          "deskPin",
          "activeOutRange",
          "deferredSkip"
        ]
      }
    },
    "allAccountingChannel": {
      "id": "all-accounting-channel",
      "label": "TOC Ops · all accounting",
      "thread": "all-accounting",
      "purpose": "Ops rollup — partners post in package forum Accounting; ops mirror here.",
      "cli": "bun run telegram:all-accounting --chat CHAT_ID --brand --post-prompt",
      "forumTopics": [
        "Deposits",
        "Withdrawals",
        "Reconcile"
      ],
      "builder": "buildAllAccountingChannelPrompt"
    },
    "pinnedTemplateRefs": {
      "partner:accounting": {
        "templateId": "topic-accounting",
        "builder": "buildSeatDeskAccountingTopicPrompt"
      },
      "partner:liquidity/outs:intake": {
        "templateId": "topic-intake"
      },
      "partner:liquidity/outs:rails": {
        "templateId": "topic-rails"
      },
      "house:all-accounting": {
        "templateId": "all-accounting-channel",
        "builder": "buildAllAccountingChannelPrompt"
      }
    }
  },
  "colors": {
    "brand": {
      "colorKey": "brand",
      "hex": "#0F766E",
      "css": "#0f766e"
    },
    "packageTopics": {
      "general": {
        "colorKey": "topicGeneral",
        "hex": "#8B949E",
        "css": "#8b949e",
        "conceptId": "telegram.forum.topic"
      },
      "ops": {
        "colorKey": "topicOps",
        "hex": "#58A6FF",
        "css": "#58a6ff",
        "conceptId": "telegram.forum.topic"
      },
      "alerts": {
        "colorKey": "topicAlerts",
        "hex": "#F85149",
        "css": "#f85149",
        "conceptId": "telegram.forum.topic"
      },
      "liquidity/outs": {
        "colorKey": "topicLiquidityOuts",
        "hex": "#3FB950",
        "css": "#3fb950",
        "conceptId": "telegram.forum.topic.liquidity_outs"
      },
      "accounting": {
        "colorKey": "topicAccounting",
        "hex": "#D29922",
        "css": "#d29922",
        "conceptId": "telegram.forum.topic.accounting"
      }
    },
    "allAccountingTopics": {
      "Deposits": {
        "colorKey": "topicDeposits",
        "hex": "#3FB950",
        "css": "#3fb950",
        "conceptId": "telegram.surface.all_accounting"
      },
      "Withdrawals": {
        "colorKey": "topicWithdrawals",
        "hex": "#F85149",
        "css": "#f85149",
        "conceptId": "telegram.surface.all_accounting"
      },
      "Reconcile": {
        "colorKey": "topicReconcile",
        "hex": "#8B949E",
        "css": "#8b949e",
        "conceptId": "telegram.surface.all_accounting"
      }
    },
    "forumIconColorHex": [
      "#8B949E",
      "#D29922",
      "#F85149",
      "#3FB950",
      "#58A6FF",
      "#A371F7",
      "#F778BA"
    ],
    "scrapeWireTaxonomyPath": "/registry/scrape-wire-taxonomy.json",
    "bookColorNote": "Deposit book chips resolve hex via scrape-wire bookRegistry colorKey/hex"
  },
  "glossary": {
    "path": "/portal/glossary/",
    "boardPath": "/portal/partners/",
    "conceptIds": [
      "telegram.wire",
      "telegram.package_group",
      "telegram.forum.topic",
      "telegram.topic_map",
      "telegram.forum.topic.accounting",
      "telegram.dod.visual_proof",
      "telegram.forum.topic.liquidity_outs",
      "telegram.surface",
      "telegram.surface.all_accounting",
      "telegram.seat_desk",
      "telegram.deposit_rail",
      "telegram.handshake",
      "telegram.membership",
      "telegram.topic_plan_row",
      "telegram.message.incoming",
      "telegram.message.outgoing",
      "telegram.message.alert",
      "telegram.message.command",
      "telegram.message.receipt",
      "telegram.status.delivered",
      "telegram.status.failed",
      "telegram.action.reply",
      "telegram.action.forward",
      "telegram.action.pin"
    ]
  },
  "jsonlActions": [
    "create_package_group",
    "ack_package_group_wired",
    "ack_package_group_linked",
    "ack_dm_seat_designated",
    "ack_forum_invite_sent"
  ],
  "readinessPhases": [
    "blocked",
    "forum_ready",
    "designated",
    "operator_ready"
  ],
  "dmSeatStatuses": [
    "none",
    "designated",
    "linked",
    "shared"
  ],
  "membershipStatuses": [
    "unknown",
    "understaffed",
    "house_only",
    "partner_present",
    "extended"
  ],
  "verifyChecks": [
    "partner_code",
    "create_artifact",
    "title_grammar",
    "soft_wired",
    "factory_registry",
    "registry_matches_wired",
    "registry_title",
    "factory_linked_ack",
    "linked_matches_registry",
    "dm_seat",
    "forum_metadata",
    "live_forum_title"
  ],
  "lanes": [
    {
      "id": "registry",
      "group": "forum",
      "summary": "package_group_registry row"
    },
    {
      "id": "jsonl_create",
      "group": "audit",
      "summary": "create_package_group in JSONL"
    },
    {
      "id": "jsonl_wired",
      "group": "audit",
      "summary": "ack_package_group_wired"
    },
    {
      "id": "jsonl_linked",
      "group": "audit",
      "summary": "ack_package_group_linked"
    },
    {
      "id": "jsonl_dm_designated",
      "group": "audit",
      "summary": "ack_dm_seat_designated or registry requested_by"
    },
    {
      "id": "jsonl_forum_invite_sent",
      "group": "audit",
      "summary": "ack_forum_invite_sent (when 2·house!)"
    },
    {
      "id": "known_chat",
      "group": "forum",
      "summary": "ops_telegram_known_chats active row"
    },
    {
      "id": "known_forum",
      "group": "forum",
      "summary": "supergroup + is_forum"
    },
    {
      "id": "bot_forum_admin",
      "group": "forum",
      "summary": "bot administrator on forum"
    },
    {
      "id": "forum_members",
      "group": "forum",
      "summary": "member count tell; fails at 2·house! when linked"
    },
    {
      "id": "forum_invite_gap",
      "group": "forum",
      "summary": "linked DM but partner not in group"
    },
    {
      "id": "surface_slug",
      "group": "forum",
      "summary": "known chat surface_slug set"
    },
    {
      "id": "invite_link",
      "group": "forum",
      "summary": "registry invite_link stored"
    },
    {
      "id": "forum_metadata",
      "group": "forum",
      "summary": "reports/telegram/forums/{CODE}.json"
    },
    {
      "id": "forum_topics",
      "group": "forum",
      "summary": "partner plan: general/ops/alerts/liquidity/outs/accounting"
    },
    {
      "id": "route_alerts",
      "group": "routing",
      "summary": "alerts → forum thread"
    },
    {
      "id": "route_plays",
      "group": "routing",
      "summary": "plays → forum thread"
    },
    {
      "id": "route_toc",
      "group": "routing",
      "summary": "toc → forum thread"
    },
    {
      "id": "surface_env_pkg",
      "group": "routing",
      "summary": "TELEGRAM_SURFACES pkg-{code} bind"
    },
    {
      "id": "seat_tree",
      "group": "operator",
      "summary": "tree_nodes row for designated seat"
    },
    {
      "id": "dm_designated",
      "group": "operator",
      "summary": "registry requested_by / designate-dm-seat"
    },
    {
      "id": "dm_telegram",
      "group": "operator",
      "summary": "tree_nodes.telegram_id linked"
    },
    {
      "id": "welcome_dm",
      "group": "operator",
      "summary": "package-room welcome can send"
    },
    {
      "id": "bot_commands",
      "group": "operator",
      "summary": "/status · /dossier · /limits · /seat · play callbacks"
    }
  ],
  "cli": [
    {
      "id": "onboard-create",
      "script": "bun tools/onboard-partner-package.ts",
      "ref": "call-sign",
      "example": "bun tools/onboard-partner-package.ts ASH-001 --create-package-group"
    },
    {
      "id": "link-package-group",
      "script": "bun run telegram:ops -- link-package-group",
      "ref": "partner CODE",
      "flags": [
        "--invite",
        "--no-dm",
        "--no-ack",
        "--requested-by"
      ]
    },
    {
      "id": "designate-dm-seat",
      "script": "bun run telegram:ops -- designate-dm-seat",
      "ref": "partner CODE + call-sign",
      "flags": [
        "--force"
      ]
    },
    {
      "id": "send-forum-invite",
      "script": "bun run telegram:ops -- send-forum-invite",
      "ref": "partner CODE",
      "flags": [
        "--all",
        "--dry-run",
        "--force"
      ]
    },
    {
      "id": "handshake-verify",
      "script": "bun run telegram:handshake:verify",
      "ref": "partner CODE",
      "flags": [
        "--live",
        "--json"
      ]
    },
    {
      "id": "handshake-desk",
      "script": "bun run telegram:handshake:desk",
      "ref": "optional CODE",
      "flags": [
        "--refresh",
        "--live",
        "--invite-gap",
        "--detail",
        "--json",
        "--db",
        "--path"
      ],
      "exitCodes": {
        "0": "ok",
        "1": "--invite-gap with gaps"
      }
    },
    {
      "id": "handshake-readiness",
      "script": "bun run telegram:handshake:readiness",
      "ref": "optional CODE",
      "flags": [
        "--detail",
        "--deep",
        "--live",
        "--json",
        "--db",
        "--path"
      ],
      "exitCodes": {
        "0": "ok",
        "1": "any row phase blocked"
      },
      "note": "Duplicate: bun run telegram:ops -- readiness (same tool)"
    },
    {
      "id": "handshake-invite-gap",
      "script": "bun run telegram:handshake:invite-gap",
      "ref": "optional CODE",
      "flags": [
        "--refresh",
        "--send",
        "--dry-run",
        "--force",
        "--json"
      ],
      "exitCodes": {
        "0": "no gaps",
        "1": "2·house! rows remain"
      }
    },
    {
      "id": "seat-map",
      "script": "bun run telegram:ops -- seat-map",
      "ref": "none"
    },
    {
      "id": "link-chat",
      "script": "bun tools/telegram-link-chat.ts",
      "ref": "call-sign + telegram ref"
    },
    {
      "id": "seat-desk-partner-message",
      "script": "bun run seat:desk:partner-message",
      "ref": "call-sign",
      "flags": [
        "--template",
        "--post",
        "--json"
      ],
      "note": "Templates: confirm-active · todo · reply-hint · topic-intake · topic-rails · topic-accounting"
    },
    {
      "id": "seat-desk-topic-prompts",
      "script": "bun run seat:desk:topic-prompts",
      "ref": "call-sign",
      "flags": [
        "--post"
      ],
      "note": "Posts topic-intake + topic-rails to Liquidity/Outs thread"
    },
    {
      "id": "seat-desk-accounting-prompt",
      "script": "bun run seat:desk:accounting-prompt",
      "ref": "call-sign",
      "flags": [
        "--post",
        "--thread-id"
      ],
      "note": "Accounting topic bootstrap when bot lacks can_manage_topics"
    },
    {
      "id": "package-group-accounting",
      "script": "bun run telegram:package-group:accounting",
      "ref": "optional partner CODE",
      "flags": [
        "--all",
        "--ensure-topics",
        "--accounting-prompt"
      ],
      "note": "Bulk ensure Accounting topic + prompt for all linked forums"
    },
    {
      "id": "all-accounting-channel",
      "script": "bun run telegram:all-accounting",
      "ref": "chat id",
      "flags": [
        "--brand",
        "--post-prompt"
      ],
      "note": "House rollup supergroup — TELEGRAM_ACCOUNTING_CHAT_ID"
    },
    {
      "id": "catalog-research",
      "script": "bun run telegram:catalog:research",
      "ref": "none",
      "flags": [
        "--json",
        "--llm",
        "--partner"
      ],
      "note": "Enhancement proposals → reports/telegram/catalog-enhancements.json"
    },
    {
      "id": "catalog-apply-enhancements",
      "script": "bun run telegram:catalog:apply-enhancements",
      "ref": "none",
      "flags": [
        "--dry-run",
        "--all"
      ],
      "note": "Merge safe changes → catalog-overrides.json + regenerate handshake catalog"
    }
  ],
  "envKeys": [
    {
      "key": "TELEGRAM_BOT_FACTORY",
      "plane": "factory",
      "purpose": "Bot token (preferred)"
    },
    {
      "key": "TELEGRAM_OPS_CHAT_ID",
      "plane": "factory",
      "purpose": "Default ops supergroup fallback"
    },
    {
      "key": "TELEGRAM_ACCOUNTING_CHAT_ID",
      "plane": "factory",
      "purpose": "Cross-partner all-accounting supergroup (alias all-accounting surface)"
    },
    {
      "key": "TELEGRAM_SURFACES",
      "plane": "factory",
      "purpose": "JSON slug→chat_id; includes pkg-{code} for package forums"
    },
    {
      "key": "TELEGRAM_TOPICS",
      "plane": "factory",
      "purpose": "Forum thread id map"
    },
    {
      "key": "OPS_DB_PATH",
      "plane": "factory",
      "purpose": "SQLite ops db (registry + known chats)"
    }
  ]
}
