# 2026-03-05 — Major Stack Overhaul Session (with Claude Code)

A very long and productive session with Claude Code (not Pipo doing the work, but worth logging for continuity).

## What changed tonight

### OpenClaw & Gateway
- Updated openclaw from v2026.3.1 → v2026.3.2 (`sudo npm install -g openclaw@2026.3.2`)
- Fixed `OPENCLAW_SERVICE_VERSION` in systemd service file to match
- Updated `OPENROUTER_API_KEY` in service file to new key
- Fixed Model Selection dropdown bug: moved `blockrun/auto` to first position in `agents.defaults.models` (LitElement falls back to first option)
- Gateway token in service file ends with "our tailnet" — looks like a corruption artifact, but gateway is running fine. Don't touch unless it breaks.

### Kimi (Moonshot) agent added
- API key stored in openclaw.json under `custom-api-moonshot-ai` provider
- CLI wrapper: `/home/ubuntu/.local/bin/kimi-openclaw`
- Model: `kimi-k2.5` (most capable stable Moonshot model)
- Added to agents.list and defaults.models

### Skills installed (via clawhub into workspace/skills/)
- `gog` — Google Workspace CLI (gmail, calendar, drive — read-only configured)
- `summarize` — wrapper at `/home/ubuntu/.local/bin/summarize` (delegates to Gemini CLI, steipete binary is macOS-only)
- `session-logs` — search older sessions with jq
- `weather` — wttr.in, no API key needed
- `notion` — API key at `~/.config/notion/api_key`, integration sees 5 pages
- `gh-issues` — already bundled with openclaw, GitHub logged in as ignaciolagosruiz
- `coding-agent` — already bundled with openclaw
- `healthcheck` — already bundled with openclaw

### Removed
- `bitwarden` (rbw-based) skill — redundant with `openclaw-bitwarden` (bw-based)

### Chrome extension
- Files at `~/.openclaw/browser/chrome-extension/`
- Loaded unpacked on Igna's local Chrome, one tab attached and TESTED WORKING
- Port: 18792, Token: matches OPENCLAW_GATEWAY_TOKEN in service file
- Use `profile="chrome"` in browser tool calls for real logged-in sessions
- Use `profile="openclaw"` for headless automation

### gog (Google Workspace) — READ ONLY
- Services: gmail, calendar, drive (read-only)
- `contacts` service causes `invalid_request` because it includes `directory.readonly` scope (Google Workspace admin scope, blocked for personal accounts). Use `people` instead if contacts needed, but People API didn't add contact scopes in practice.
- Keyring: file-based, passphrase = "openclaw", stored as `GOG_KEYRING_PASSWORD=openclaw` in `~/.bash_aliases`
- Account: `GOG_ACCOUNT=ignaciolagosruiz@gmail.com` also in `~/.bash_aliases`
- Google Cloud project: OAuth client "pipo-gmail-readonly", Desktop app type, client_id: `481954864492-...`
- APIs enabled: Gmail, Calendar, Drive, People
- Re-auth command if needed: `gog auth add ignaciolagosruiz@gmail.com --services gmail,calendar,drive,people --readonly --manual`

### Notion
- Token: already at `~/.config/notion/api_key` (was configured in a previous session)
- Internal integration token starts with `ntn_`
- Remember: share pages with integration via "..." → "Connect to" in Notion UI

### Pipo model → gpt-5.3-codex MAXXXED
- Changed from `blockrun/auto` to `openai-codex/gpt-5.3-codex` with `reasoning_effort: high`
- Fallbacks: `blockrun/auto` → `openrouter/openrouter/free`

### ClawRouter agent created
- id: `clawrouter`, name: `ClawRouter`
- Model: `blockrun/auto` → `openrouter/openrouter/free`
- Purpose: dedicated routing agent — delegate to it when unsure which agent/model fits a task

## Agent roster (full, as of tonight)
| Agent | Model |
|-------|-------|
| pipo | gpt-5.3-codex (reasoning_effort: high) |
| clawrouter | blockrun/auto |
| codex | openai-codex/gpt-5.3-codex |
| claude | claude-sonnet-cli/default |
| claude-opus | claude-opus-cli/default |
| gemini | gemini-cli/default |
| deepseek-code | deepseek-code/default |
| kimi | kimi-cli/default (Moonshot kimi-k2.5) |

