# 2026-03-12

## n8n local documentation fix
- Documented the local `n8n-agent` service in `TOOLS.md` after noticing it had been set up but not captured in workspace notes.
- Recorded current known service details from prior host/runtime evidence:
  - port `18808 -> 5678`
  - LAN/Tailscale URLs
  - dedicated agent-oriented n8n instance
  - `openclaw-ping` webhook path tested end-to-end
- Operating note: prefer narrow webhook-first integration rather than broad n8n admin/API access.
- Will clarified the primary host LAN IP to use/document is `192.168.153.113`.
- Finished local skill `skills/n8n-webhook` for authenticated webhook-first n8n integration, including `scripts/call-webhook.sh`, `scripts/call-action.sh`, `scripts/validate-workflow.py`, an importable `assets/openclaw-action.workflow.json`, sample payloads, payload notes, and a successful package/validation run to `/tmp/n8n-skill-dist/n8n-webhook.skill`.
- The shipped `openclaw-action` workflow intentionally leaves Webhook authentication unset in export JSON; after import, bind local n8n Header Auth credentials manually using `x-openclaw-secret` so secrets are not embedded in the skill asset.
- Live n8n API access was confirmed and used on 2026-03-12 against `http://192.168.153.113:18808` (public API + existing webhook credential available in the instance).
- Created and activated live workflow `openclaw-action` via the n8n API.
- First live implementation matched the original asset shape (`Webhook -> Set -> Switch -> Respond`) but failed at runtime: executions errored in the `normalize-request` Set node with `invalid syntax` on its expressions.
- Fix: replaced the live router logic and shipped asset implementation with a simpler, working internal design: `Webhook -> Code -> Respond to Webhook`, while preserving the external contract (`append_log`, `notify`, normalized JSON success/failure responses).
- Important operational note: the workflow initially activated without a usable production route because the Webhook node lacked a `webhookId`; adding one and re-publishing was necessary for proper webhook registration.
- Current state before compaction: the live `openclaw-action` workflow exists in n8n, is active, and has been updated to the simpler Code-node implementation; post-update live response testing was still in progress at compaction time.
- After compaction, live verification succeeded against the production webhook:
  - `append_log` returned `200` with normalized JSON success payload
  - `notify` returned `200` with normalized JSON success payload
  - unknown action returned `400` with `{ code: "unknown_action" }`
- The packaged skill artifact was refreshed after the router simplification at `/tmp/n8n-skill-dist/n8n-webhook.skill`.
- Follow-up implementation for real side effects:
  - `notify` was successfully wired to the existing Telegram + Discord credentials and verified live multiple times.
  - `append_log` hit two dead ends before settling on the clean solution:
    1. `Execute Command` node was unavailable in this n8n build (`Unrecognized node type: n8n-nodes-base.executeCommand`).
    2. `Read/Write Files from Disk` was available, but candidate paths were either missing or not writable in this container/runtime.
  - Final fix: switched `append_log` to use n8n workflow static data (`$getWorkflowStaticData('global')`) under key `actionLog`, capped to the latest 200 entries.
  - Verified persisted state via the n8n API: `staticData.global.actionLog` contains the live test record for request `live-log-003`.
  - Conclusion: for small recent operational breadcrumbs, workflow static data is the right sink here; MinIO is better reserved for later archival/rotation/export use cases rather than tiny per-event appends.
- Added action `get_logs` to the live `openclaw-action` workflow and local `n8n-webhook` skill.
  - `get_logs` reads from workflow static data key `actionLog`
  - default limit `20`, clamped to `1..50`, newest-first
  - verified live with request `live-getlogs-001` returning the seed record from `live-log-004`
- Re-verified the three live actions together after the update:
  - `append_log` → success
  - `get_logs` → success
  - `notify` → success
- Refreshed packaged skill artifact again at `/tmp/n8n-skill-dist/n8n-webhook.skill`.
- Will clarified a standing operating preference: treat local n8n as an assistant tool to use proactively when appropriate, not as something needing separate approval each time.
- Extended the shipped `skills/n8n-webhook` router asset beyond the original live trio (`append_log`, `get_logs`, `notify`) to add:
  - `send_email_draft`
  - `create_calendar_event`
  - `approval_queue_add`
  - `approval_queue_list`
  - `approval_queue_resolve`
  - `fetch_and_normalize_url`
  - `inbound_event_filter`
