[object Object]

← back to Tk11438 Postgres Migration

Record approved Ken socket rollout and independent verification

7470da847854cd0de0696ba8da97bebe979b1b5f · 2026-09-11 10:46:09 -0700 · Steve Abrams

Files touched

Diff

commit 7470da847854cd0de0696ba8da97bebe979b1b5f
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Sep 11 10:46:09 2026 -0700

    Record approved Ken socket rollout and independent verification
---
 .gitignore                                         |   2 +
 NEXT-BATCH.md                                      |  17 +-
 README.md                                          |  34 +--
 next-batch.json                                    |  21 +-
 verification/e2e-proof.json                        |  52 +++-
 verification/ken-rollout/after-initial.json        | 129 +++++++++
 verification/ken-rollout/after.json                | 110 ++++++++
 verification/ken-rollout/applied.json              |   8 +
 verification/ken-rollout/baseline.json             | 119 +++++++++
 verification/ken-rollout/canary-log-baseline.json  |   5 +
 verification/ken-rollout/config-applied.json       |  10 +
 verification/ken-rollout/independent-check.cjs     |  56 ++++
 .../ken-rollout/independent-verification.json      | 287 +++++++++++++++++++++
 verification/ken-rollout/monitor.json              | 110 ++++++++
 verification/ken-rollout/mutation-started.json     |   6 +
 verification/ken-rollout/observation.json          |  23 ++
 verification/ken-rollout/parent-acceptance.json    |  17 ++
 verification/ken-rollout/preparation.json          |   9 +
 verification/ken-rollout/reload-started.json       |   6 +
 verification/ken-rollout/reloaded.json             |   7 +
 verification/ken-rollout/rollback-fault-proof.json |  22 ++
 verification/ken-rollout/rollback-test.cjs         |  21 ++
 verification/ken-rollout/rollout.cjs               | 112 ++++++++
 verification/ken-rollout/verification.json         |  39 +++
 verification/ken-rollout/verify-failure.json       |   6 +
 verification/ken-rollout/verify-rpc-timeout.json   |   6 +
 26 files changed, 1205 insertions(+), 29 deletions(-)

diff --git a/.gitignore b/.gitignore
index 87910ab..8ea1807 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,5 @@ dist/
 build/
 .next/
 __pycache__/
+
+/verification/ken-rollout/private/
diff --git a/NEXT-BATCH.md b/NEXT-BATCH.md
index 52c1281..b8c81e9 100644
--- a/NEXT-BATCH.md
+++ b/NEXT-BATCH.md
@@ -1,6 +1,10 @@
-# TK-11438 — Ken socket migration approval
+# TK-11438 — applied Ken batch and retained scope
 
-Status: PREPARED, NOT APPLIED. Overall ticket remains blocked. Owner: codex-run-11438.
+Status: APPLIED AND INDEPENDENTLY VERIFIED, 2026-09-11. Overall fleet remains incomplete.
+Owner: codex-run-11438. Source commit d840eb6; independent26/26PASS.
+Approval received via TK-11478, excluding all email sends. See verification/ken-rollout/parent-acceptance.json.
+
+The following is the retained approved scope.
 
 Approve this Ken batch, including its service restart and reconciliation job
 reload. Restarting Ken resumes its existing autonomous loops and therefore needs
@@ -76,3 +80,12 @@ or mark TK-11438 done.
 Gate source: Steve's current ticket instruction requires a pending-approval
 draft and STOP for production/customer-facing/spend/send/canonical changes.
 E2E Proof requires approved R4 rollout and independent verification.
+
+## Result and limitations
+
+Ken PID25903 is using sockets for both databases. Saved fields match runtime.
+Reconciliation is loaded with socket URL and1800-second interval, and exited0.
+The underlying signal module passed using the changed wrapper URL; the wrapper
+itself was not invoked because it can send email. Captured trading settings
+match; full risk_state hash is volatile and uncaptured keys were not independently
+proven unchanged. Private rollback materials are retained and gitignored.
diff --git a/README.md b/README.md
index 66c653c..3f728e0 100644
--- a/README.md
+++ b/README.md
@@ -1,21 +1,25 @@
 # TK-11438 PostgreSQL socket migration
 
-Status: PARTIAL / BLOCKED at the next production approval gate.
+Status: PARTIAL. Two approved batches applied; full fleet remains incomplete.
 
-The first approved batch (interiordesignershowroom and patterndesignlab) is applied.
-Fresh independent verification passes all 15 assertions, including saved PM2
-configuration, database identity, auth and missing-socket behavior. Counts remain
-3,201 products and 1,694 designs. No reboot or rollback rehearsal is claimed.
+First batch: showroom and Pattern Design Lab source, runtime and durable configs
+migrated and independently reverified (15 checks; 3,201 products/1,694 designs).
 
-Read NEXT-BATCH.md for the exact Ken approval scope. next-batch.json contains
-hashed source and environment change specifications; these have not been applied.
-Ken still uses TCP. Full original/expanded inventory classification is incomplete.
-The historical first-batch approval specification is retained in APPROVAL.md.
+Ken batch: source commit d840eb6; both databases now use Unix sockets. Effective
+and saved PM2 URLs match the shell-sourced .env. Hourly wrapper URI is migrated;
+its underlying SELECT-only module was tested without invoking its email branch.
+The reconciliation launchd job is loaded with the socket URL and exited0.
+Independent26/26 checks plus parent stability observation passed.
 
-reverify.cjs uses direct read-only PM2 RPC and never starts a daemon. It verifies
-the previously migrated services. ken-proof.cjs loads only the installed pg driver
-and runs SELECT-only socket and missing-socket tests. Neither imports app startup.
-Verification scripts save credential-free results in verification/.
+Read verification/ken-rollout/parent-acceptance.json for acceptance and limits.
+Private rollback materials are in verification/ken-rollout/private, mode0700,
+files0600, excluded from git. Never stage them. Helper rollback is guarded and
+must only be invoked to address an actual regression; no live rollback was needed.
 
-No live source/config edits, restarts, scheduled job runs, database writes, or
-remote changes were performed during this resumption.
+Captured trading switches and Ken config hashes match. Whole risk_state hash
+changed consistently with automatic weather-cache updates; uncaptured keys cannot
+be proven unchanged retrospectively. No email sends were invoked by this work.
+
+Original217 files plus an invalid directory entry and expanded candidates still
+need full disposition/migration. Gracie, other services and TCP shutdown remain
+outside the Ken approval. NEXT-BATCH.md retains its historical approved scope.
diff --git a/next-batch.json b/next-batch.json
index 276932e..06d7b66 100644
--- a/next-batch.json
+++ b/next-batch.json
@@ -1,6 +1,6 @@
 {
   "ticket": "TK-11438",
-  "status": "PREPARED_NOT_APPLIED",
+  "status": "APPLIED_VERIFIED_WITH_DOCUMENTED_LIMITS",
   "scope": "Ken application and its two known explicit-TCP scheduled consumers only",
   "file_changes": [
     {
@@ -48,5 +48,22 @@
     "Do not invoke follow-the-winners wrapper during verification: can send email on signal transition",
     "TCP shutdown excluded",
     "No Gracie restart: mounted vendor module calls canonical CREATE TABLE IF NOT EXISTS on startup"
-  ]
+  ],
+  "result": {
+    "ticket": "TK-11438",
+    "timestamp": "2026-09-11T17:46:09.886710+00:00",
+    "verdict": "KEN_BATCH_PASS_FULL_FLEET_INCOMPLETE",
+    "source_commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+    "approval": "Steve -> codex-run-11340/TK-11478 -> current session, 2026-09-11; described current Ken batch only; all email sends excluded",
+    "parent_acceptance": "Independent 26 PASS assertions inspected against parent baseline, after, stability proofs and exact d840eb6 diff. Accepted.",
+    "email_sends_invoked": 0,
+    "wrapper_invoked": false,
+    "private_backup": "verification/ken-rollout/private/ (0700; files0600; gitignored)",
+    "rollback": "node verification/ken-rollout/rollout.cjs rollback (only on actual regression, guarded file fields and job restore; live rollback not executed)",
+    "limits": [
+      "Only captured operational switches and Ken config hashes proved unchanged; full risk_state hash changed consistently with automatic weather_cache, exclusive cause unproven.",
+      "Email-capable hourly wrapper not executed. Exact wrapper URL used with underlying SELECT-only module; normal schedule definition preserved.",
+      "No reboot or full-fleet TCP shutdown. Original/expanded inventory remains incomplete."
+    ]
+  }
 }
diff --git a/verification/e2e-proof.json b/verification/e2e-proof.json
index b1dce78..adc0780 100644
--- a/verification/e2e-proof.json
+++ b/verification/e2e-proof.json
@@ -1,9 +1,9 @@
 {
   "intent": "Migrate every local Postgres TCP consumer to Unix sockets",
   "ticket": "TK-11438",
-  "timestamp": "2026-09-11T16:53:55.686878+00:00",
-  "risk_tier": "R3 read-only revalidation; R4 next rollout not authorized",
-  "build_identity": "Resolve documentation and proof scripts with git log -1; service commits a482499/bf33afd unchanged by this turn",
+  "timestamp": "2026-09-11T17:46:09.886710+00:00",
+  "risk_tier": "R4 authorized Ken rollout; scoped independent verification complete",
+  "build_identity": "Ken source d840eb6; evidence repository commit containing this record",
   "environment": "macstudio3; PostgreSQL 14.23; /tmp socket; existing PM2 daemon",
   "verdict": "PARTIAL_BLOCKED",
   "assertions": [
@@ -30,24 +30,39 @@
       "reason": "Still incomplete. Active Ken TCP seen; remaining inventory is candidates, not migrated consumers."
     },
     {
-      "check": "Ken app and scheduled job cutover",
+      "check": "Approved Ken application and scheduler socket migration",
+      "verdict": "PASS",
+      "evidence": "verification/ken-rollout/parent-acceptance.json",
+      "detail": "Two pools, effective/saved env, real auth boundary, missing sockets, underlying signal module and loaded reconciliation exit0 verified. Independent26/26PASS."
+    },
+    {
+      "check": "Rollback copy rehearsal and launchd failure recovery",
+      "verdict": "PASS",
+      "evidence": "verification/ken-rollout/rollback-fault-proof.json",
+      "detail": "Exact file/four-field dump roundtrips and4failure scenarios passed without induced live faults."
+    },
+    {
+      "check": "All risk_state keys unchanged",
       "verdict": "SKIP",
-      "reason": "Explicit production/spend/send gates; approval draft prepared."
+      "reason": "Full config hash includes volatile weather_cache; uncaptured keys cannot be proven retroactively. Captured trading switches and exact live_run/trade_config hashes match."
     },
     {
-      "check": "Restart rollback rehearsal and reboot persistence test",
+      "check": "Full hourly wrapper email branch",
       "verdict": "SKIP",
-      "reason": "No restart/reboot/rollback performed this turn; saved field-level configuration inspected only."
+      "reason": "All emails excluded by current approval; wrapper was not invoked. Transport verified through exact wrapper URI and underlying SELECT module."
     }
   ],
   "commands": [
     "node reverify.cjs (direct getMonitorData RPC, installed pg SELECTs, authenticated GETs, lsof)",
     "node ken-proof.cjs",
     "Read Ken start.sh, two Pool sites, .env key metadata and three launchd plists",
-    "psql read-only pg_stat_activity/settings SELECT"
+    "psql read-only pg_stat_activity/settings SELECT",
+    "node rollout.cjs prepare/apply/verify/reload/observe",
+    "node rollback-test.cjs",
+    "node independent-check.cjs"
   ],
