/mnt/rpi/pi-config/README.md:12:- `scripts/apply-pi-shutdown-hook-patch.py`
/mnt/rpi/pi-config/README.md:30:- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
/mnt/rpi/pi-config/README.md:31:- Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
/mnt/rpi/pi-config/README.md:35:- Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`
/mnt/rpi/repos/ballbox/PLAN.md:29:- Payments are now owned inside Ballbox host app: native MercadoPago webhook intake exists under `/notification/mercadopago` with signature validation and order fetch.
/mnt/rpi/repos/ballbox/PLAN.md:46:- Network admin frontend client-state, mutation orchestration, and local form-state wiring have started moving out of `components/network-admin-client.tsx` into dedicated hooks so the client trends toward a shell.
/mnt/rpi/repos/ballbox/PLAN.md:48:- Network admin frontend layout/class wiring now also lives in a dedicated shell component so `components/network-admin-client.tsx` is closer to trivial hook composition.
/mnt/rpi/repos/ballbox/PLAN.md:148:- Payments integration health and sessions routes now report Ballbox-internal native MercadoPago status plus webhook/order metadata.
/mnt/rpi/repos/ballbox/PLAN.md:149:- Payments native MercadoPago webhook flow now lands in Ballbox, validates canonical payment correlation from fetched orders, and persists approved payments as `PaymentSession` records.
/mnt/rpi/repos/ballbox/PLAN.md:247:- Ballbox persists approved payments locally through the native MercadoPago webhook flow at `/notification/mercadopago`.
/mnt/rpi/repos/ballbox/docs/domain-model-target.md:39:  - optional future extraction if approvals/webhooks need their own lifecycle
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:4:- Ballbox owns the admin surface, native MercadoPago webhook intake, and native QR order creation.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:5:- `/notification/mercadopago` is the first-party MercadoPago webhook route with signature validation and order fetch.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:21:- Native MercadoPago webhook intake validates `x-signature` + `x-request-id`, fetches the upstream order, maps approved orders into the canonical Ballbox payment shape, and persists them locally.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:28:- Ballbox native MercadoPago webhook path is `/notification/mercadopago`.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:32:- `NEXT_PUBLIC_SITE_URL` is required if Ballbox should expose a concrete `recommendedMercadoPagoWebhookUrl`.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:35:- Host app now owns the native MercadoPago order creation + webhook persistence path inside Ballbox.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:36:- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:39:- End-to-end payment validation now depends on Ballbox native MercadoPago webhook configuration, not just local admin CRUD.
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:44:  - public HTTPS exposure for Ballbox webhook endpoint `/notification/mercadopago`
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:47:  - MercadoPago reaches Ballbox webhook publicly
/mnt/rpi/repos/ballbox/docs/implemented-payments.md:51:  - the exact public webhook URL and observed payload quirks are documented after the run
/mnt/rpi/repos/ballbox/docs/screen-runtime-delivery-investigation.md:59:  - requires extracting runtime logic out of React component state/hooks
/mnt/rpi/repos/ballbox/docs/implemented-host-app.md:22:- Ballbox owns native MercadoPago QR order creation at `/api/integrations/payments/orders` and native webhook intake at `/notification/mercadopago`.
/mnt/rpi/repos/ballbox/tests/contracts/payments/orders-route.test.ts:21:    webhookSecret: "secret",
/mnt/rpi/repos/ballbox/tests/contracts/payments/auth.test.ts:21:        "Use the native MercadoPago flow: create orders at /api/integrations/payments/orders and receive webhooks at /notification/mercadopago.",
/mnt/rpi/repos/ballbox/tests/contracts/payments/auth.test.ts:30:      message: "Use the native MercadoPago webhook at /notification/mercadopago.",
/mnt/rpi/repos/ballbox/tests/contracts/payments/auth.test.ts:36:    process.env.BALLBOX_MERCADOPAGO_WEBHOOK_SECRET = "mp-webhook";
/mnt/rpi/repos/ballbox/tests/contracts/payments/auth.test.ts:48:      mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/ballbox/tests/contracts/payments/auth.test.ts:51:        webhookSecretConfigured: true,
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:5:  isValidMercadoPagoWebhookSignature,
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:21:  it("validates webhook signature with canonical manifest", () => {
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:22:    const webhookSecret = "webhook-secret";
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:27:    const digest = crypto.createHmac("sha256", webhookSecret).update(manifest).digest("hex");
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:29:    const result = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:30:      webhookSecret,
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:40:    const webhookSecret = "webhook-secret";
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:44:    const digest = crypto.createHmac("sha256", webhookSecret).update(manifest).digest("hex").toUpperCase();
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:46:    const result = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/ballbox/tests/unit/payments/mercadopago.test.ts:47:      webhookSecret,
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:69:  - run one public webhook smoke against the native MercadoPago webhook path
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:73:- Progress (2026-04-19): PAY-2 cleanup removed bridge-oriented operational status from host-app health/docs; OPS-1 is now focused on validating the native MercadoPago order-create + webhook-persist path only.
/mnt/rpi/repos/ballbox/docs/next-steps-queue.md:127:  - native MercadoPago webhook + QR order lifecycle is now owned in Ballbox routes/lib
/mnt/rpi/repos/ballbox/tests/unit/README.md:3:- Scope: single-module/component/hook behavior with controlled dependencies.
/mnt/rpi/repos/ballbox/package.json:20:    "prepare": "node scripts/install-git-hooks.mjs",
/mnt/rpi/repos/ballbox/package.json:34:    "@hookform/resolvers": "^3.10.0",
/mnt/rpi/repos/ballbox/package.json:77:    "react-hook-form": "^7.60.0",
/mnt/rpi/repos/ballbox/scripts/install-git-hooks.mjs:9:const hooksDir = resolve(repoRoot, ".githooks")
/mnt/rpi/repos/ballbox/scripts/install-git-hooks.mjs:11:if (!existsSync(gitDir) || !existsSync(hooksDir)) {
/mnt/rpi/repos/ballbox/scripts/install-git-hooks.mjs:16:  execSync("git config core.hooksPath .githooks", {
/mnt/rpi/repos/ballbox/docs/maintainability-priorities-2026-03.md:93:  - Introduce small domain hooks such as `useNetworkAdminData()` and `useNetworkAdminMutations()`.
/mnt/rpi/repos/ballbox/docs/maintainability-priorities-2026-03.md:96:  - `network-admin-client` is now a thin shell over config, hooks, shell, styles, and smaller sections/cards.
/mnt/rpi/repos/ballbox/docs/maintainability-priorities-2026-03.md:97:  - `players` now has dedicated client config, state hook, shell, and smaller stats/create/list-card seams.
/mnt/rpi/repos/ballbox/docs/maintainability-priorities-2026-03.md:98:  - `sports` now also has dedicated client config, state hook, shell, and smaller coach/event sections.
/mnt/rpi/repos/ballbox/docs/maintainability-priorities-2026-03.md:221:  - focused component/hook coverage for `network`, `players`, and `sports` admin seams
/mnt/rpi/repos/ballbox/docs/atc-access-request-checklist.md:17:- Any webhook/event support for booking changes
/mnt/rpi/repos/ballbox/docs/atc-integration-discovery.md:132:- no visible webhook/event model for booking changes
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:11:      '@hookform/resolvers':
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:13:        version: 3.10.0(react-hook-form@7.71.1(react@19.2.0))
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:140:      react-hook-form:
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:581:  '@hookform/resolvers@3.10.0':
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:584:      react-hook-form: ^7.0.0
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:2501:  eslint-plugin-react-hooks@7.0.1:
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:3344:  react-hook-form@7.71.1:
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:4235:  '@hookform/resolvers@3.10.0(react-hook-form@7.71.1(react@19.2.0))':
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:4237:      react-hook-form: 7.71.1(react@19.2.0)
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:6077:      eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1))
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:6170:  eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)):
/mnt/rpi/repos/ballbox/pnpm-lock.yaml:7009:  react-hook-form@7.71.1(react@19.2.0):
/mnt/rpi/repos/ballbox/lib/platform.ts:49:    capabilities: ["QR checkout", "Status tracking", "Approval callback intake", "Webhook audit", "Reconciliation"],
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:32:  webhookSecret: string | null;
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:53:    webhookSecret: resolveStringEnv(
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:117:export function isValidMercadoPagoWebhookSignature(input: {
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:118:  webhookSecret: string | null;
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:123:  if (!input.webhookSecret) {
/mnt/rpi/repos/ballbox/lib/payments-mercadopago.ts:153:    const expectedDigest = crypto.createHmac("sha256", input.webhookSecret as string).update(manifest).digest("hex");
/mnt/rpi/repos/ballbox/app/api/integrations/payments/sessions/route.ts:18:    mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/ballbox/app/api/integrations/payments/sessions/route.ts:19:    recommendedMercadoPagoWebhookUrl: publicAppUrl ? `${publicAppUrl}/notification/mercadopago` : null,
/mnt/rpi/repos/ballbox/app/api/integrations/payments/sessions/route.ts:21:    mercadopagoWebhookSecretConfigured: Boolean(mercadoPago.webhookSecret),
/mnt/rpi/repos/ballbox/app/api/integrations/payments/health/route.ts:16:    mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/ballbox/app/api/integrations/payments/health/route.ts:17:    recommendedMercadoPagoWebhookUrl: publicAppUrl ? `${publicAppUrl}/notification/mercadopago` : null,
/mnt/rpi/repos/ballbox/app/api/integrations/payments/health/route.ts:21:      webhookSecretConfigured: Boolean(mercadoPago.webhookSecret),
/mnt/rpi/repos/ballbox/app/api/integrations/payments/health/route.ts:22:      webhookSecretEnvVar: "BALLBOX_MERCADOPAGO_WEBHOOK_SECRET",
/mnt/rpi/repos/ballbox/app/api/integrations/payments/health/route.ts:28:      "mercadopago webhook intake",
/mnt/rpi/repos/ballbox/app/api/integrations/payments/orders/route.ts:27:      webhookSecret: Boolean(config.webhookSecret),
/mnt/rpi/repos/ballbox/app/api/integrations/payments/approved/route.ts:10:      message: "Use the native MercadoPago flow: create orders at /api/integrations/payments/orders and receive webhooks at /notification/mercadopago.",
/mnt/rpi/repos/ballbox/app/api/integrations/payments/approved/route.ts:21:      message: "Use the native MercadoPago webhook at /notification/mercadopago.",
/mnt/rpi/repos/ballbox/app/api/admin/summary/route.ts:41:        mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/ballbox/app/api/admin/summary/route.ts:43:        mercadopagoWebhookSecretConfigured: Boolean(mercadoPago.webhookSecret),
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:7:  isValidMercadoPagoWebhookSignature,
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:24:  const signatureCheck = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:25:    webhookSecret: config.webhookSecret,
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:33:    console.warn("[payments][mercadopago-webhook] rejected", {
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:53:    console.warn("[payments][mercadopago-webhook] order fetch failed", {
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:73:    console.info("[payments][mercadopago-webhook] ignored", {
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:92:  console.info("[payments][mercadopago-webhook] approved persisted", {
/mnt/rpi/repos/ballbox/app/notification/mercadopago/route.ts:103:    source: "mercadopago-webhook",
/mnt/rpi/repos/ballbox/components.json:18:    "hooks": "@/hooks"
/mnt/rpi/repos/ballbox/README.md:17:  - Ballbox recibe webhooks de MercadoPago en `/notification/mercadopago`
/mnt/rpi/repos/ballbox/README.md:46:- `BALLBOX_MERCADOPAGO_WEBHOOK_SECRET`: secreto webhook de MercadoPago para validar `x-signature`
/mnt/rpi/repos/ballbox/README.md:110:- Hay hook `pre-push` versionado en `.githooks/pre-push`.
/mnt/rpi/repos/ballbox/README.md:127:- Webhook MercadoPago implementado en host app (`/notification/mercadopago`)
/mnt/rpi/repos/ballbox/README.md:133:- Falta validación manual end-to-end con configuración real de MercadoPago y webhook público
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:11:      '@hookform/resolvers':
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:13:        version: 3.10.0(react-hook-form@7.71.1(react@19.2.0))
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:140:      react-hook-form:
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:581:  '@hookform/resolvers@3.10.0':
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:584:      react-hook-form: ^7.0.0
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:2501:  eslint-plugin-react-hooks@7.0.1:
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:3344:  react-hook-form@7.71.1:
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:4235:  '@hookform/resolvers@3.10.0(react-hook-form@7.71.1(react@19.2.0))':
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:4237:      react-hook-form: 7.71.1(react@19.2.0)
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:6077:      eslint-plugin-react-hooks: 7.0.1(eslint@9.39.4(jiti@2.6.1))
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:6170:  eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)):
/mnt/rpi/repos/FelipeVersion/pnpm-lock.yaml:7009:  react-hook-form@7.71.1(react@19.2.0):
/mnt/rpi/repos/FelipeVersion/package-lock.json:12:        "@hookform/resolvers": "^3.10.0",
/mnt/rpi/repos/FelipeVersion/package-lock.json:55:        "react-hook-form": "^7.60.0",
/mnt/rpi/repos/FelipeVersion/package-lock.json:1221:    "node_modules/@hookform/resolvers": {
/mnt/rpi/repos/FelipeVersion/package-lock.json:1223:      "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
/mnt/rpi/repos/FelipeVersion/package-lock.json:1227:        "react-hook-form": "^7.0.0"
/mnt/rpi/repos/FelipeVersion/package-lock.json:6349:        "eslint-plugin-react-hooks": "^7.0.0",
/mnt/rpi/repos/FelipeVersion/package-lock.json:6578:    "node_modules/eslint-plugin-react-hooks": {
/mnt/rpi/repos/FelipeVersion/package-lock.json:6580:      "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz",
/mnt/rpi/repos/FelipeVersion/package-lock.json:9023:    "node_modules/react-hook-form": {
/mnt/rpi/repos/FelipeVersion/package-lock.json:9025:      "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.1.tgz",
/mnt/rpi/repos/FelipeVersion/package-lock.json:9033:        "url": "https://opencollective.com/react-hook-form"
/mnt/rpi/repos/FelipeVersion/scripts/install-git-hooks.mjs:9:const hooksDir = resolve(repoRoot, ".githooks")
/mnt/rpi/repos/FelipeVersion/scripts/install-git-hooks.mjs:11:if (!existsSync(gitDir) || !existsSync(hooksDir)) {
/mnt/rpi/repos/FelipeVersion/scripts/install-git-hooks.mjs:16:  execSync("git config core.hooksPath .githooks", {
/mnt/rpi/repos/FelipeVersion/lib/platform.ts:49:    capabilities: ["QR checkout", "Status tracking", "Approval callback intake", "Webhook audit", "Reconciliation"],
/mnt/rpi/repos/FelipeVersion/tests/contracts/payments/orders-route.test.ts:21:    webhookSecret: "secret",
/mnt/rpi/repos/FelipeVersion/tests/contracts/payments/auth.test.ts:179:    process.env.BALLBOX_MERCADOPAGO_WEBHOOK_SECRET = "mp-webhook";
/mnt/rpi/repos/FelipeVersion/tests/contracts/payments/auth.test.ts:192:        webhookSecretConfigured: true,
/mnt/rpi/repos/FelipeVersion/README.md:17:  - Ballbox recibe webhooks de MercadoPago en `/notification/mercadopago`
/mnt/rpi/repos/FelipeVersion/README.md:45:- `BALLBOX_MERCADOPAGO_WEBHOOK_SECRET`: secreto webhook de MercadoPago para validar `x-signature`
/mnt/rpi/repos/FelipeVersion/README.md:84:- Hay hook `pre-push` versionado en `.githooks/pre-push`.
/mnt/rpi/repos/FelipeVersion/README.md:101:- Webhook MercadoPago implementado en host app (`/notification/mercadopago`)
/mnt/rpi/repos/FelipeVersion/README.md:107:- Falta validación manual end-to-end con configuración real de MercadoPago y webhook público
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:32:  webhookSecret: string | null;
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:53:    webhookSecret: resolveStringEnv(
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:117:export function isValidMercadoPagoWebhookSignature(input: {
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:118:  webhookSecret: string | null;
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:123:  if (!input.webhookSecret) {
/mnt/rpi/repos/FelipeVersion/lib/payments-mercadopago.ts:153:    const expectedDigest = crypto.createHmac("sha256", input.webhookSecret as string).update(manifest).digest("hex");
/mnt/rpi/repos/FelipeVersion/PLAN.md:28:- Payments are now being consolidated into Ballbox host app: native MercadoPago webhook intake exists under `/notification/mercadopago` with signature validation and order fetch.
/mnt/rpi/repos/FelipeVersion/PLAN.md:45:- Network admin frontend client-state, mutation orchestration, and local form-state wiring have started moving out of `components/network-admin-client.tsx` into dedicated hooks so the client trends toward a shell.
/mnt/rpi/repos/FelipeVersion/PLAN.md:47:- Network admin frontend layout/class wiring now also lives in a dedicated shell component so `components/network-admin-client.tsx` is closer to trivial hook composition.
/mnt/rpi/repos/FelipeVersion/PLAN.md:110:  - none required for current payments callback/webhook flow (legacy bridge compatibility remains temporarily accepted)
/mnt/rpi/repos/FelipeVersion/PLAN.md:124:- Payments integration health and sessions routes now report Ballbox-internal status plus MercadoPago configuration/webhook metadata.
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:5:  isValidMercadoPagoWebhookSignature,
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:21:  it("validates webhook signature with canonical manifest", () => {
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:22:    const webhookSecret = "webhook-secret";
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:27:    const digest = crypto.createHmac("sha256", webhookSecret).update(manifest).digest("hex");
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:29:    const result = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:30:      webhookSecret,
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:40:    const webhookSecret = "webhook-secret";
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:44:    const digest = crypto.createHmac("sha256", webhookSecret).update(manifest).digest("hex").toUpperCase();
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:46:    const result = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/FelipeVersion/tests/unit/payments/mercadopago.test.ts:47:      webhookSecret,
/mnt/rpi/repos/FelipeVersion/tests/unit/README.md:3:- Scope: single-module/component/hook behavior with controlled dependencies.
/mnt/rpi/repos/FelipeVersion/package.json:16:    "prepare": "node scripts/install-git-hooks.mjs",
/mnt/rpi/repos/FelipeVersion/package.json:29:    "@hookform/resolvers": "^3.10.0",
/mnt/rpi/repos/FelipeVersion/package.json:72:    "react-hook-form": "^7.60.0",
/mnt/rpi/repos/FelipeVersion/docs/domain-model-target.md:39:  - optional future extraction if approvals/webhooks need their own lifecycle
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:5:- Ballbox now also owns a first-party MercadoPago webhook intake route at `/notification/mercadopago` with signature validation and order fetch.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:25:- MercadoPago webhook intake now validates `x-signature` + `x-request-id` against a local webhook secret before fetching order details.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:34:- Ballbox native MercadoPago webhook path is `/notification/mercadopago`.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:43:- Host app now includes local MercadoPago webhook signature validation and order fetch primitives.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:44:- Remaining gap is operational/manual: one real public webhook + one real approved payment persisted and visible in `/admin/payments`.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:47:- End-to-end payment validation now depends on Ballbox native MercadoPago webhook configuration, not just local admin CRUD.
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:53:  - public HTTPS exposure for Ballbox webhook endpoint `/notification/mercadopago`
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:56:  - MercadoPago reaches Ballbox webhook publicly
/mnt/rpi/repos/FelipeVersion/docs/implemented-payments.md:60:  - the exact public webhook URL and observed payload quirks are documented after the run
/mnt/rpi/repos/FelipeVersion/docs/maintainability-priorities-2026-03.md:93:  - Introduce small domain hooks such as `useNetworkAdminData()` and `useNetworkAdminMutations()`.
/mnt/rpi/repos/FelipeVersion/docs/maintainability-priorities-2026-03.md:96:  - `network-admin-client` is now a thin shell over config, hooks, shell, styles, and smaller sections/cards.
/mnt/rpi/repos/FelipeVersion/docs/maintainability-priorities-2026-03.md:97:  - `players` now has dedicated client config, state hook, shell, and smaller stats/create/list-card seams.
/mnt/rpi/repos/FelipeVersion/docs/maintainability-priorities-2026-03.md:98:  - `sports` now also has dedicated client config, state hook, shell, and smaller coach/event sections.
/mnt/rpi/repos/FelipeVersion/docs/maintainability-priorities-2026-03.md:221:  - focused component/hook coverage for `network`, `players`, and `sports` admin seams
/mnt/rpi/repos/FelipeVersion/docs/screen-runtime-delivery-investigation.md:59:  - requires extracting runtime logic out of React component state/hooks
/mnt/rpi/repos/FelipeVersion/docs/atc-access-request-checklist.md:17:- Any webhook/event support for booking changes
/mnt/rpi/repos/FelipeVersion/docs/atc-integration-discovery.md:132:- no visible webhook/event model for booking changes
/mnt/rpi/repos/FelipeVersion/docs/next-steps-queue.md:69:  - run one public webhook smoke with real callback path
/mnt/rpi/repos/FelipeVersion/docs/next-steps-queue.md:121:  - migrate remaining bridge-owned MercadoPago flow (webhook + order lifecycle) into Ballbox routes/lib
/mnt/rpi/repos/FelipeVersion/docs/next-steps-queue.md:124:- Progress (2026-04-14): host app now includes MercadoPago webhook route (`/notification/mercadopago`) plus signature verification and order fetch primitives.
/mnt/rpi/repos/FelipeVersion/components.json:18:    "hooks": "@/hooks"
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:7:  isValidMercadoPagoWebhookSignature,
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:24:  const signatureCheck = isValidMercadoPagoWebhookSignature({
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:25:    webhookSecret: config.webhookSecret,
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:33:    console.warn("[payments][mercadopago-webhook] rejected", {
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:53:    console.warn("[payments][mercadopago-webhook] order fetch failed", {
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:73:    console.info("[payments][mercadopago-webhook] ignored", {
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:92:  console.info("[payments][mercadopago-webhook] approved persisted", {
/mnt/rpi/repos/FelipeVersion/app/notification/mercadopago/route.ts:103:    source: "mercadopago-webhook",
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/sessions/route.ts:21:    mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/sessions/route.ts:23:    recommendedMercadoPagoWebhookUrl: publicAppUrl ? `${publicAppUrl}/notification/mercadopago` : null,
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/sessions/route.ts:27:    mercadopagoWebhookSecretConfigured: Boolean(mercadoPago.webhookSecret),
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/health/route.ts:18:    mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/health/route.ts:20:    recommendedMercadoPagoWebhookUrl: publicAppUrl ? `${publicAppUrl}/notification/mercadopago` : null,
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/health/route.ts:26:      webhookSecretConfigured: Boolean(mercadoPago.webhookSecret),
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/health/route.ts:27:      webhookSecretEnvVar: "BALLBOX_MERCADOPAGO_WEBHOOK_SECRET",
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/health/route.ts:32:      "mercadopago webhook intake",
/mnt/rpi/repos/FelipeVersion/app/api/integrations/payments/orders/route.ts:26:      webhookSecret: Boolean(config.webhookSecret),
/mnt/rpi/repos/FelipeVersion/app/api/admin/summary/route.ts:43:        mercadopagoWebhookPath: "/notification/mercadopago",
/mnt/rpi/repos/FelipeVersion/app/api/admin/summary/route.ts:48:        mercadopagoWebhookSecretConfigured: Boolean(mercadoPago.webhookSecret),


**Considering README structure**

I'm noticing that the README has a specific line that might relate to titles or hooks, so it’s worth inspecting around line 30 for more context. It makes me wonder if adding a title for the hook could enhance clarity or grab attention better. I should think about how that could improve understanding for anyone reading the document. Let's check and see what’s there to ensure the best presentation!


Make this repo both the source of truth **and the live Pi config directory**.

## Notes

- Direct, compressed, low-noise communication is the default style.
- Session close hook now prompts once before switch/fork/exit to encourage saving durable memory into agents-database.
- Session memory now uses an external local dispatcher on `127.0.0.1:17882`; the hook only captures session identity/context, the dispatcher enqueues metadata, and a background analyzer decides whether anything durable is worth saving.
- The analyzer uses `pi` in background with a cheap model (`gpt-5.4-mini`) and thinking off when possible.
- Use `./scripts/install-session-memory-dispatcher.service` to keep the dispatcher alive under systemd user service if desired.
- The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.
- Reapply the runtime patch after Pi upgrades only if you still use that fallback: `./scripts/apply-pi-shutdown-hook-patch.py`
- **Pending validation:** confirm the default communication mode auto-loads in every new Pi session.
- Remote laptop workflow can be driven from this Pi using `bin/laptop-run`, `bin/laptop-read`, `bin/laptop-write`, `bin/laptop-rsync`, `bin/laptop-mount`, and `bin/laptop-umount` against host `sebastians-macbook-pro-2`.

## Local server routes on `ballbox-first`

[42 more lines in file. Use offset=40 to continue.]


**Searching for hook title**

The user is asking for the title of a hook related to `memory_dispatcher`. I should check if there's a file in the UI that I can't read. It might be helpful to search for the prompt text within the session close hook code located under scripts.  I need to look for `rg session_before_shutdown` to see if I can find what they need there. Let's keep exploring this!

bash

cd /mnt/rpi/pi-config && rg -n "session_close|before_shutdown|memory dispatcher|memory_dispatcher|Save insights|insights" . --glob '!**/node_modules/**' --glob '!**/.git/**'


./extensions/session-memory-dispatcher.ts:85:		throw new Error(`session memory dispatcher failed: ${response.status} ${response.statusText}`);
./extensions/session-memory-dispatcher.ts:150:	pi.on("session_before_shutdown", async (event, ctx) => {
./extensions/session-memory-dispatcher.ts:151:		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Before exit: save durable memory?", timeoutMs: 12000 });
./scripts/install-session-memory-dispatcher.service:2:Description=Pi session memory dispatcher
./scripts/apply-pi-shutdown-hook-patch.py:48:                """/** Fired before forking a session (can be cancelled) */\nexport interface SessionBeforeForkEvent {\n    type: \"session_before_fork\";\n    entryId: string;\n}\n/** Fired before graceful shutdown paths such as Ctrl+C, Ctrl+D, /quit, or extension-triggered exit (can be cancelled). */\nexport interface SessionBeforeShutdownEvent {\n    type: \"session_before_shutdown\";\n    reason: \"ctrl_c\" | \"ctrl_d\" | \"command\" | \"extension\" | \"signal\" | \"rpc_eof\" | \"rpc_shutdown\" | \"unknown\";\n    signal?: \"SIGHUP\" | \"SIGTERM\";\n}\n/** Fired before context compaction (can be cancelled or customized) */\nexport interface SessionBeforeCompactEvent {\n""",
./scripts/apply-pi-shutdown-hook-patch.py:60:                """    on(event: \"session_before_switch\", handler: ExtensionHandler<SessionBeforeSwitchEvent, SessionBeforeSwitchResult>): void;\n    on(event: \"session_before_fork\", handler: ExtensionHandler<SessionBeforeForkEvent, SessionBeforeForkResult>): void;\n    on(event: \"session_before_shutdown\", handler: ExtensionHandler<SessionBeforeShutdownEvent, SessionBeforeShutdownResult>): void;\n    on(event: \"session_before_compact\", handler: ExtensionHandler<SessionBeforeCompactEvent, SessionBeforeCompactResult>): void;\n""",
./scripts/apply-pi-shutdown-hook-patch.py:76:                """} ? SessionBeforeSwitchResult | undefined : TEvent extends {\n    type: \"session_before_fork\";\n} ? SessionBeforeForkResult | undefined : TEvent extends {\n    type: \"session_before_shutdown\";\n} ? SessionBeforeShutdownResult | undefined : TEvent extends {\n    type: \"session_before_compact\";\n} ? SessionBeforeCompactResult | undefined : TEvent extends {\n""",
./scripts/apply-pi-shutdown-hook-patch.py:82:                """    isSessionBeforeEvent(event) {\n        return (event.type === \"session_before_switch\" ||\n            event.type === \"session_before_fork\" ||\n            event.type === \"session_before_shutdown\" ||\n            event.type === \"session_before_compact\" ||\n            event.type === \"session_before_tree\");\n    }\n""",
./scripts/apply-pi-shutdown-hook-patch.py:104:                """    async emitBeforeShutdown(reason = \"unknown\", signal) {\n        const runner = this.runtimeHost.session.extensionRunner;\n        if (!runner?.hasHandlers(\"session_before_shutdown\")) {\n            return { cancelled: false };\n        }\n        const result = await runner.emit({\n            type: \"session_before_shutdown\",\n            reason,\n            signal,\n        });\n        return { cancelled: result?.cancel === true };\n    }\n    /**\n     * Gracefully shutdown the agent.\n     * Emits shutdown event to extensions, then exits.\n     */\n    isShuttingDown = false;\n    async shutdown(reason = \"unknown\", signal) {\n        if (this.isShuttingDown)\n            return;\n        this.isShuttingDown = true;\n        const beforeResult = await this.emitBeforeShutdown(reason, signal);\n        if (beforeResult.cancelled) {\n            this.isShuttingDown = false;\n            this.shutdownRequested = false;\n            this.shutdownRequestReason = \"extension\";\n            this.shutdownRequestSignal = undefined;\n            return;\n        }\n        this.unregisterSignalHandlers();\n        await this.runtimeHost.dispose();\n""",
./scripts/apply-pi-shutdown-hook-patch.py:138:                """    let detachInput = () => { };\n    async function emitBeforeShutdown(reason = \"rpc_shutdown\", signal) {\n        const runner = runtimeHost.session.extensionRunner;\n        if (!runner?.hasHandlers(\"session_before_shutdown\")) {\n            return { cancelled: false };\n        }\n        const result = await runner.emit({\n            type: \"session_before_shutdown\",\n            reason,\n            signal,\n        });\n        return { cancelled: result?.cancel === true };\n    }\n    async function shutdown(exitCode = 0, reason = \"rpc_shutdown\", signal) {\n        if (shuttingDown) {\n            process.exit(exitCode);\n        }\n        shuttingDown = true;\n        const beforeResult = await emitBeforeShutdown(reason, signal);\n        if (beforeResult.cancelled) {\n            shuttingDown = false;\n            shutdownRequested = false;\n            shutdownRequestReason = \"extension\";\n            shutdownRequestSignal = undefined;\n            return;\n        }\n        for (const cleanup of signalCleanupHandlers) {\n            cleanup();\n        }\n        unsubscribe?.();\n        await runtimeHost.dispose();\n        detachInput();\n        process.stdin.pause();\n        process.exit(exitCode);\n    }\n    async function checkShutdownRequested() {\n        if (!shutdownRequested)\n            return;\n        await shutdown(0, shutdownRequestReason, shutdownRequestSignal);\n    }\n""",
./README.md:34:- The old `session_before_shutdown` runtime patch remains as fallback only if you choose to keep it enabled.


			if (!allowCancel) return { cancel: false, skipped: true, error: message };
			const ok = await ctx.ui.confirm("Session analysis failed", "Continue closing this session without queueing?", dialogOptions);
			if (!ok) return { cancel: true, skipped: false, error: message };
			promptHandledForCurrentSession = true;
			return { cancel: false, skipped: true, error: message };
		}
	}

	pi.on("session_before_switch", async (event, ctx) => {
		const result = await runPrompt(ctx, `before_switch:${event.reason}`, { allowCancel: true, title: "Before closing this session" });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_before_fork", async (_event, ctx) => {
		const result = await runPrompt(ctx, "before_fork", { allowCancel: true, title: "Before forking away from this session" });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_before_shutdown", async (event, ctx) => {
		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Before exit: save durable memory?", timeoutMs: 12000 });
		if (result.cancel) return { cancel: true };
		return undefined;
	});

	pi.on("session_shutdown", async (_event, ctx) => {
		await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Before exit: save durable memory?", timeoutMs: 12000 });
	});

	pi.registerCommand("remember-session", {
		description: "Queue a session analysis from the current session",
		handler: async (_args, ctx) => {
			promptHandledForCurrentSession = false;
			await runPrompt(ctx, "manual_command", { allowCancel: false, title: "Queue session analysis from this session" });
		},
	});
}


edit

const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Before exit: save durable memory?", timeoutMs: 12000 });

const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Guardar insights en memoria?", timeoutMs: 12000 });

await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Before exit: save durable memory?", timeoutMs: 12000 });

await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Guardar insights en memoria?", timeoutMs: 12000 });


Successfully replaced 2 block(s) in /mnt/rpi/pi-config/extensions/session-memory-dispatcher.ts.

     ...
 147 		return undefined;
 148 	});
 149
 150 	pi.on("session_before_shutdown", async (event, ctx) => {
-151 		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Before exit: save durable memory?", timeoutMs: 12000 });
+151 		const result = await runPrompt(ctx, `before_shutdown:${event.reason}`, { allowCancel: true, title: "Guardar insights en memoria?", timeoutMs: 12000 });
 152 		if (result.cancel) return { cancel: true };
 153 		return undefined;
 154 	});
 155
 156 	pi.on("session_shutdown", async (_event, ctx) => {
-157 		await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Before exit: save durable memory?", timeoutMs: 12000 });
+157 		await runPrompt(ctx, "shutdown_fallback", { allowCancel: false, title: "Guardar insights en memoria?", timeoutMs: 12000 });
 158 	});
 159
 160 	pi.registerCommand("remember-session", {
 161 		description: "Queue a session analysis from the current session",
     ...


Listo. Cambié el título en `extensions/session-memory-dispatcher.ts` a:

`Guardar insights en memoria?`

Apliqué el cambio en ambos prompts del hook.