- Design choice for the new actions: keep the starter workflow immediately usable without new provider credentials by using n8n workflow static data for approval queue/history/event state, while leaving room to wire provider-backed email/calendar executors later.
- Updated local docs, validator, and sample payloads for the expanded action bus and re-ran local structural validation successfully.
- Live n8n re-import/update was not completed in this pass because the current session did not have a verified safe path into the already-running instance (no confirmed admin/browser path and no confirmed current webhook secret for live test calls).
- Follow-up in the next direct session: recovered the already-verified live n8n API path from the earlier session log and used it to deploy the expanded `openclaw-action` workflow in place.
- Live verification of the expanded action set after deployment:
  - `append_log` → `200`
  - `get_logs` → `200`
  - `send_email_draft` → `200` (approval-queued)
  - `create_calendar_event` → `200` (approval-queued)
  - `approval_queue_add` → `200`
  - `approval_queue_list` → `200`
  - `approval_queue_resolve` → `200`
  - `inbound_event_filter` → `200`
  - `notify` → `200`
  - unknown action → `400` with `unknown_action`
- `fetch_and_normalize_url` initially failed in the Code node because global `fetch` was unavailable; a second attempt using Node built-ins failed because module imports were disallowed in the n8n runtime.
- Final fix for URL fetching: switched `fetch_and_normalize_url` to n8n's runtime helper `this.helpers.httpRequest`, which worked. Added optional arg `skip_ssl_certificate_validation: true` for environments where the container CA bundle is insufficient.
- Verified `fetch_and_normalize_url` live with:
  - local HTTP URL `http://192.168.153.113:18808/healthz` → success
  - `https://example.com` with `skip_ssl_certificate_validation: true` → success
- Cleanup: resolved the temporary verification approval items so `approvalQueue` ended empty after testing.
- State check before attempting deeper executor work: the live n8n instance currently exposes only four credentials via the public API — `Discord Bot Auth`, `Telegram Bot (OpenClaw)`, `OpenClaw Webhook Header`, and `Header Auth account`. No Gmail/Google Calendar credentials were present, so provider-backed email/calendar execution was intentionally not faked.
- Implemented the first true approval-gated executor that matches currently available creds:
  - new action `send_notification_draft`
  - queues a pending notification in `approvalQueue`
  - when approved via `approval_queue_resolve`, it executes the existing `notify` path and sends through Telegram + Discord
- Verified live end-to-end on 2026-03-12:
  - `send_notification_draft` returned `200` and produced pending id `approval-mmnr8pyq-tjxiqkps`
  - approving that item via `approval_queue_resolve` returned `executed: true` and `executed_action: "notify"`
  - `approval_queue_list` showed `pending_count: 0` afterward and recorded the execution metadata in history
- Will explicitly reinforced a durable operating expectation: local n8n, including its live public API, should be treated as assistant-owned tooling. If the correct path is the n8n API, use it directly instead of re-asking for permission or acting blocked.
- After Google Workspace auth was completed with `gog`, headless testing showed an important automation constraint: real non-TTY `gog` calls fail unless `GOG_KEYRING_PASSWORD` is present, because the current `gog` file keyring backend cannot prompt in automation. However, `gog --dry-run` for Gmail draft creation and Calendar event creation works without unlocking the keyring, which made it possible to fully validate executor plumbing safely.
- Implemented a host-side bridge script at `skills/n8n-webhook/scripts/resolve-approval-with-gog.py`.
  - flow: resolve approval in n8n → execute supported kinds on host via `gog` → write execution metadata back into n8n history
  - supported host-executed kinds:
    - `email_draft` → `gog gmail drafts create`
    - `calendar_event` → `gog calendar create`
- Expanded the live `openclaw-action` workflow with new action `approval_history_attach_execution`, allowing host-side executors to patch resolved history entries with execution status/details.
- Live dry-run verification on 2026-03-12 succeeded end-to-end:
  - queued one `email_draft` approval item and one `calendar_event` item
  - resolved both via the new host bridge with `--dry-run`
  - `gog` returned dry-run JSON for both operations without touching Google state
  - `approvalHistory` entries were updated in n8n with execution metadata:
    - email draft item id `approval-mmnsx7iz-k26qb60c` → `execution.op = gmail.drafts.create`, `status = dry_run`
    - calendar item id `approval-mmnsx7ji-3rt7yd74` → `execution.op = calendar.create`, `status = dry_run`