-  "cleanup": "All test DB connections closed. Only evidence/verification scripts and ticket metadata changed this turn; prior first-batch source/runtime mutations remain recorded in first_batch.",
-  "rollback": "Prior first-batch scoped rollback helper is in peer evidence; not executed or claimed rehearsed. Next batch must privately capture exact fields and rehearse rollback before restart.",
+  "cleanup": "Test connections closed; one authorized Ken restart and one reconciliation reload completed. No email-capable wrapper executed. Private scoped rollback materials retained with0700/0600 permissions and excluded from git.",
+  "rollback": "node verification/ken-rollout/rollout.cjs rollback (only on actual regression, guarded file fields and job restore; live rollback not executed)",
   "first_batch": {
     "showroom_commit": "a482499",
     "patterndesignlab_commit": "bf33afd",
@@ -70,5 +85,22 @@
     "peer": "codex-run-11401",
     "change": "Two saved showroom DATABASE_URL fields only, no restart",
     "acceptance": "Fresh independent verification/reverification.json passes"
+  },
+  "ken_batch": {
+    "ticket": "TK-11438",
+    "timestamp": "2026-09-11T17:46:09.886710+00:00",
+    "verdict": "KEN_BATCH_PASS_FULL_FLEET_INCOMPLETE",
+    "source_commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+    "approval": "Steve -> codex-run-11340/TK-11478 -> current session, 2026-09-11; described current Ken batch only; all email sends excluded",
+    "parent_acceptance": "Independent 26 PASS assertions inspected against parent baseline, after, stability proofs and exact d840eb6 diff. Accepted.",
+    "email_sends_invoked": 0,
+    "wrapper_invoked": false,
+    "private_backup": "verification/ken-rollout/private/ (0700; files0600; gitignored)",
+    "rollback": "node verification/ken-rollout/rollout.cjs rollback (only on actual regression, guarded file fields and job restore; live rollback not executed)",
+    "limits": [
+      "Only captured operational switches and Ken config hashes proved unchanged; full risk_state hash changed consistently with automatic weather_cache, exclusive cause unproven.",
+      "Email-capable hourly wrapper not executed. Exact wrapper URL used with underlying SELECT-only module; normal schedule definition preserved.",
+      "No reboot or full-fleet TCP shutdown. Original/expanded inventory remains incomplete."
+    ]
   }
 }