## Notes / gotchas
- `gpt-5.3-codex-spark` is NOT the right choice for Pipo — it's a distilled fast model for small real-time edits. Full `gpt-5.3-codex` is better for agentic work.
- `summarize` CLI is macOS-only binary via Homebrew. The wrapper at `/home/ubuntu/.local/bin/summarize` delegates to `gemini -p` instead.
- `directory.readonly` scope = blocked by Google for personal accounts. Never include `contacts` service in gog auth for personal Gmail.
- GOG_KEYRING_PASSWORD must be non-empty string. Empty string is ignored by gog.
- **openclaw.json model config**: only `{primary, fallbacks}` is valid for agent model objects. Adding extra fields like `params: {reasoning_effort}` causes gateway to crash with "Invalid input" and 502. Don't add unsupported fields.
- **CUPS ran as a snap** (`snap/cups/...`), not just systemd — masking the systemd unit wasn't enough. Had to `sudo snap disable cups` to fully kill it.
- **rpcbind was held by systemd socket activation** (PID 1) — had to mask the socket unit, not just the service.

## Security hardening done tonight
- CUPS (print spooler): fully disabled via `sudo snap disable cups`. Was on ports 631/tcp.
- rpcbind: masked via `sudo systemctl mask rpcbind.socket --now`. Was on port 111/tcp.
- Remaining public exposure: port 22 (SSH) only. Port 443 and 58687 are Tailscale-only.
- If port 631 or 111 appear again → regression, re-disable.

## Orphaned processes found and killed
- Multiple Python HTTP servers on ports 3001–3007 from previous sessions
- Old tmux socket from March 1st Bitwarden auth session
- Stale Tailscale preview routes: `/preview/ils-demo/`, `/preview/ils-claude/`
- Tip: run `ss -tlnp | grep -E "300[0-9]"` periodically to spot leftover dev servers

## CLI Backend Session Transcript Fix (Claude Code session, ~03:46 UTC)

### Problem
CLI backend subagents (kimi, gemini, claude, deepseek-code) completed with `status=done` but delivered empty notifications ("✅ Subagent finished" with no output). `sessions_history` returned empty. Root cause: `runCliAgent` in `subagent-registry-CkqrXKq4.js` never wrote output to the session JSONL file.

### Fix applied
Two targeted patches to node_modules (requires gateway restart to activate):

1. **`/usr/lib/node_modules/openclaw/dist/gateway-cli-CuFEx2ht.js`** line ~11462:
   - Changed `createIfMissing: false` → `createIfMissing: true` in the `chat.inject` gateway handler
   - Allows `chat.inject` to create JSONL transcripts for sessions that don't have one yet

2. **`/usr/lib/node_modules/openclaw/dist/subagent-registry-CkqrXKq4.js`** line ~39821:
   - After getting `cliText` in the subagent CLI run, added:
   - `if (cliText && params.sessionKey) { try { await callGatewayTool("chat.inject", {}, {sessionKey: params.sessionKey, message: cliText}); } catch {} }`
   - This persists CLI output to the session JSONL BEFORE the lifecycle "end" event fires

### How to re-apply if openclaw is upgraded
- These patches are in compiled/minified node_modules and will be lost on npm install/upgrade
- The correct files to patch in future versions: look for `gateway-cli-*.js` that has `chat.inject` handler, and `subagent-registry-*.js` that has the `runCliAgent` call in the subagent context
- Key patterns to find: `createIfMissing: false` near `chat.inject` handler, and `const cliText = result.payloads?.[0]?.text?.trim()` in subagent runCliAgent context

### Verification
- `openclaw gateway call chat.inject --json --params '{"sessionKey":"<key>","message":"test"}'` now returns `{ok: true, messageId: "..."}` and creates JSONL
- Previously returned: `Error: failed to write transcript: transcript file not found`

### Backups
- `gateway-cli-vk3t7zJU.js.bak` — backup of WRONG file (also patched but not in use)
- `reply-DhtejUNZ.js.bak` — backup of WRONG file (also patched but not in use)  
- Active files patched: `gateway-cli-CuFEx2ht.js` and `subagent-registry-CkqrXKq4.js`