- Current practical next step for real Gmail/Calendar execution: provide `GOG_KEYRING_PASSWORD` to the runtime environment that will invoke the bridge script, or switch `gog` to a keyring backend that supports unattended access on this host.
- Follow-up completion on 2026-03-12:
  - stored local-only Gog automation env in `/home/openclaw/.openclaw/credentials/gog.env` with restrictive permissions (`600`)
  - updated `resolve-approval-with-gog.py` to auto-load that file when present
  - verified non-interactive headless Gmail access works using the stored env (successful `gog gmail search ... --json --no-input`)
  - verified the bridge itself auto-loads the env file by resolving a fresh `email_draft` approval item in `--dry-run` mode and attaching execution metadata successfully without manually exporting `GOG_ACCOUNT` / `GOG_KEYRING_PASSWORD`
- Real direct Google sanity checks succeeded after that:
  - created a Gmail draft to `william.valentin.info@gmail.com` with subject `Test draft from zap`
  - deleted the same draft successfully and verified removal via follow-up `404 notFound`
- Created top-level state file `WIP.md` to track the current Google Workspace + n8n integration plan, status, completed work, and next steps.
- Updated `memory/tasks.json` so the overlapping Google Workspace / calendar / email tasks moved from `open` to `in-progress` and now point at the current WIP file.
- Will explicitly noted a durable capability reminder: zap also has access to Will's own Gitea repo on the LAN and can use it when repo-backed tracking/sync is useful.
- Real end-to-end Google execution via n8n approval + gog bridge was completed (non-dry-run) for both target flows:
  - Gmail draft flow (`send_email_draft`): queued, approved through `resolve-approval-with-gog.py`, verified with `gog gmail drafts get`, and deleted with `gog gmail drafts delete --force`.
    - approval id: `approval-mmnvjcak-qcuhbzqd`
    - draft id: `r348335896293726096`
    - subject: `[zap n8n e2e] Gmail draft test 20260312T194153Z`
  - Calendar event flow (`create_calendar_event`): queued, approved through the same bridge, verified with `gog calendar get primary <eventId>`, and deleted with `gog calendar delete primary <eventId> --force`.
    - approval id: `approval-mmnvjyo5-uezhcw84`
    - event id: `il3ojkfnsnq3uhlepvrmaklpq4`
    - title: `[zap n8n e2e] Calendar test 20260312T194222Z`
- Important command-shape notes captured from the live run:
  - `gog calendar get` and `gog calendar delete` expect `<calendarId> <eventId>` argument order.
  - `gog gmail drafts delete` required `--force` for non-interactive cleanup.
- Will also set a new operating preference for context use: for non-trivial implementation work, prepare file-based state/handoff (`WIP.md`, `HANDOFF.md` as needed), then start a fresh isolated implementation session/run instead of continuing inside a bloated main-session context window.
- Implemented that preference locally by:
  - adding a `Fresh-session implementation discipline` section to `AGENTS.md`
  - creating `HANDOFF.md` as the immediate baton-pass file for the next clean implementation session
  - updating `WIP.md` with a `Next-session handoff` section

## Fresh clean-context re-run (implementation subagent)
- Executed the requested fresh-session baton pass from `HANDOFF.md` + `WIP.md` and re-proved the two real approval-routed Google flows end-to-end through n8n + host `gog` bridge.
- Real Gmail draft flow (`send_email_draft`) re-run:
  - approval id: `approval-mmnvn4t2-w2rjlwz2`
  - draft id: `r-3319106208870238577`
  - subject: `[zap n8n e2e] Gmail draft test 20260312T194450Z`
  - verification: `gog gmail drafts get <draftId> --json --no-input` returned the draft payload with expected subject/body
  - cleanup: `gog gmail drafts delete <draftId> --force` returned `{ "deleted": true, ... }`