diff --git a/verification/ken-rollout/after-initial.json b/verification/ken-rollout/after-initial.json
new file mode 100644
index 0000000..07cf1ba
--- /dev/null
+++ b/verification/ken-rollout/after-initial.json
@@ -0,0 +1,129 @@
+{
+  "at": "2026-09-11T17:32:47.884Z",
+  "label": "after",
+  "pid": 25903,
+  "pm_id": 15,
+  "restart_time": 39,
+  "identities": [
+    {
+      "key": "DATABASE_URL",
+      "database": "bertha_betting",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    },
+    {
+      "key": "KEN_DATABASE_URL",
+      "database": "ken",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    }
+  ],
+  "settings": {
+    "safe_mode": "false",
+    "trading_on": "true",
+    "kalshi_env": "prod",
+    "config_hash": "402d274e93015b9d3c875fa9aa76b60a"
+  },
+  "kenSettings": [
+    {
+      "key": "live_run",
+      "value_hash": "5007ffdbce2484c46f0f1ebfed156a79"
+    },
+    {
+      "key": "trade_config",
+      "value_hash": "c77ced4a430c742f316497e371b18efc"
+    }
+  ],
+  "reconciliation": {
+    "returned": true,
+    "invested": true,
+    "balance": true
+  },
+  "http": [
+    {
+      "route": "/api/setup",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/setup",
+      "auth": true,
+      "status": 200,
+      "sha256": "b7618c456aa65071f5e72b8d56effe52b8edccbb2173a34abfac7ea514d3f2f0",
+      "bytes": 75
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": true,
+      "status": 200,
+      "sha256": "5e461574d4ae8b1ce2c5495529884fdecb0dbe49b483473258640165a9212d7c",
+      "bytes": 1197
+    }
+  ],
+  "process_db_tcp": [],
+  "saved_match": true,
+  "targets": {
+    "DATABASE_URL": {
+      "host": "localhost",
+      "database": "bertha_betting",
+      "socket": "/tmp",
+      "port": "5432"
+    },
+    "KEN_DATABASE_URL": {
+      "host": "localhost",
+      "database": "ken",
+      "socket": "/tmp",
+      "port": "5432"
+    }
+  },
+  "activity": [
+    {
+      "datname": "bertha_betting",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 25947
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 25948
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 25952
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": null,
+      "pid": 30591
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": null,
+      "pid": 30592
+    }
+  ],
+  "source_hash": "8c20f30eb1a31fe7ad5c3884094dab8fcb0020ea7cf87d1b7c0a974693e9dc74",
+  "wrapper_hash": "61fb025a8011dccedc26f71e1017317074070b96a7b7f299d9f0556606f350a7"
+}
diff --git a/verification/ken-rollout/after.json b/verification/ken-rollout/after.json
new file mode 100644
index 0000000..229001e
--- /dev/null
+++ b/verification/ken-rollout/after.json
@@ -0,0 +1,110 @@
+{
+  "at": "2026-09-11T17:38:04.144Z",
+  "label": "after",
+  "pid": 25903,
+  "pm_id": 15,
+  "restart_time": 39,
+  "identities": [
+    {
+      "key": "DATABASE_URL",
+      "database": "bertha_betting",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    },
+    {
+      "key": "KEN_DATABASE_URL",
+      "database": "ken",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    }
+  ],
+  "settings": {
+    "safe_mode": "false",
+    "trading_on": "true",
+    "kalshi_env": "prod",
+    "config_hash": "402d274e93015b9d3c875fa9aa76b60a",
+    "stable_settings_hash": "c265a97bdda701bcaebe80968fee995d",
+    "weather_cache_fetched_at": "2026-09-11T17:32:45.483Z"
+  },
+  "kenSettings": [
+    {
+      "key": "live_run",
+      "value_hash": "5007ffdbce2484c46f0f1ebfed156a79"
+    },
+    {
+      "key": "trade_config",
+      "value_hash": "c77ced4a430c742f316497e371b18efc"
+    }
+  ],
+  "reconciliation": {
+    "returned": true,
+    "invested": true,
+    "balance": true
+  },
+  "http": [
+    {
+      "route": "/api/setup",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/setup",
+      "auth": true,
+      "status": 200,
+      "sha256": "b7618c456aa65071f5e72b8d56effe52b8edccbb2173a34abfac7ea514d3f2f0",
+      "bytes": 75
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": true,
+      "status": 200,
+      "sha256": "5e461574d4ae8b1ce2c5495529884fdecb0dbe49b483473258640165a9212d7c",
+      "bytes": 1197
+    }
+  ],
+  "process_db_tcp": [],
+  "saved_match": true,
+  "targets": {
+    "DATABASE_URL": {
+      "host": "localhost",
+      "database": "bertha_betting",
+      "socket": "/tmp",
+      "port": "5432"
+    },
+    "KEN_DATABASE_URL": {
+      "host": "localhost",
+      "database": "ken",
+      "socket": "/tmp",
+      "port": "5432"
+    }
+  },
+  "activity": [
+    {
+      "datname": "bertha_betting",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 80756
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 80757
+    }
+  ],
+  "source_hash": "8c20f30eb1a31fe7ad5c3884094dab8fcb0020ea7cf87d1b7c0a974693e9dc74",
+  "wrapper_hash": "61fb025a8011dccedc26f71e1017317074070b96a7b7f299d9f0556606f350a7"
+}
diff --git a/verification/ken-rollout/applied.json b/verification/ken-rollout/applied.json
new file mode 100644
index 0000000..fdff485
--- /dev/null
+++ b/verification/ken-rollout/applied.json
@@ -0,0 +1,8 @@
+{
+  "at": "2026-09-11T17:32:32.364Z",
+  "verdict": "APPLIED_VERIFY_PENDING",
+  "before_pid": 92641,
+  "after_pid": 25903,
+  "commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+  "email_wrapper_executed": false
+}
diff --git a/verification/ken-rollout/baseline.json b/verification/ken-rollout/baseline.json
new file mode 100644
index 0000000..f007fbb
--- /dev/null
+++ b/verification/ken-rollout/baseline.json
@@ -0,0 +1,119 @@
+{
+  "at": "2026-09-11T17:28:40.054Z",
+  "label": "baseline",
+  "pid": 92641,
+  "pm_id": 15,
+  "restart_time": 38,
+  "identities": [
+    {
+      "key": "DATABASE_URL",
+      "database": "bertha_betting",
+      "role": "macstudio3",
+      "addr": "::1/128",
+      "readonly": "on"
+    },
+    {
+      "key": "KEN_DATABASE_URL",
+      "database": "ken",
+      "role": "macstudio3",
+      "addr": "::1/128",
+      "readonly": "on"
+    }
+  ],
+  "settings": {
+    "safe_mode": "false",
+    "trading_on": "true",
+    "kalshi_env": "prod",
+    "config_hash": "2fddc5b0ff823bbe1ea724dca0dca0d0"
+  },
+  "kenSettings": [
+    {
+      "key": "live_run",
+      "value_hash": "5007ffdbce2484c46f0f1ebfed156a79"
+    },
+    {
+      "key": "trade_config",
+      "value_hash": "c77ced4a430c742f316497e371b18efc"
+    }
+  ],
+  "reconciliation": {
+    "returned": true,
+    "invested": true,
+    "balance": true
+  },
+  "http": [
+    {
+      "route": "/api/setup",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/setup",
+      "auth": true,
+      "status": 200,
+      "sha256": "b7618c456aa65071f5e72b8d56effe52b8edccbb2173a34abfac7ea514d3f2f0",
+      "bytes": 75
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": true,
+      "status": 200,
+      "sha256": "5e461574d4ae8b1ce2c5495529884fdecb0dbe49b483473258640165a9212d7c",
+      "bytes": 1197
+    }
+  ],
+  "process_db_tcp": [
+    "node    92641 macstudio3   17u  IPv6 0x593636ef484b2ded      0t0  TCP [::1]:63505->[::1]:5432 (ESTABLISHED)",
+    "node    92641 macstudio3   37u  IPv6 0x88be5de58ac91ca4      0t0  TCP [::1]:49930->[::1]:5432 (ESTABLISHED)",
+    "node    92641 macstudio3   39u  IPv6 0x233e031ec3ae6c64      0t0  TCP [::1]:50147->[::1]:5432 (ESTABLISHED)"
+  ],
+  "saved_match": true,
+  "targets": {
+    "DATABASE_URL": {
+      "host": "localhost",
+      "database": "bertha_betting",
+      "socket": null,
+      "port": "5432"
+    },
+    "KEN_DATABASE_URL": {
+      "host": "localhost",
+      "database": "ken",
+      "socket": null,
+      "port": "5432"
+    }
+  },
+  "activity": [
+    {
+      "datname": "bertha_betting",
+      "usename": "macstudio3",
+      "client_addr": "::1/128",
+      "client_port": 49930,
+      "pid": 86148
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": "::1/128",
+      "client_port": 63505,
+      "pid": 74147
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": "::1/128",
+      "client_port": 50147,
+      "pid": 86293
+    }
+  ],
+  "source_hash": "8c20f30eb1a31fe7ad5c3884094dab8fcb0020ea7cf87d1b7c0a974693e9dc74",
+  "wrapper_hash": "04a512566936f561c7c37d623e26ec8bb0e2ec507607dbd5db5c8f5e19d5b5b9"
+}
diff --git a/verification/ken-rollout/canary-log-baseline.json b/verification/ken-rollout/canary-log-baseline.json
new file mode 100644
index 0000000..856dfb9
--- /dev/null
+++ b/verification/ken-rollout/canary-log-baseline.json
@@ -0,0 +1,5 @@
+{
+  "at": "2026-09-11T17:37:28.616082+00:00",
+  "path": "/Users/macstudio3/Projects/Ken/kalshi-dash/logs/reconcile-canary.log",
+  "size": 202789
+}
diff --git a/verification/ken-rollout/config-applied.json b/verification/ken-rollout/config-applied.json
new file mode 100644
index 0000000..13906ec
--- /dev/null
+++ b/verification/ken-rollout/config-applied.json
@@ -0,0 +1,10 @@
+{
+  "at": "2026-09-11T17:32:30.028Z",
+  "dump": {
+    "before_sha": "92da72322f2e0a36503a1a2ba1ad5c016d3aed44793c8f84571e495f2792ec16",
+    "after_sha": "d733e1d077687f60cb8b79bebb3656dbde21866a0f5b3b37680c55b80af1ece7",
+    "only_target_fields": true
+  },
+  "commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+  "application_restart_pending": true
+}
diff --git a/verification/ken-rollout/independent-check.cjs b/verification/ken-rollout/independent-check.cjs
new file mode 100644
index 0000000..1909dde
--- /dev/null
+++ b/verification/ken-rollout/independent-check.cjs
@@ -0,0 +1,56 @@
+// Independent read-only verifier. No service starts/restarts, wrappers, or sends.
+const fs=require('fs'),cp=require('child_process'),assert=require('assert/strict'),crypto=require('crypto'),os=require('os');
+const base=__dirname,dir='/Users/macstudio3/Projects/Ken/kalshi-dash';
+const parse=require('/Users/macstudio3/Projects/patterndesignlab/node_modules/dotenv').parse;
+const {Client}=require(dir+'/node_modules/pg');
+const deps='/Users/macstudio3/.npm-global/lib/node_modules/pm2/node_modules/';
+const axon=require(deps+'pm2-axon'),rpc=require(deps+'pm2-axon-rpc');
+const read=p=>fs.readFileSync(p),json=p=>JSON.parse(read(p)),hash=b=>crypto.createHash('sha256').update(b).digest('hex');
+const run=(cmd,args)=>cp.execFileSync(cmd,args,{encoding:'utf8',timeout:15000,maxBuffer:8e6});
+const proof={task_id:'TK-11438',owner:'codex-run-11438-verifier',risk:'R4',timestamp:new Date().toISOString(),assertions:[],commands:[],application_mutations:0,email_sends:0};
+function pass(name,detail){proof.assertions.push({name,verdict:'PASS',detail});}
+async function db(url,fn){const c=new Client({connectionString:url,application_name:'TK11438-independent-verifier',connectionTimeoutMillis:3000,options:'-c default_transaction_read_only=on -c statement_timeout=5000'});try{await c.connect();return await fn(c);}finally{await c.end().catch(()=>{});}}
+async function monitor(){return await new Promise((resolve,reject)=>{const s=axon.socket('req'),c=new rpc.Client(s);const t=setTimeout(()=>{s.close();reject(Error('PM2 RPC timeout'));},30000);s.on('error',e=>{clearTimeout(t);s.close();reject(e);});s.connect('/Users/macstudio3/.pm2/rpc.sock');c.call('getMonitorData',{},(err,res)=>{clearTimeout(t);s.close();err?reject(Error('PM2 RPC failed')):resolve(res);});});}
+(async()=>{
+ const baseline=json(base+'/baseline.json'),receipt=json(base+'/private/receipt.json');
+ const env=parse(read(dir+'/.env')),services=await monitor(),matches=services.filter(s=>s.name==='ken');assert.equal(matches.length,1);
+ const live=matches[0],e=live.pm2_env;assert.equal(e.pm_cwd,dir);assert.equal(e.pm_exec_path,dir+'/start.sh');assert.equal(e.status,'online');assert.notEqual(live.pid,baseline.pid);
+ proof.pid=live.pid;proof.pm_id=e.pm_id;proof.commands.push('direct existing PM2 getMonitorData RPC');pass('Ken unique identity and online restart',{before_pid:baseline.pid,after_pid:live.pid,restart_time:e.restart_time});
+ const saved=json('/Users/macstudio3/.pm2/dump.pm2').filter(s=>s.name==='ken');assert.equal(saved.length,1);assert.equal(saved[0].pm_cwd,dir);assert.equal(saved[0].pm_exec_path,dir+'/start.sh');
+ for(const key of ['DATABASE_URL','KEN_DATABASE_URL']){
+  for(const v of [e[key],e.env[key],saved[0][key],saved[0].env[key]])assert.equal(v,env[key]);
+  const expected=baseline.identities.find(x=>x.key===key);
+  for(const [source,url]of [['dotenv',env[key]],['saved dump',saved[0][key]]]){
+   assert.equal(new URL(url).searchParams.get('host'),'/tmp');
+   const id=await db(url,async c=>(await c.query("SELECT current_database() database,current_user role,inet_client_addr()::text addr,current_setting('transaction_read_only') readonly")).rows[0]);
+   assert.equal(id.database,expected.database);assert.equal(id.role,expected.role);assert.equal(id.addr,null);assert.equal(id.readonly,'on');pass(key+' '+source+' Unix socket identity',id);
+  }
+ }
+ pass('Four effective/saved URL fields agree with dotenv',true);
+ const settings=await db(env.DATABASE_URL,async c=>(await c.query("SELECT config->>'safe_mode' safe_mode,config->>'trading_on' trading_on,config->>'kalshi_env' kalshi_env,md5(config::text) config_hash FROM risk_state ORDER BY updated_at DESC LIMIT 1")).rows[0]);
+ const ksettings=await db(env.KEN_DATABASE_URL,async c=>(await c.query("SELECT key,md5(value::text) value_hash FROM ken_config WHERE key IN ('live_run','trade_config') ORDER BY key")).rows);
+ for(const key of ['safe_mode','trading_on','kalshi_env'])assert.equal(settings[key],baseline.settings[key]);assert.deepEqual(ksettings,baseline.kenSettings);pass('Captured operational switches and Ken trading configuration unchanged',{settings,ksettings});proof.warnings=[{name:'Whole risk_state.config comparison',reason:'Baseline whole-config hash differs. Unchanged server.js lines 8027-8031 refresh weather_cache on scan. This is consistent with expected cache churn, but baseline omitted individual other keys, so exclusive cause and all-key preservation cannot be proved retroactively.',baseline_hash:baseline.settings.config_hash,current_hash:settings.config_hash}];
+ const lan=Object.values(os.networkInterfaces()).flat().find(x=>x.family==='IPv4'&&!x.internal&&x.address.startsWith('192.168.'));assert(lan);
+ for(const route of ['/api/setup','/api/trading/history'])for(const auth of ['none','invalid','valid']){
+  const headers=auth==='none'?{}:{authorization:'Basic '+Buffer.from(auth==='valid'?(env.ADMIN_USER||'admin')+':'+env.ADMIN_PASSWORD:'TK11438-verifier:invalid').toString('base64')};
+  const response=await fetch('http://'+lan.address+':7810'+route,{headers,signal:AbortSignal.timeout(10000)}),body=await response.text();
+  assert.equal(response.status,auth==='valid'?200:401);if(auth==='valid'){const obj=JSON.parse(body);assert(!obj.error);}
+  pass('LAN '+route+' '+auth+' auth',{status:response.status,bytes:body.length,body_sha256:hash(body)});
+ }
+ let sockets='';try{sockets=run('lsof',['-nP','-a','-p',String(live.pid),'-iTCP']);}catch(err){assert.equal(err.status,1);sockets=err.stdout||'';}
+ assert(!/:5432\b/.test(sockets));pass('Ken PID has no TCP 5432 socket',true);
+ const unix=run('lsof',['-nP','-a','-p',String(live.pid),'-U']);assert(unix.includes('unix'));pass('Ken PID has active Unix sockets',{lines:unix.trim().split('\n').length-1,sha256:hash(unix)});
+ const activity=await db('postgresql:///postgres?host=/tmp',async c=>(await c.query("SELECT datname,usename,client_addr::text,client_port,pid FROM pg_stat_activity WHERE datname IN ('ken','bertha_betting') AND application_name NOT LIKE 'TK11438%' ORDER BY datname,pid")).rows);
+ for(const name of ['ken','bertha_betting'])assert(activity.some(x=>x.datname===name&&x.client_addr===null));pass('Application database sessions use Unix sockets',activity);
+ for(const key of ['DATABASE_URL','KEN_DATABASE_URL']){const bad=new URL(env[key]);bad.searchParams.set('host',base+'/missing-socket');let code;try{await db(bad.toString(),async()=>{});}catch(e){code=e.code;}assert.equal(code,'ENOENT');pass(key+' missing socket fails closed',{code});}
+ assert.equal(hash(read(dir+'/server.js')),baseline.source_hash);assert.equal(hash(read(dir+'/server.js')),receipt.source_hash);pass('Server source unchanged',baseline.source_hash);
+ const manifest=json('/Users/macstudio3/Projects/tk11438-postgres-migration/next-batch.json');
+ assert.equal(read(dir+'/follow-the-winners-check.sh').toString(),read(base+'/private/wrapper.before').toString().replace(manifest.file_changes[0].old,manifest.file_changes[0].new));pass('Hourly wrapper exact approved one-line change; not executed',true);
+ for(const name of ['env','wrapper','plist']){assert.equal(hash(read(base+'/private/rehearsal-'+name)),receipt.hashes[name]);pass(name+' rollback rehearsal restored exact prior bytes',true);}
+ assert.equal(hash(read(dir+'/.env')),hash(read(base+'/private/env.after')));pass('Durable dotenv matches prepared bytes',true);
+ const plistPath='/Users/macstudio3/Library/LaunchAgents/com.steve.ken-reconcile-canary.plist',plist=JSON.parse(run('plutil',['-convert','json','-o','-',plistPath]));
+ assert.equal(hash(read(plistPath)),hash(read(base+'/private/plist.after')));assert.equal(new URL(plist.EnvironmentVariables.KEN_DATABASE_URL).searchParams.get('host'),'/tmp');assert.equal(plist.StartInterval,1800);assert.deepEqual(plist.ProgramArguments,['/opt/homebrew/bin/node',dir+'/scripts/reconcile-canary.mjs']);
+ const launch=run('launchctl',['print','gui/'+process.getuid()+'/com.steve.ken-reconcile-canary']);assert.equal(new URL(launch.match(/KEN_DATABASE_URL => (\S+)/)[1]).searchParams.get('host'),'/tmp');assert(/last exit code = 0/.test(launch));pass('Reconciliation durable and loaded socket; interval preserved; exit zero',{interval:plist.StartInterval,last_exit:0});
+ const log=read(plist.StandardOutPath).toString().trim().split('\n');assert(log.at(-1).includes('[reconcile-canary] OK'));assert(fs.statSync(plist.StandardOutPath).mtimeMs>=Date.parse(json(base+'/reloaded.json').at)-10000);pass('Reconciliation post-reload result',{last_line:log.at(-1),mtime:fs.statSync(plist.StandardOutPath).mtime.toISOString()});
+ proof.commit=run('git',['-C','/Users/macstudio3/Projects/Ken','rev-parse','HEAD']).trim();proof.verdict='PASS';proof.status='complete';proof.skipped=[{name:'Hourly email-capable wrapper execution',verdict:'SKIP',reason:'Explicitly excluded from authorization; parent separately verifies SELECT-only signal module.'},{name:'Full-fleet TCP shutdown',verdict:'SKIP',reason:'Outside Ken batch.'},{name:'Live rollback execution',verdict:'SKIP',reason:'Healthy rollout; rollback reviewed and copies rehearsed, no induced production fault.'}];
+})().catch(err=>{proof.verdict='FAIL';proof.status='partial';proof.error={name:err.name,code:err.code,message:String(err.message).replace(/postgres(?:ql)?:\/\/\S+/g,'[REDACTED_URL]')};}).finally(()=>{fs.writeFileSync(base+'/independent-verification.json',JSON.stringify(proof,null,2)+'\n');console.log(JSON.stringify(proof,null,2));if(proof.verdict!=='PASS')process.exitCode=1;});
diff --git a/verification/ken-rollout/independent-verification.json b/verification/ken-rollout/independent-verification.json
new file mode 100644
index 0000000..004815e
--- /dev/null
+++ b/verification/ken-rollout/independent-verification.json
@@ -0,0 +1,287 @@
+{
+  "task_id": "TK-11438",
+  "owner": "codex-run-11438-verifier",
+  "risk": "R4",
+  "timestamp": "2026-09-11T17:41:15.071Z",
+  "assertions": [
+    {
+      "name": "Ken unique identity and online restart",
+      "verdict": "PASS",
+      "detail": {
+        "before_pid": 92641,
+        "after_pid": 25903,
+        "restart_time": 39
+      }
+    },
+    {
+      "name": "DATABASE_URL dotenv Unix socket identity",
+      "verdict": "PASS",
+      "detail": {
+        "database": "bertha_betting",
+        "role": "macstudio3",
+        "addr": null,
+        "readonly": "on"
+      }
+    },
+    {
+      "name": "DATABASE_URL saved dump Unix socket identity",
+      "verdict": "PASS",
+      "detail": {
+        "database": "bertha_betting",
+        "role": "macstudio3",
+        "addr": null,
+        "readonly": "on"
+      }
+    },
+    {
+      "name": "KEN_DATABASE_URL dotenv Unix socket identity",
+      "verdict": "PASS",
+      "detail": {
+        "database": "ken",
+        "role": "macstudio3",
+        "addr": null,
+        "readonly": "on"
+      }
+    },
+    {
+      "name": "KEN_DATABASE_URL saved dump Unix socket identity",
+      "verdict": "PASS",
+      "detail": {
+        "database": "ken",
+        "role": "macstudio3",
+        "addr": null,
+        "readonly": "on"
+      }
+    },
+    {
+      "name": "Four effective/saved URL fields agree with dotenv",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "Captured operational switches and Ken trading configuration unchanged",
+      "verdict": "PASS",
+      "detail": {
+        "settings": {
+          "safe_mode": "false",
+          "trading_on": "true",
+          "kalshi_env": "prod",
+          "config_hash": "556a9eb6c8fed4832b515ffd6dba0de1"
+        },
+        "ksettings": [
+          {
+            "key": "live_run",
+            "value_hash": "5007ffdbce2484c46f0f1ebfed156a79"
+          },
+          {
+            "key": "trade_config",
+            "value_hash": "c77ced4a430c742f316497e371b18efc"
+          }
+        ]
+      }
+    },
+    {
+      "name": "LAN /api/setup none auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 401,
+        "body_sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+        "body_code_units": 35
+      }
+    },
+    {
+      "name": "LAN /api/setup invalid auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 401,
+        "body_sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+        "body_code_units": 35
+      }
+    },
+    {
+      "name": "LAN /api/setup valid auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 200,
+        "body_sha256": "b7618c456aa65071f5e72b8d56effe52b8edccbb2173a34abfac7ea514d3f2f0",
+        "body_code_units": 75
+      }
+    },
+    {
+      "name": "LAN /api/trading/history none auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 401,
+        "body_sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+        "body_code_units": 35
+      }
+    },
+    {
+      "name": "LAN /api/trading/history invalid auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 401,
+        "body_sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+        "body_code_units": 35
+      }
+    },
+    {
+      "name": "LAN /api/trading/history valid auth",
+      "verdict": "PASS",
+      "detail": {
+        "status": 200,
+        "body_sha256": "5e461574d4ae8b1ce2c5495529884fdecb0dbe49b483473258640165a9212d7c",
+        "body_code_units": 1195
+      }
+    },
+    {
+      "name": "Ken PID has no TCP 5432 socket",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "Ken PID has active Unix sockets",
+      "verdict": "PASS",
+      "detail": {
+        "lines": 7,
+        "sha256": "53c2cefe2834d7e98a83c8ae9d458de9b0ccb969d357530c0037bad80386ad2f"
+      }
+    },
+    {
+      "name": "Application database sessions use Unix sockets",
+      "verdict": "PASS",
+      "detail": [
+        {
+          "datname": "bertha_betting",
+          "usename": "macstudio3",
+          "client_addr": null,
+          "client_port": -1,
+          "pid": 20448
+        },
+        {
+          "datname": "ken",
+          "usename": "macstudio3",
+          "client_addr": null,
+          "client_port": -1,
+          "pid": 20457
+        }
+      ]
+    },
+    {
+      "name": "DATABASE_URL missing socket fails closed",
+      "verdict": "PASS",
+      "detail": {
+        "code": "ENOENT"
+      }
+    },
+    {
+      "name": "KEN_DATABASE_URL missing socket fails closed",
+      "verdict": "PASS",
+      "detail": {
+        "code": "ENOENT"
+      }
+    },
+    {
+      "name": "Server source unchanged",
+      "verdict": "PASS",
+      "detail": "8c20f30eb1a31fe7ad5c3884094dab8fcb0020ea7cf87d1b7c0a974693e9dc74"
+    },
+    {
+      "name": "Hourly wrapper exact approved one-line change; not executed",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "env rollback rehearsal restored exact prior bytes",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "wrapper rollback rehearsal restored exact prior bytes",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "plist rollback rehearsal restored exact prior bytes",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "Durable dotenv matches prepared bytes",
+      "verdict": "PASS",
+      "detail": true
+    },
+    {
+      "name": "Reconciliation durable and loaded socket; interval preserved; exit zero",
+      "verdict": "PASS",
+      "detail": {
+        "interval": 1800,
+        "last_exit": 0
+      }
+    },
+    {
+      "name": "Reconciliation post-reload result",
+      "verdict": "PASS",
+      "detail": {
+        "last_line": "[reconcile-canary] OK \u2014 returned=$472535.99 invested=$319060.81 balance=$163375.18 (all reconcile within $2)",
+        "mtime": "2026-09-11T17:38:14.880Z"
+      }
+    }
+  ],
+  "commands": [
+    "node --check /tmp/tk11438-ken-approved/independent-check.cjs",
+    "node /tmp/tk11438-ken-approved/independent-check.cjs (read-only; initial sandbox EPERM, authorized retry PASS)",
+    "direct existing PM2 getMonitorData RPC",
+    "node-postgres read-only identity/config/activity SELECTs using dotenv and saved PM2 URLs",
+    "LAN HTTP GET /api/setup and /api/trading/history with missing, invalid and valid Basic Auth",
+    "lsof -nP -a -p 25903 -iTCP and -U",
+    "launchctl print gui/501/com.steve.ken-reconcile-canary",
+    "plutil -convert json -o - reconciliation plist; source/backup hash reads; latest canary log read",
+    "git show --stat --oneline HEAD and git rev-parse HEAD"
+  ],
+  "application_mutations": 0,
+  "email_sends": 0,
+  "pid": 25903,
+  "pm_id": 15,
+  "warnings": [
+    {
+      "name": "Whole risk_state.config comparison",
+      "reason": "Baseline whole-config hash differs. Unchanged server.js lines 8027-8031 refresh weather_cache on scan. This is consistent with expected cache churn, but baseline omitted individual other keys, so exclusive cause and all-key preservation cannot be proved retroactively.",
+      "baseline_hash": "2fddc5b0ff823bbe1ea724dca0dca0d0",
+      "current_hash": "556a9eb6c8fed4832b515ffd6dba0de1"
+    }
+  ],
+  "commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+  "verdict": "PASS",
+  "status": "complete",
+  "skipped": [
+    {
+      "name": "Hourly email-capable wrapper execution",
+      "verdict": "SKIP",
+      "reason": "Explicitly excluded from authorization; parent separately verifies SELECT-only signal module."
+    },
+    {
+      "name": "Full-fleet TCP shutdown",
+      "verdict": "SKIP",
+      "reason": "Outside Ken batch."
+    },
+    {
+      "name": "Live rollback execution",
+      "verdict": "SKIP",
+      "reason": "Healthy rollout; rollback reviewed and copies rehearsed, no induced production fault."
+    }
+  ],
+  "rollback_review": {
+    "original_must_fix": "Reload bootstrap failure left unloaded job outside rollback marker",
+    "resolution": "Parent now journals before bootout and restores loaded or absent prior target; reviewed actual helper and four mocked fault scenarios",
+    "mocked_scenarios": [
+      "loaded recovery",
+      "unloaded recovery",
+      "unexpected error fails closed",
+      "bootstrap failure retains journal"
+    ],
+    "mocked_artifact": "/tmp/tk11438-ken-approved/rollback-fault-proof.json",
+    "live_rollback_performed": false
+  },
+  "parent_acceptance_required": true
+}
diff --git a/verification/ken-rollout/monitor.json b/verification/ken-rollout/monitor.json
new file mode 100644
index 0000000..d56d454
--- /dev/null
+++ b/verification/ken-rollout/monitor.json
@@ -0,0 +1,110 @@
+{
+  "at": "2026-09-11T17:39:23.438Z",
+  "label": "monitor",
+  "pid": 25903,
+  "pm_id": 15,
+  "restart_time": 39,
+  "identities": [
+    {
+      "key": "DATABASE_URL",
+      "database": "bertha_betting",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    },
+    {
+      "key": "KEN_DATABASE_URL",
+      "database": "ken",
+      "role": "macstudio3",
+      "addr": null,
+      "readonly": "on"
+    }
+  ],
+  "settings": {
+    "safe_mode": "false",
+    "trading_on": "true",
+    "kalshi_env": "prod",
+    "config_hash": "402d274e93015b9d3c875fa9aa76b60a",
+    "stable_settings_hash": "c265a97bdda701bcaebe80968fee995d",
+    "weather_cache_fetched_at": "2026-09-11T17:32:45.483Z"
+  },
+  "kenSettings": [
+    {
+      "key": "live_run",
+      "value_hash": "5007ffdbce2484c46f0f1ebfed156a79"
+    },
+    {
+      "key": "trade_config",
+      "value_hash": "c77ced4a430c742f316497e371b18efc"
+    }
+  ],
+  "reconciliation": {
+    "returned": true,
+    "invested": true,
+    "balance": true
+  },
+  "http": [
+    {
+      "route": "/api/setup",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/setup",
+      "auth": true,
+      "status": 200,
+      "sha256": "b7618c456aa65071f5e72b8d56effe52b8edccbb2173a34abfac7ea514d3f2f0",
+      "bytes": 75
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": false,
+      "status": 401,
+      "sha256": "ca730ec7fc2b7edb7277e5b8e58d6a474971ca59dafb499141a4856c06132638",
+      "bytes": 35
+    },
+    {
+      "route": "/api/trading/history",
+      "auth": true,
+      "status": 200,
+      "sha256": "5e461574d4ae8b1ce2c5495529884fdecb0dbe49b483473258640165a9212d7c",
+      "bytes": 1197
+    }
+  ],
+  "process_db_tcp": [],
+  "saved_match": true,
+  "targets": {
+    "DATABASE_URL": {
+      "host": "localhost",
+      "database": "bertha_betting",
+      "socket": "/tmp",
+      "port": "5432"
+    },
+    "KEN_DATABASE_URL": {
+      "host": "localhost",
+      "database": "ken",
+      "socket": "/tmp",
+      "port": "5432"
+    }
+  },
+  "activity": [
+    {
+      "datname": "bertha_betting",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 98157
+    },
+    {
+      "datname": "ken",
+      "usename": "macstudio3",
+      "client_addr": null,
+      "client_port": -1,
+      "pid": 98158
+    }
+  ],
+  "source_hash": "8c20f30eb1a31fe7ad5c3884094dab8fcb0020ea7cf87d1b7c0a974693e9dc74",
+  "wrapper_hash": "61fb025a8011dccedc26f71e1017317074070b96a7b7f299d9f0556606f350a7"
+}
diff --git a/verification/ken-rollout/mutation-started.json b/verification/ken-rollout/mutation-started.json
new file mode 100644
index 0000000..b6e8ed4
--- /dev/null
+++ b/verification/ken-rollout/mutation-started.json
@@ -0,0 +1,6 @@
+{
+  "at": "2026-09-11T17:32:29.516Z",
+  "scope": "Ken only",
+  "before_pid": 92641,
+  "steps": []
+}
diff --git a/verification/ken-rollout/observation.json b/verification/ken-rollout/observation.json
new file mode 100644
index 0000000..e5c5bf1
--- /dev/null
+++ b/verification/ken-rollout/observation.json
@@ -0,0 +1,23 @@
+{
+  "at": "2026-09-11T17:39:23.460Z",
+  "verdict": "PASS",
+  "pid": 25903,
+  "restart_count_stable": true,
+  "two_pools_socket": true,
+  "auth_statuses": [
+    401,
+    200,
+    401,
+    200
+  ],
+  "reconciliation_job": {
+    "loaded_socket": true,
+    "last_exit": 0,
+    "interval": 1800,
+    "other_plist_fields_preserved": true,
+    "new_log_bytes": 109,
+    "log_sha256": "65773db99f0cd3902340b7a88a67697647ba0c08891bec2abff941a613016c10",
+    "result": "OK"
+  },
+  "email_wrapper_invoked": false
+}
diff --git a/verification/ken-rollout/parent-acceptance.json b/verification/ken-rollout/parent-acceptance.json
new file mode 100644
index 0000000..f66b08d
--- /dev/null
+++ b/verification/ken-rollout/parent-acceptance.json
@@ -0,0 +1,17 @@
+{
+  "ticket": "TK-11438",
+  "timestamp": "2026-09-11T17:46:09.886710+00:00",
+  "verdict": "KEN_BATCH_PASS_FULL_FLEET_INCOMPLETE",
+  "source_commit": "d840eb666dbe5986eb48925ef9e694c3c3d450d3",
+  "approval": "Steve -> codex-run-11340/TK-11478 -> current session, 2026-09-11; described current Ken batch only; all email sends excluded",
+  "parent_acceptance": "Independent 26 PASS assertions inspected against parent baseline, after, stability proofs and exact d840eb6 diff. Accepted.",
+  "email_sends_invoked": 0,
+  "wrapper_invoked": false,
+  "private_backup": "verification/ken-rollout/private/ (0700; files0600; gitignored)",
+  "rollback": "node verification/ken-rollout/rollout.cjs rollback (only on actual regression, guarded file fields and job restore; live rollback not executed)",
+  "limits": [
+    "Only captured operational switches and Ken config hashes proved unchanged; full risk_state hash changed consistently with automatic weather_cache, exclusive cause unproven.",
+    "Email-capable hourly wrapper not executed. Exact wrapper URL used with underlying SELECT-only module; normal schedule definition preserved.",
+    "No reboot or full-fleet TCP shutdown. Original/expanded inventory remains incomplete."
+  ]
+}
diff --git a/verification/ken-rollout/preparation.json b/verification/ken-rollout/preparation.json
new file mode 100644
index 0000000..9a68ba5
--- /dev/null
+++ b/verification/ken-rollout/preparation.json
@@ -0,0 +1,9 @@
+{
+  "at": "2026-09-11T17:28:40.056Z",
+  "verdict": "PASS",
+  "rollback_rehearsal": "exact file restores plus four-field dump roundtrip PASS",
+  "application_mutations": 0,
+  "email_sends": 0,
+  "pid": 92641,
+  "pm_id": 15
+}
diff --git a/verification/ken-rollout/reload-started.json b/verification/ken-rollout/reload-started.json
new file mode 100644
index 0000000..458d754
--- /dev/null
+++ b/verification/ken-rollout/reload-started.json
@@ -0,0 +1,6 @@
+{
+  "at": "2026-09-11T17:38:14.560Z",
+  "target": "gui/501/com.steve.ken-reconcile-canary",
+  "prior_loaded": true,
+  "phase": "bootout-complete"
+}
diff --git a/verification/ken-rollout/reloaded.json b/verification/ken-rollout/reloaded.json
new file mode 100644
index 0000000..a3031a8
--- /dev/null
+++ b/verification/ken-rollout/reloaded.json
@@ -0,0 +1,7 @@
+{
+  "at": "2026-09-11T17:38:14.577Z",
+  "verdict": "RELOADED_OBSERVE_PENDING",
+  "target": "gui/501/com.steve.ken-reconcile-canary",
+  "loaded_socket": true,
+  "loaded": true
+}
diff --git a/verification/ken-rollout/rollback-fault-proof.json b/verification/ken-rollout/rollback-fault-proof.json
new file mode 100644
index 0000000..443f790
--- /dev/null
+++ b/verification/ken-rollout/rollback-fault-proof.json
@@ -0,0 +1,22 @@
+{
+  "at": "2026-09-11T17:39:12.372Z",
+  "results": [
+    {
+      "scenario": "loaded",
+      "verdict": "PASS"
+    },
+    {
+      "scenario": "unloaded",
+      "verdict": "PASS"
+    },
+    {
+      "scenario": "unexpected-error",
+      "verdict": "PASS"
+    },
+    {
+      "scenario": "bootstrap-failure-retains-recovery-journal",
+      "verdict": "PASS"
+    }
+  ],
+  "live_commands": 0
+}
diff --git a/verification/ken-rollout/rollback-test.cjs b/verification/ken-rollout/rollback-test.cjs
new file mode 100644
index 0000000..aa7a2f0
--- /dev/null
+++ b/verification/ken-rollout/rollback-test.cjs
@@ -0,0 +1,21 @@
+// Fault-inject the actual launchd recovery branch without system tools or live IO.
+const fs=require('fs'),vm=require('vm'),assert=require('assert/strict');
+const source=fs.readFileSync(__dirname+'/rollout.cjs','utf8');
+const start=source.indexOf("  if(fs.existsSync(OUT+'/reload-started.json')){");
+const end=source.indexOf("  record('rollback'",start);assert(start>0&&end>start);
+const branch=source.slice(start,end),results=[];
+for(const state of ['loaded','unloaded','unexpected-error']){
+ const calls=[],records=[];const old='postgresql://localhost:5432/ken';
+ const context={OUT:'/fake',PRIV:'/fake/private',PLIST:'/fake/canary.plist',assert,JSON,process:{getuid:()=>501},fs:{existsSync:()=>true},cp:{spawnSync:()=>state==='loaded'?{status:0}:state==='unloaded'?{status:113,stderr:'Could not find service'}:{status:1,stderr:'Permission denied'}},record:(name,v)=>records.push({name,...v}),run:(cmd,args)=>{calls.push({cmd,args});if(cmd==='/usr/bin/plutil')return JSON.stringify({EnvironmentVariables:{KEN_DATABASE_URL:old}});if(args[0]==='print')return 'KEN_DATABASE_URL => '+old+'\n';return '';}};
+ let error;try{vm.runInNewContext(branch,context,{timeout:1000});}catch(e){error=e;}
+ if(state==='unexpected-error'){assert(error);assert(!calls.length);}else{assert(!error);assert.equal(calls.filter(x=>x.args[0]==='bootstrap').length,1);assert.equal(calls.filter(x=>x.args[0]==='bootout').length,state==='loaded'?1:0);assert.equal(records[0].verdict,'PASS');}
+ results.push({scenario:state,verdict:'PASS'});
+}
+const forwardStart=source.indexOf("  record('reload-started',{target,prior_loaded:true,phase:'before-bootout'});");
+const forwardEnd=source.indexOf(" }else if(mode===",forwardStart);assert(forwardStart>0&&forwardEnd>forwardStart);
+const journal=[],calls=[];let failed=false;
+try{vm.runInNewContext(source.slice(forwardStart,forwardEnd),{target:'gui/501/test',PLIST:'/fake',process:{getuid:()=>501},record:(name,v)=>journal.push({name,...v}),run:(cmd,args)=>{calls.push(args[0]);if(args[0]==='bootstrap')throw Error('injected bootstrap failure');return '';},assert,URL},{timeout:1000});}catch(e){failed=true;}
+assert(failed);assert.deepEqual(calls,['bootout','bootstrap']);assert.equal(journal.at(-1).phase,'bootout-complete');assert(journal.every(x=>x.name==='reload-started'));
+results.push({scenario:'bootstrap-failure-retains-recovery-journal',verdict:'PASS'});
+fs.writeFileSync(__dirname+'/rollback-fault-proof.json',JSON.stringify({at:new Date().toISOString(),results,live_commands:0},null,2)+'\n');
+console.log(JSON.stringify(results));
diff --git a/verification/ken-rollout/rollout.cjs b/verification/ken-rollout/rollout.cjs
new file mode 100644
index 0000000..28a5802
--- /dev/null
+++ b/verification/ken-rollout/rollout.cjs
@@ -0,0 +1,112 @@
+// TK11438 approved Ken-only transport migration. Never invokes the email wrapper.
+const fs=require('fs'),path=require('path'),crypto=require('crypto'),cp=require('child_process'),assert=require('assert/strict'),os=require('os');
+const ROOT='/Users/macstudio3/Projects/Ken',DIR=ROOT+'/kalshi-dash',OUT=__dirname,PRIV=OUT+'/private';
+const ENV=DIR+'/.env',WRAPPER=DIR+'/follow-the-winners-check.sh',PLIST='/Users/macstudio3/Library/LaunchAgents/com.steve.ken-reconcile-canary.plist',DUMP='/Users/macstudio3/.pm2/dump.pm2';
+const manifest=JSON.parse(fs.readFileSync('/Users/macstudio3/Projects/tk11438-postgres-migration/next-batch.json'));
+const parse=require('/Users/macstudio3/Projects/patterndesignlab/node_modules/dotenv').parse;
+const {Client}=require(DIR+'/node_modules/pg');
+const deps='/Users/macstudio3/.npm-global/lib/node_modules/pm2/node_modules/';
+const axon=require(deps+'pm2-axon'),rpc=require(deps+'pm2-axon-rpc');
+const mode=process.argv[2];assert(['prepare','apply','verify','reload','observe','rollback'].includes(mode));
+const sha=b=>crypto.createHash('sha256').update(b).digest('hex');
+const json=p=>JSON.parse(fs.readFileSync(p));
+const record=(name,x)=>fs.writeFileSync(OUT+'/'+name+'.json',JSON.stringify({at:new Date().toISOString(),...x},null,2)+'\n');
+const run=(cmd,args,opts={})=>cp.execFileSync(cmd,args,{encoding:'utf8',timeout:15000,maxBuffer:16*1024*1024,...opts});
+const git=(...args)=>run('git',args,{cwd:ROOT}).trim();
+const socketURL=s=>{const u=new URL(s);assert(['localhost','127.0.0.1','[::1]',''].includes(u.hostname));u.searchParams.set('host','/tmp');return u.toString();};
+const summarize=s=>{const u=new URL(s);return {host:u.hostname,database:u.pathname.slice(1),socket:u.searchParams.get('host'),port:u.port||'5432'};};
+const liveRpc=(method,opts={})=>new Promise((resolve,reject)=>{const sock=axon.socket('req'),client=new rpc.Client(sock);const t=setTimeout(()=>{sock.close();reject(Error('RPC timeout: '+method));},30000);sock.on('error',e=>{clearTimeout(t);sock.close();reject(e);});sock.connect('/Users/macstudio3/.pm2/rpc.sock');client.call(method,opts,(err,data)=>{clearTimeout(t);sock.close();err?reject(Error('RPC failed: '+method)):resolve(data);});});
+function unique(list){const a=list.filter(x=>x.name==='ken');assert.equal(a.length,1);const x=a[0],e=x.pm2_env||x;assert.equal(e.pm_cwd,DIR);assert.equal(e.pm_exec_path,DIR+'/start.sh');return x;}
+function fields(e){return {DATABASE_URL:e.DATABASE_URL,KEN_DATABASE_URL:e.KEN_DATABASE_URL,env:{DATABASE_URL:e.env?.DATABASE_URL,KEN_DATABASE_URL:e.env?.KEN_DATABASE_URL}};}
+function setFields(e,v){e.DATABASE_URL=v.DATABASE_URL;e.KEN_DATABASE_URL=v.KEN_DATABASE_URL;e.env.DATABASE_URL=v.env.DATABASE_URL;e.env.KEN_DATABASE_URL=v.env.KEN_DATABASE_URL;}
+function atomic(file,bytes,expected){assert.equal(sha(fs.readFileSync(file)),expected,'concurrent change: '+file);const temp=file+'.TK11438-'+process.pid;fs.writeFileSync(temp,bytes,{flag:'wx',mode:fs.statSync(file).mode&0o777});assert.equal(sha(fs.readFileSync(file)),expected,'changed during preparation: '+file);fs.renameSync(temp,file);assert.equal(sha(fs.readFileSync(file)),sha(bytes));}
+function patchDump(expected,next){const bytes=fs.readFileSync(DUMP),d=JSON.parse(bytes),e=unique(d);assert.deepEqual(fields(e),expected);setFields(e,next);const reverted=JSON.parse(JSON.stringify(d));setFields(unique(reverted),expected);assert.deepEqual(reverted,JSON.parse(bytes),'out-of-scope dump edit');atomic(DUMP,JSON.stringify(d,null,2),sha(bytes));return {before_sha:sha(bytes),after_sha:sha(fs.readFileSync(DUMP)),only_target_fields:true};}
+async function withDb(url,fn){const c=new Client({connectionString:url,application_name:'TK11438-Ken-'+mode,connectionTimeoutMillis:4000,options:'-c default_transaction_read_only=on -c statement_timeout=5000'});try{await c.connect();return await fn(c);}finally{await c.end().catch(()=>{});}}
+async function snapshot(label){
+ const list=await liveRpc('getMonitorData'),s=unique(list),e=s.pm2_env;assert.equal(e.status,'online');const env=parse(fs.readFileSync(ENV));const identities=[];
+ for(const key of ['DATABASE_URL','KEN_DATABASE_URL'])identities.push({key,...await withDb(env[key],async c=>(await c.query("SELECT current_database() database,current_user role,inet_client_addr()::text addr,current_setting('transaction_read_only') readonly")).rows[0])});
+ const settings=await withDb(env.DATABASE_URL,async c=>(await c.query("SELECT config->>'safe_mode' safe_mode,config->>'trading_on' trading_on,config->>'kalshi_env' kalshi_env,md5(config::text) config_hash,md5((config-'weather_cache')::text) stable_settings_hash,config->'weather_cache'->>'fetched_at' weather_cache_fetched_at FROM risk_state ORDER BY updated_at DESC LIMIT 1")).rows[0]);
+ const kenSettings=await withDb(env.KEN_DATABASE_URL,async c=>(await c.query("SELECT key,md5(value::text) value_hash FROM ken_config WHERE key IN ('live_run','trade_config') ORDER BY key")).rows);
+ const canarySource=fs.readFileSync(DIR+'/scripts/reconcile-canary.mjs','utf8');const sql=canarySource.match(/await pool\.query\(`([\s\S]*?)`\)/)[1];
+ const sums=await withDb(env.KEN_DATABASE_URL,async c=>(await c.query(sql)).rows[0]);
+ const num=x=>Number(x);const reconciliation={returned:Math.abs(num(sums.rollup_returned)-num(sums.ledger_returned))<=200,invested:Math.abs(num(sums.rollup_invested)-num(sums.ledger_invested))<=200,balance:Math.abs(num(sums.balance)-(num(sums.seed)-num(sums.rollup_invested)+num(sums.rollup_returned)))<=200};
+ const http=[];const lan=Object.values(os.networkInterfaces()).flat().find(x=>x.family==='IPv4'&&!x.internal&&/^192\.168\./.test(x.address));assert(lan,'LAN interface needed for real non-loopback auth test');
+ for(const route of ['/api/setup','/api/trading/history'])for(const auth of [false,true]){
+  const r=await fetch('http://'+lan.address+':7810'+route,{headers:auth?{authorization:'Basic '+Buffer.from((env.ADMIN_USER||'admin')+':'+env.ADMIN_PASSWORD).toString('base64')}:{},signal:AbortSignal.timeout(10000)});const text=await r.text();assert.equal(r.status,auth?200:401);if(auth)JSON.parse(text);http.push({route,auth,status:r.status,sha256:sha(text),bytes:Buffer.byteLength(text)});
+ }
+ let tcp='';try{tcp=run('lsof',['-nP','-a','-p',String(s.pid),'-iTCP']);}catch(err){if(err.status!==1)throw err;tcp=err.stdout||'';}
+ const processDbTcp=tcp.split('\n').filter(l=>/:5432\b/.test(l));
+ const saved=unique(json(DUMP));const savedMatch=['DATABASE_URL','KEN_DATABASE_URL'].every(k=>e[k]===env[k]&&e.env[k]===env[k]&&saved[k]===env[k]&&saved.env[k]===env[k]);
+ const activity=await withDb('postgresql:///postgres?host=/tmp',async c=>(await c.query("SELECT datname,usename,client_addr::text,client_port,pid FROM pg_stat_activity WHERE datname IN ('ken','bertha_betting') AND application_name NOT LIKE 'TK11438%' ORDER BY datname,pid")).rows);
+ const proof={label,pid:s.pid,pm_id:e.pm_id,restart_time:e.restart_time,identities,settings,kenSettings,reconciliation,http,process_db_tcp:processDbTcp,saved_match:savedMatch,targets:Object.fromEntries(['DATABASE_URL','KEN_DATABASE_URL'].map(k=>[k,summarize(env[k])])),activity,source_hash:sha(fs.readFileSync(DIR+'/server.js')),wrapper_hash:sha(fs.readFileSync(WRAPPER))};record(label,proof);return proof;
+}
+(async()=>{
+ if(mode==='prepare'){
+  assert(!fs.existsSync(PRIV),'Already prepared; do not overwrite baseline');assert.equal(git('status','--porcelain'),'','Ken working tree dirty');
+  for(const x of [...manifest.file_changes,...manifest.environment_changes])if(x.sha256)assert.equal(sha(fs.readFileSync(x.path)),x.sha256,'approved source drift: '+x.path);
+  const s=unique(await liveRpc('getMonitorData')),saved=unique(json(DUMP)),envBytes=fs.readFileSync(ENV),env=parse(envBytes);const before=fields(s.pm2_env);assert.deepEqual(before,fields(saved),'effective/dump baseline mismatch');for(const k of ['DATABASE_URL','KEN_DATABASE_URL'])assert.equal(before[k],env[k]);
+  const desired={DATABASE_URL:socketURL(env.DATABASE_URL),KEN_DATABASE_URL:socketURL(env.KEN_DATABASE_URL)};assert.equal(summarize(desired.DATABASE_URL).database,'bertha_betting');assert.equal(summarize(desired.KEN_DATABASE_URL).database,'ken');
+  let nextEnv=envBytes.toString();for(const [k,v]of Object.entries(desired)){assert(!v.includes("'"));const re=new RegExp('^(?:export\\s+)?'+k+'=.*$','gm');assert.equal([...nextEnv.matchAll(re)].length,1);nextEnv=nextEnv.replace(re,k+"='"+v+"'");}assert.deepEqual({...parse(nextEnv),...env},env);for(const k of Object.keys(env))if(!(k in desired))assert.equal(parse(nextEnv)[k],env[k]);
+  const w=manifest.file_changes[0],oldWrapper=fs.readFileSync(WRAPPER,'utf8'),newWrapper=oldWrapper.replace(w.old,w.new);assert.notEqual(oldWrapper,newWrapper);assert.equal(newWrapper.replace(w.new,w.old),oldWrapper);
+  const oldPlist=fs.readFileSync(PLIST);const plist=JSON.parse(run('/usr/bin/plutil',['-convert','json','-o','-',PLIST]));assert.equal(plist.Label,'com.steve.ken-reconcile-canary');const oldUrl=plist.EnvironmentVariables.KEN_DATABASE_URL;plist.EnvironmentVariables.KEN_DATABASE_URL=socketURL(oldUrl);const newPlist=run('/usr/bin/plutil',['-convert','xml1','-o','-','--','-'],{input:JSON.stringify(plist)});
+  fs.mkdirSync(PRIV,{mode:0o700});for(const [f,b]of Object.entries({'env.before':envBytes,'env.after':nextEnv,'wrapper.before':oldWrapper,'wrapper.after':newWrapper,'plist.before':oldPlist,'plist.after':newPlist}))fs.writeFileSync(PRIV+'/'+f,b,{mode:0o600,flag:'wx'});
+  const rec={head:git('rev-parse','HEAD'),pid:s.pid,pm_id:s.pm2_env.pm_id,before,desired,source_hash:sha(fs.readFileSync(DIR+'/server.js')),hashes:{env:sha(envBytes),wrapper:sha(oldWrapper),plist:sha(oldPlist)}};fs.writeFileSync(PRIV+'/receipt.json',JSON.stringify(rec,null,2),{mode:0o600,flag:'wx'});
+  // Rehearse full file restore and scoped dump round-trip on copies, never live.
+  const d=json(DUMP),original=JSON.parse(JSON.stringify(d));setFields(unique(d),{...desired,env:desired});setFields(unique(d),before);assert.deepEqual(d,original);
+  for(const name of ['env','wrapper','plist']){const p=PRIV+'/rehearsal-'+name;fs.copyFileSync(PRIV+'/'+name+'.before',p);atomic(p,fs.readFileSync(PRIV+'/'+name+'.after'),rec.hashes[name]);atomic(p,fs.readFileSync(PRIV+'/'+name+'.before'),sha(fs.readFileSync(PRIV+'/'+name+'.after')));assert.equal(sha(fs.readFileSync(p)),rec.hashes[name]);}
+  run('/bin/bash',['-n',PRIV+'/wrapper.after']);run('/bin/bash',['-n',PRIV+'/env.after']);run('/usr/bin/plutil',['-lint',PRIV+'/plist.after']);
+  const baseline=await snapshot('baseline');assert(Object.values(baseline.reconciliation).every(Boolean),'Existing reconciliation failure: stop before job reload');
+  record('preparation',{verdict:'PASS',rollback_rehearsal:'exact file restores plus four-field dump roundtrip PASS',application_mutations:0,email_sends:0,pid:s.pid,pm_id:rec.pm_id});
+ }else if(mode==='apply'){
+  const rec=json(PRIV+'/receipt.json');assert.equal(json(OUT+'/preparation.json').verdict,'PASS');assert(!fs.existsSync(OUT+'/applied.json'));assert.equal(git('status','--porcelain'),'');assert.equal(git('rev-parse','HEAD'),rec.head);
+  const s=unique(await liveRpc('getMonitorData'));assert.equal(s.pid,rec.pid);assert.deepEqual(fields(s.pm2_env),rec.before);assert.equal(sha(fs.readFileSync(DIR+'/server.js')),rec.source_hash);
+  for(const [p,k]of [[ENV,'env'],[WRAPPER,'wrapper'],[PLIST,'plist']])assert.equal(sha(fs.readFileSync(p)),rec.hashes[k]);assert.deepEqual(fields(unique(json(DUMP))),rec.before);
+  record('mutation-started',{scope:'Ken only',before_pid:s.pid,steps:[]});
+  atomic(ENV,fs.readFileSync(PRIV+'/env.after'),rec.hashes.env);atomic(WRAPPER,fs.readFileSync(PRIV+'/wrapper.after'),rec.hashes.wrapper);atomic(PLIST,fs.readFileSync(PRIV+'/plist.after'),rec.hashes.plist);
+  run('/bin/bash',['-n',WRAPPER]);run('/usr/bin/plutil',['-lint',PLIST]);
+  git('add','--','kalshi-dash/follow-the-winners-check.sh');git('-c','user.name=Steve Abrams','-c','user.email=steve@designerwallcoverings.com','commit','-q','-m','Use Unix socket for Ken hourly signal database');
+  const dumpProof=patchDump(rec.before,{...rec.desired,env:rec.desired});
+  record('config-applied',{dump:dumpProof,commit:git('rev-parse','HEAD'),application_restart_pending:true});
+  await liveRpc('restartProcessId',{id:rec.pm_id,env:rec.desired});
+  const after=unique(await liveRpc('getMonitorData'));assert.notEqual(after.pid,rec.pid);record('applied',{verdict:'APPLIED_VERIFY_PENDING',before_pid:rec.pid,after_pid:after.pid,commit:git('rev-parse','HEAD'),email_wrapper_executed:false});
+ }else if(mode==='verify'){
+  const p=await snapshot('after'),b=json(OUT+'/baseline.json'),rec=json(PRIV+'/receipt.json');assert(p.identities.every(x=>x.addr===null&&x.readonly==='on'));assert.equal(p.process_db_tcp.length,0);assert(p.saved_match);assert.deepEqual(p.identities.map(x=>[x.key,x.database,x.role]),b.identities.map(x=>[x.key,x.database,x.role]));assert.deepEqual(['safe_mode','trading_on','kalshi_env'].map(k=>p.settings[k]),['safe_mode','trading_on','kalshi_env'].map(k=>b.settings[k]));assert.deepEqual(p.kenSettings,b.kenSettings);assert.equal(p.source_hash,b.source_hash);assert(p.activity.some(x=>x.datname==='ken'&&x.client_addr===null));assert(p.activity.some(x=>x.datname==='bertha_betting'&&x.client_addr===null));
+  assert.equal(sha(fs.readFileSync(ENV)),sha(fs.readFileSync(PRIV+'/env.after')));assert.equal(sha(fs.readFileSync(PLIST)),sha(fs.readFileSync(PRIV+'/plist.after')));
+  const bad=new Client({host:OUT+'/missing-socket',database:'ken',connectionTimeoutMillis:1000});let code;try{await bad.connect();throw Error('unexpected missing socket connection');}catch(err){code=err.code;assert.equal(code,'ENOENT');}finally{await bad.end().catch(()=>{});}
+  // Run underlying pure SELECT module only. The wrapper that emails is excluded.
+  const wrapperUrl=fs.readFileSync(WRAPPER,'utf8').match(/KEN_DATABASE_URL="([^"]+)"/)?.[1];assert(wrapperUrl);assert.equal(new URL(wrapperUrl).searchParams.get('host'),'/tmp');assert.equal(new URL(wrapperUrl).pathname,'/ken');
+  const result=run('/opt/homebrew/bin/node',[DIR+'/follow-the-winners.mjs'],{cwd:DIR,timeout:20000,env:{PATH:process.env.PATH,HOME:process.env.HOME,USER:os.userInfo().username,KEN_DATABASE_URL:wrapperUrl,PGOPTIONS:'-c default_transaction_read_only=on -c statement_timeout=5000'}});
+  record('verification',{verdict:'PASS',pid:p.pid,two_pools_socket:true,auth_statuses:p.http.map(x=>x.status),captured_trading_controls_unchanged:true,whole_config_warning:{before:b.settings.config_hash,after:p.settings.config_hash,detail:'AutonomousScan updates weather_cache. Captured switches and ken_config hashes match; uncaptured risk_state keys cannot be proven unchanged retroactively.'},durable_match:true,missing_socket:code,signal_module:{exit:0,configuration_source:'exact KEN_DATABASE_URL from updated hourly wrapper',target:summarize(wrapperUrl),output_sha256:sha(result),bytes:result.length,email_wrapper_executed:false},reconciliation:p.reconciliation,source_unchanged:true});
+ }else if(mode==='reload'){
+  assert.equal(json(OUT+'/verification.json').verdict,'PASS');assert(!fs.existsSync(OUT+'/reloaded.json'));const target='gui/'+process.getuid()+'/com.steve.ken-reconcile-canary';const before=run('launchctl',['print',target]);assert(!/\n\s+pid = \d+/.test(before),'Canary running: wait until idle');
+  record('reload-started',{target,prior_loaded:true,phase:'before-bootout'});
+  run('launchctl',['bootout','gui/'+process.getuid(),PLIST]);record('reload-started',{target,prior_loaded:true,phase:'bootout-complete'});
+  run('launchctl',['bootstrap','gui/'+process.getuid(),PLIST]);
+  const after=run('launchctl',['print',target]);const loadedUrl=after.match(/KEN_DATABASE_URL => (\S+)/)?.[1];assert(loadedUrl);assert.equal(new URL(loadedUrl).searchParams.get('host'),'/tmp');record('reloaded',{verdict:'RELOADED_OBSERVE_PENDING',target,loaded_socket:true,loaded:true});
+ }else if(mode==='observe'){
+  const proof=await snapshot('monitor'),after=json(OUT+'/after.json');assert.equal(proof.pid,after.pid);assert.equal(proof.restart_time,after.restart_time);assert.equal(proof.process_db_tcp.length,0);assert(proof.saved_match);assert(proof.identities.every(x=>x.addr===null));
+  const target='gui/'+process.getuid()+'/com.steve.ken-reconcile-canary',loaded=run('launchctl',['print',target]);
+  const loadedUrl=loaded.match(/KEN_DATABASE_URL => (\S+)/)?.[1];assert.equal(new URL(loadedUrl).searchParams.get('host'),'/tmp');assert(/last exit code = 0\b/.test(loaded));assert(!/\n\s+pid = \d+/.test(loaded),'canary still running');assert(/run interval = 1800 seconds/.test(loaded));
+  const baseline=json(OUT+'/canary-log-baseline.json'),bytes=fs.readFileSync(baseline.path);assert(bytes.length>=baseline.size,'log rotated; inspect new evidence');const tail=bytes.subarray(baseline.size).toString();assert(tail.includes('[reconcile-canary] OK'));assert(!/DIVERGENCE|\[reconcile-canary\] error:/.test(tail));
+  const before=JSON.parse(run('/usr/bin/plutil',['-convert','json','-o','-',PRIV+'/plist.before'])),current=JSON.parse(run('/usr/bin/plutil',['-convert','json','-o','-',PLIST]));current.EnvironmentVariables.KEN_DATABASE_URL=before.EnvironmentVariables.KEN_DATABASE_URL;assert.deepEqual(current,before);
+  record('observation',{verdict:'PASS',pid:proof.pid,restart_count_stable:true,two_pools_socket:true,auth_statuses:proof.http.map(x=>x.status),reconciliation_job:{loaded_socket:true,last_exit:0,interval:1800,other_plist_fields_preserved:true,new_log_bytes:tail.length,log_sha256:sha(tail),result:'OK'},email_wrapper_invoked:false});
+ }else if(mode==='rollback'){
+  const rec=json(PRIV+'/receipt.json');assert(fs.existsSync(OUT+'/mutation-started.json'));assert.equal(sha(fs.readFileSync(DIR+'/server.js')),rec.source_hash);
+  for(const [p,k]of [[ENV,'env'],[WRAPPER,'wrapper'],[PLIST,'plist']]){const current=sha(fs.readFileSync(p)),after=sha(fs.readFileSync(PRIV+'/'+k+'.after'));assert([rec.hashes[k],after].includes(current),'peer edit blocks rollback');if(current===after)atomic(p,fs.readFileSync(PRIV+'/'+k+'.before'),after);}
+  const f=fields(unique(json(DUMP)));if(JSON.stringify(f)!==JSON.stringify(rec.before))patchDump({...rec.desired,env:rec.desired},rec.before);
+  await liveRpc('restartProcessId',{id:rec.pm_id,env:{DATABASE_URL:rec.before.DATABASE_URL,KEN_DATABASE_URL:rec.before.KEN_DATABASE_URL}});
+  if(fs.existsSync(OUT+'/reload-started.json')){
+   const target='gui/'+process.getuid()+'/com.steve.ken-reconcile-canary';
+   const loaded=cp.spawnSync('launchctl',['print',target],{encoding:'utf8',timeout:10000});
+   if(loaded.status===0)run('launchctl',['bootout','gui/'+process.getuid(),PLIST]);
+   else assert(/could not find service|service not found/i.test(loaded.stderr||''),'unexpected launchd state; inspect before rollback');
+   run('launchctl',['bootstrap','gui/'+process.getuid(),PLIST]);
+   const restored=run('launchctl',['print',target]),loadedUrl=restored.match(/KEN_DATABASE_URL => (\S+)/)?.[1];
+   const originalPlist=JSON.parse(run('/usr/bin/plutil',['-convert','json','-o','-',PRIV+'/plist.before']));
+   assert.equal(loadedUrl,originalPlist.EnvironmentVariables.KEN_DATABASE_URL);
+   record('reload-rollback',{verdict:'PASS',prior_job_restored:true,target});
+  }
+  record('rollback',{verdict:'RESTORED_VERIFY_BASELINE_REQUIRED'});
+ }
+ console.log(JSON.stringify({mode,verdict:'PASS',evidence:OUT}));
+})().catch(err=>{record(mode+'-failure',{verdict:'FAIL',code:err.code||err.name,message:String(err.message).replace(/postgres(?:ql)?:\/\/\S+/g,'[REDACTED_URL]')});console.error(JSON.stringify({mode,verdict:'FAIL',code:err.code||err.name,message:String(err.message).replace(/postgres(?:ql)?:\/\/\S+/g,'[REDACTED_URL]')}));process.exitCode=1;});
diff --git a/verification/ken-rollout/verification.json b/verification/ken-rollout/verification.json
new file mode 100644
index 0000000..8dd3864
--- /dev/null
+++ b/verification/ken-rollout/verification.json
@@ -0,0 +1,39 @@
+{
+  "at": "2026-09-11T17:38:04.312Z",
+  "verdict": "PASS",
+  "pid": 25903,
+  "two_pools_socket": true,
+  "auth_statuses": [
+    401,
+    200,
+    401,
+    200
+  ],
+  "captured_trading_controls_unchanged": true,
+  "whole_config_warning": {
+    "before": "2fddc5b0ff823bbe1ea724dca0dca0d0",
+    "after": "402d274e93015b9d3c875fa9aa76b60a",
+    "detail": "AutonomousScan updates weather_cache. Captured switches and ken_config hashes match; uncaptured risk_state keys cannot be proven unchanged retroactively."
+  },
+  "durable_match": true,
+  "missing_socket": "ENOENT",
+  "signal_module": {
+    "exit": 0,
+    "configuration_source": "exact KEN_DATABASE_URL from updated hourly wrapper",
+    "target": {
+      "host": "localhost",
+      "database": "ken",
+      "socket": "/tmp",
+      "port": "5432"
+    },
+    "output_sha256": "68cd0b07d3758275b99e33dddcbf8f6d619a198016d6f8fc68f019920f4195b5",
+    "bytes": 978,
+    "email_wrapper_executed": false
+  },
+  "reconciliation": {
+    "returned": true,
+    "invested": true,
+    "balance": true
+  },
+  "source_unchanged": true
+}
diff --git a/verification/ken-rollout/verify-failure.json b/verification/ken-rollout/verify-failure.json
new file mode 100644
index 0000000..645337a
--- /dev/null
+++ b/verification/ken-rollout/verify-failure.json
@@ -0,0 +1,6 @@
+{
+  "at": "2026-09-11T17:37:10.631Z",
+  "verdict": "FAIL",
+  "code": "Error",
+  "message": "RPC timeout: getMonitorData"
+}
diff --git a/verification/ken-rollout/verify-rpc-timeout.json b/verification/ken-rollout/verify-rpc-timeout.json
new file mode 100644
index 0000000..645337a
--- /dev/null
+++ b/verification/ken-rollout/verify-rpc-timeout.json
@@ -0,0 +1,6 @@
+{
+  "at": "2026-09-11T17:37:10.631Z",
+  "verdict": "FAIL",
+  "code": "Error",
+  "message": "RPC timeout: getMonitorData"
+}

← f93e515 Verify socket handoff and prepare Ken scheduler migration ap  ·  back to Tk11438 Postgres Migration  ·  Classify remaining PostgreSQL consumers and prepare Gracie s b4b5e5c →