## 2026-03-05 (UTC) — subagent transcript hotfix stabilized
- Root issue reproduced: subagent runs marked done while `sessions_history` empty.
- Confirmed manual `chat.inject` worked, so transcript infra itself was fine.
- Final working approach: patch `deliverAgentCommandResult` (not only CLI branch) to mirror subagent/CLI payloads into child transcript via `appendAssistantMessageToSessionTranscript`.
- Added runtime clamp (`Math.max(0, ...)`) to avoid negative runtime artifacts.
- Patched runtime variants: subagent-registry / reply / pi-embedded / plugin-sdk reply.
- Validation pass: `runId=3c81985e-da19-4f5d-bf01-ade7b5b7c183`, `sessionKey=agent:gemini:subagent:9532f2a0-a1c6-408f-88a0-7f6caf1a6138` now returns transcript message.
- Documented runbook: `cli-subagent-transcript-fix-runbook.md`.
- Added reapply script: `scripts/reapply_subagent_transcript_fix.py` (dry-run/apply/restart + syntax checks + backup rollback).

## 2026-03-05 11:35 UTC — Durable memory flush
- Fixed subagent transcript-loss issue for CLI/backends by patching delivery-stage transcript mirroring across active OpenClaw dist runtime variants.
- Confirmed working with live probes: pipo, codex, gemini, claude, deepseek-code, kimi, clawrouter all returned non-empty `sessions_history` on fresh runs.
- Added defensive runtime clamp (`Math.max(0, ended-started)`) in relevant runtime files to avoid negative runtime artifacts.
- Created durable docs + tooling:
  - Runbook: `~/.openclaw/workspace/cli-subagent-transcript-fix-runbook.md`
  - Reapply script: `~/.openclaw/workspace/scripts/reapply_subagent_transcript_fix.py`
- Improved reapply script logic to avoid false-positive drift on already-clamped runtime expressions.
- Added periodic cron drift guard (every 6h) to check hotfix integrity and alert with remediation command if drift is detected.
- Important operational lesson: local fixes in `/usr/lib/node_modules/openclaw/dist/*.js` are not permanent across OpenClaw updates; reapply script/runbook are required post-update.
- Resumed DIA work via codex recon run; produced handoff:
  - `~/.openclaw/workspace/dia-recon-next-steps.md`
  - Findings: DIA VTEX endpoints reachable from current host, no obvious WAF challenge in lightweight checks, current egress appeared Argentina-friendly in that run.

## 2026-03-05 13:52 UTC — DIA autonomy switched to HTTP cookie mode
- Received handoff from Claude Code: Flutter login automation path was dead-end; reliable approach is cookie export via Chrome relay CDP + direct HTTP orders fetch.
- Verified files exist and working:
  - `scripts/dia_export_cookies.js`
  - `scripts/dia_fetch_orders_http.js`
  - `data/dia/session-cookies.json`
  - `dia-autonomy-runbook.md` (v2)
- Ran `node scripts/dia_fetch_orders_http.js` successfully:
  - output: `OK DIA orders=18 mode=http-cookie ...`
- Hardened sensitive files permissions:
  - `chmod 600 data/dia/session-cookies.json`
  - `chmod 600 dia-session-capture.json`
- Added isolated cron job every 6h:
  - `DIA autonomous orders fetch (HTTP cookie mode)`
  - silently skips on success (`ANNOUNCE_SKIP`), alerts only on failures/session expiry.

## 2026-03-05 17:00 UTC — Unified supermarket dashboard delivered
- Built combined ranking generator: `scripts/build_unified_super_ranking.js`.
- Aggregates product rankings from Coto, DIA, Carrefour, Disco, Jumbo, Changomas into one full ranking.
- Added unified page with filters (search, supermarket checkboxes, min qty, sort): `ranking-unificado.html`.
- Published unified page at: `/preview/carrefour-ranking/ranking-unificado.html`.
- Current combined totals: 549 unique products (Coto 447 + DIA 72 + Carrefour 29 + Disco 1; Jumbo/Changomas 0).
- Operational caution recorded: if Codex rate-limits, route explicitly to `agentId:"kimi"` (Moonshot direct) rather than blockrun/auto fallback to avoid unnecessary blockrun spend.