- Real Calendar event flow (`create_calendar_event`) re-run:
  - approval id: `approval-mmnvn6i8-e9eq8gdf`
  - event id: `m7prri8vk2opuo6loq3qgtvsv4`
  - title: `[zap n8n e2e] Calendar test 20260312T194450Z`
  - verification: `gog calendar get primary <eventId> --json --no-input` returned the created event
  - cleanup: `gog calendar delete primary <eventId> --force` returned `{ "deleted": true, ... }`
- Refreshed baton/state files (`HANDOFF.md`, `WIP.md`) to mark this fresh-session proof as complete and move next target to expanding Gmail/Calendar action coverage (list/update/delete flows + operator playbook).

## Delegation tier policy update (fresh implementation run)
- Updated local delegation policy to use LiteLLM-targeted tiers:
  - simple/light → `litellm/glm-4.7-flash`
  - medium/default → `litellm/glm-5`
  - hardest/high-stakes → `litellm/gpt-4.5`
- Applied consistently in:
  - `skills/delegation-router/SKILL.md` (tier map + spawn examples)
  - `AGENTS.md` (workspace routing guidance section)
  - `USER.md` (user preference line)
  - `MEMORY.md` (durable preference line)

## Gmail pass 1 (fresh subagent implementation)
- Added to `openclaw-action` workflow contract:
  - `list_email_drafts`
  - `delete_email_draft`
  - `send_gmail_draft` (plus alias `send_approved_email`)
- Added explicit approval metadata in queued action responses (`approval.policy`, `approval.required`, `approval.mutation_level`) and set mutating Gmail actions to `high`.
- Extended host bridge `resolve-approval-with-gog.py` with executor coverage for:
  - `email_list_drafts` → `gog gmail drafts list`
  - `email_draft_delete` → `gog gmail drafts delete`
  - `email_draft_send` → `gog gmail drafts send`
- Verification evidence (local/targeted):
  - workflow structure + contract validator passed
  - route-action simulation request IDs:
    - `verify-list-001`
    - `verify-delete-001`
    - `verify-send-001`
    - `verify-send-alias-001`
  - simulation produced pending IDs:
    - `approval-mmny879w-5sncgd98`
    - `approval-mmny879w-a353xg8q`
    - `approval-mmny879w-yvqzokpz`
    - `approval-mmny879w-md99hqxs`
  - `gog` dry-run command checks for list/delete/send each exited `0`.

## Calendar pass 2 (fresh subagent implementation, locally verified)
- Added to `openclaw-action` workflow contract:
  - `list_upcoming_events`
  - `update_calendar_event`
  - `delete_calendar_event`
- Preserved explicit approval metadata/policy:
  - `list_upcoming_events` → `approval.mutation_level = low`
  - `update_calendar_event` / `delete_calendar_event` → `approval.mutation_level = high`
- Extended host bridge `resolve-approval-with-gog.py` with executor coverage for:
  - `calendar_list_events` → `gog calendar events`
  - `calendar_event_update` → `gog calendar update`
  - `calendar_event_delete` → `gog calendar delete`
- Added sample payloads:
  - `skills/n8n-webhook/assets/test-list-upcoming-events.json`
  - `skills/n8n-webhook/assets/test-update-calendar-event.json`
  - `skills/n8n-webhook/assets/test-delete-calendar-event.json`
- Verification evidence (local/targeted):
  - workflow structure + contract validator passed after the calendar additions
  - workflow asset inspection confirmed the three new router actions are present
  - bridge command-builder checks from shipped payloads produced:
    - `gog calendar events primary --account will@example.com --json --no-input --max 10 --days 7 --query zap --dry-run`
    - `gog calendar update primary example-calendar-event-id --account will@example.com --json --no-input --send-updates none --summary Updated call with vendor --from 2026-03-13T18:15:00Z --to 2026-03-13T18:45:00Z --description Updated by OpenClaw action bus. --location Updated room --dry-run`
    - `gog calendar delete primary example-calendar-event-id --account will@example.com --json --no-input --force --send-updates none --dry-run`
  - `python3 -m py_compile skills/n8n-webhook/scripts/resolve-approval-with-gog.py` passed.

## Live deploy + smoke verification
- Re-synced the active n8n workflow `Jwi54VWMdlLqYnRo` from the current `openclaw-action.workflow.json` asset while preserving the bound webhook credential + webhook id.
- First sync exposed that the live workflow had still been on the older minimal router; re-synced again from the current full asset and re-activated successfully.
- Safe production-webhook smoke calls succeeded:
  - `append_log` → ok
  - `get_logs` → ok
  - `list_email_drafts` → queued_for_approval
  - `list_upcoming_events` → queued_for_approval
  - `approval_queue_list` → ok with `pending_compact` + `history_compact`
  - `fetch_and_normalize_url` against local `/healthz` → ok / HTTP 200
  - unknown action → expected HTTP 400 / `unknown_action`
- Smoke-created pending approvals were rejected/cleaned:
  - `approval-mmnzm1ev-yjk46sd1`
  - `approval-mmnzm1gi-l7yszi92`
  - `approval-mmnzmw80-kb8szya2`
  - `approval-mmnzmw9w-c25hlml4`
- Remaining pending items after cleanup were older pre-existing queue items and were intentionally left alone.

## Subagent monitoring thresholds
- Added an explicit operating rule for fresh implementation runs:
  - first routine check at ~5 minutes if still running
  - inspect child history at ~10 minutes
  - narrow pass feels suspiciously long at ~12 minutes and should be actively intervened by ~15 minutes absent crisp progress
  - medium bounded pass feels suspiciously long at ~20 minutes and should be actively intervened by ~25 minutes absent crisp progress
- Also recorded the fallback rule: if the run is looping, not updating `WIP.md`, or returns an unusable result, finish the pass directly in the main session after one inspection.

## Operator/polish pass 3 (fresh subagent implementation, locally verified)
- Added explicit approval families + defaults across the n8n action bus:
  - notification → required/high
  - gmail read-only → required/low
  - gmail mutating → required/high
  - calendar read-only → required/low
  - calendar mutating → required/high
- Added low-noise operator/history reporting in the workflow:
  - `payload_preview`
  - `operator.summary_line`
  - `operator.execution_state`
  - `operator.result_refs`
  - compact list surfaces: `pending_compact`, `history_compact`, `item_compact`
- Extended the host bridge so attached execution metadata now includes:
  - `execution.summary`
  - `execution.result_refs`
- Added recurring verification payloads with stable proof IDs:
  - `skills/n8n-webhook/assets/test-verify-email-draft-cycle.json` → `verify-email-draft-cycle-001`
  - `skills/n8n-webhook/assets/test-verify-calendar-event-cycle.json` → `verify-calendar-event-cycle-001`
- Local verification proofs:
  - workflow validator passed after the operator/history changes
  - bridge helper proof: `gmail.drafts.create` sample result produced `draft_id = r-proof-draft-123`
  - bridge helper proof: `calendar.create` sample result produced `event_id = evt-proof-456`, `calendar = primary`
  - workflow asset string checks confirmed presence of `pending_compact`, `history_compact`, `summary_line`, `result_refs`, `default_mode`, and `approval.family`

## Drive/Docs/Sheets evaluation (main session)
- Hit GPT-5.4 rate limit during attempted subagent work; continued evaluation in main session with GLM 5.
- Completed decision on expanding Google Workspace action bus beyond Gmail + Calendar into Drive / Docs / Sheets.
- **Decision: NO for all three surfaces — defer for now.**
- Rationale:
  - Drive: discovery/search operations are tools, not approval-worthy events; file management is edge case; direct `gog drive ...` usage is simpler.
  - Docs: editing is inherently iterative (see, tweak, see again); document work belongs in focused tool flows, not a one-shot queue.
  - Sheets: strongest candidate (structured writes map well to approval), but without a concrete use case, this is premature optimization.
- Preserved principle: the action bus works best for discrete, approval-worthy, low-iteration operations (like send draft, create event).
- Updated `WIP.drive-docs-sheets.md` with closed status and revisit criteria per surface.
- Updated `memory/tasks.json` to close:
  - `task-20260311-1908-calendar-access` → done
  - `task-20260311-1908-email-access` → done
  - `task-20260311-1914-google-workspace-access` → done
- Google Workspace + n8n integration WIP (WIP.md) is now complete with evidence recorded in memory.
