@rubytech/create-maxy-code 0.1.300 → 0.1.301

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +10 -10
  3. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +84 -84
  4. package/payload/platform/plugins/admin/skills/public-agent-manager/SKILL.md +4 -4
  5. package/payload/platform/plugins/aeo/PLUGIN.md +1 -1
  6. package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +4 -4
  7. package/payload/platform/plugins/cloudflare/references/manual-setup.md +14 -8
  8. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +3 -0
  9. package/payload/platform/plugins/contacts/PLUGIN.md +1 -1
  10. package/payload/platform/plugins/docs/references/admin-identity-gate.md +6 -6
  11. package/payload/platform/plugins/docs/references/admin-session.md +15 -15
  12. package/payload/platform/plugins/docs/references/admin-ui.md +18 -18
  13. package/payload/platform/plugins/docs/references/aeo.md +1 -1
  14. package/payload/platform/plugins/docs/references/deployment.md +3 -3
  15. package/payload/platform/plugins/docs/references/internals.md +28 -28
  16. package/payload/platform/plugins/docs/references/platform.md +21 -21
  17. package/payload/platform/plugins/docs/references/plugins-guide.md +3 -3
  18. package/payload/platform/plugins/docs/references/troubleshooting.md +6 -6
  19. package/payload/platform/plugins/docs/references/visitor-graph.md +3 -3
  20. package/payload/platform/plugins/email/PLUGIN.md +2 -2
  21. package/payload/platform/plugins/email/references/email-reference.md +2 -2
  22. package/payload/platform/plugins/linkedin-import/skills/linkedin-import/SKILL.md +2 -2
  23. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  24. package/payload/platform/plugins/memory/PLUGIN.md +9 -9
  25. package/payload/platform/plugins/memory/references/graph-primitives.md +2 -2
  26. package/payload/platform/plugins/memory/references/schema-base.md +14 -14
  27. package/payload/platform/plugins/memory/references/transcript-formats/circleback.md +1 -1
  28. package/payload/platform/plugins/memory/references/transcript-formats/otter.md +1 -1
  29. package/payload/platform/plugins/memory/skills/conversation-archive/SKILL.md +6 -6
  30. package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +1 -1
  31. package/payload/platform/plugins/memory/skills/conversation-archive-mcp/SKILL.md +8 -8
  32. package/payload/platform/plugins/memory/skills/document-ingest/SKILL.md +5 -5
  33. package/payload/platform/plugins/obsidian-import/skills/obsidian-import/references/daily-notes.md +1 -1
  34. package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -1
  35. package/payload/platform/plugins/slides/PROVENANCE.md +1 -1
  36. package/payload/platform/plugins/substack-import/skills/substack-import/SKILL.md +1 -1
  37. package/payload/platform/plugins/telegram/PLUGIN.md +1 -1
  38. package/payload/platform/plugins/url-get/PLUGIN.md +2 -2
  39. package/payload/platform/plugins/whatsapp/.claude-plugin/plugin.json +1 -1
  40. package/payload/platform/plugins/whatsapp/PLUGIN.md +5 -5
  41. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +5 -5
  42. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +1 -1
  43. package/payload/platform/plugins/workflows/PLUGIN.md +1 -1
  44. package/payload/platform/plugins/x-import/PLUGIN.md +2 -2
  45. package/payload/platform/plugins/x-import/skills/x-import/SKILL.md +1 -1
  46. package/payload/platform/scripts/__tests__/check-no-task-id-leaks.test.sh +125 -0
  47. package/payload/platform/scripts/__tests__/resume-tunnel.test.sh +253 -0
  48. package/payload/platform/scripts/check-no-task-id-leaks.mjs +54 -6
  49. package/payload/platform/scripts/resume-tunnel.sh +89 -5
  50. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +14 -0
  51. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  52. package/payload/platform/services/whatsapp-channel/dist/notification.js +11 -0
  53. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  54. package/payload/platform/services/whatsapp-channel/dist/server.d.ts +4 -0
  55. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  56. package/payload/platform/services/whatsapp-channel/dist/server.js +54 -3
  57. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  58. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts +18 -0
  59. package/payload/platform/services/whatsapp-channel/dist/turn-follow.d.ts.map +1 -0
  60. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js +137 -0
  61. package/payload/platform/services/whatsapp-channel/dist/turn-follow.js.map +1 -0
  62. package/payload/platform/templates/specialists/agents/database-operator.md +1 -1
  63. package/payload/platform/templates/specialists/agents/librarian.md +2 -2
  64. package/payload/server/public/assets/{AdminShell-BFdBxg4P.js → AdminShell-BJrxifYa.js} +1 -1
  65. package/payload/server/public/assets/{Checkbox-BZ1XbA1t.js → Checkbox-C7bBRzLP.js} +1 -1
  66. package/payload/server/public/assets/{Transcript-CaVix6W0.js → Transcript-bGy_Gx99.js} +1 -1
  67. package/payload/server/public/assets/admin-jYKA7jR5.js +1 -0
  68. package/payload/server/public/assets/{audio-attachment-mime-CFyc3gpD.js → audio-attachment-mime-DL5R1M2A.js} +1 -1
  69. package/payload/server/public/assets/{browser-ClnGUKp-.js → browser-7fyPCFWB.js} +1 -1
  70. package/payload/server/public/assets/{chat-ZTAhSoJV.js → chat-UF9ATDwe.js} +1 -1
  71. package/payload/server/public/assets/{data-DDjeQQ7e.js → data-CIJnkfgX.js} +1 -1
  72. package/payload/server/public/assets/{file-download-DBMW0BNz.js → file-download-gth6Gpmn.js} +1 -1
  73. package/payload/server/public/assets/{graph-CR74qec3.js → graph-DHnB3BHK.js} +1 -1
  74. package/payload/server/public/assets/{graph-labels-1Uz0ZBRd.js → graph-labels-huk-fKcY.js} +1 -1
  75. package/payload/server/public/assets/jsx-runtime-DZddXDvJ.css +1 -0
  76. package/payload/server/public/assets/operator-D3QAzlPs.js +1 -0
  77. package/payload/server/public/assets/page-86edaOS1.js +1 -0
  78. package/payload/server/public/assets/{public-W-HholkU.js → public-B4LqL6lA.js} +1 -1
  79. package/payload/server/public/assets/{useSelectionMode-geYq13zs.js → useSelectionMode-lrH0qwAR.js} +1 -1
  80. package/payload/server/public/browser.html +6 -6
  81. package/payload/server/public/chat.html +8 -8
  82. package/payload/server/public/data.html +5 -5
  83. package/payload/server/public/graph.html +8 -8
  84. package/payload/server/public/index.html +9 -9
  85. package/payload/server/public/operator.html +9 -9
  86. package/payload/server/public/public.html +6 -6
  87. package/payload/server/server.js +51 -6
  88. package/payload/server/public/assets/admin-BK7xIr3o.js +0 -1
  89. package/payload/server/public/assets/jsx-runtime-BqGVUT1h.css +0 -1
  90. package/payload/server/public/assets/operator-QP_z97s8.js +0 -1
  91. package/payload/server/public/assets/page-CLijftr1.js +0 -1
  92. /package/payload/server/public/assets/{jsx-runtime-D3JkFIbi.js → jsx-runtime-CMu8uN0-.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.300",
3
+ "version": "0.1.301",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -104,9 +104,9 @@ Platform management tools for both the admin and public agents. The `plugin-read
104
104
 
105
105
  Tools are available via the `admin` MCP server.
106
106
 
107
- **Intent → skill resolution (Task 675).** `skill-search query="<free text>"` is the description-driven resolver: it walks every plugin's `skills/<slug>/SKILL.md`, reads frontmatter `name`+`description` only, and returns the top owners ranked by a frequency-aware match (distinctive terms dominate; common trigger words contribute little) with each owner's canonical `skills/<slug>/SKILL.md` path. It exists because the session's available-skills menu — the only other description-driven surface — is injected at session start and resume but **not** after a context compaction; once it leaves context, intent-phrased guesses miss `skill-find` (which is exact-slug only) and the agent falls back to blind `find`/`grep`. `skill-search` makes the mapping a single deterministic call regardless of whether the menu is present; feed the returned slug straight to `skill-load`. `skill-find` and `skill-load` stay exact-slug — they resolve a slug you already know — and are unchanged.
107
+ **Intent → skill resolution.** `skill-search query="<free text>"` is the description-driven resolver: it walks every plugin's `skills/<slug>/SKILL.md`, reads frontmatter `name`+`description` only, and returns the top owners ranked by a frequency-aware match (distinctive terms dominate; common trigger words contribute little) with each owner's canonical `skills/<slug>/SKILL.md` path. It exists because the session's available-skills menu — the only other description-driven surface — is injected at session start and resume but **not** after a context compaction; once it leaves context, intent-phrased guesses miss `skill-find` (which is exact-slug only) and the agent falls back to blind `find`/`grep`. `skill-search` makes the mapping a single deterministic call regardless of whether the menu is present; feed the returned slug straight to `skill-load`. `skill-find` and `skill-load` stay exact-slug — they resolve a slug you already know — and are unchanged.
108
108
 
109
- **Admin-seat authoring-skill gate (Task 516).** `skill-load` and `plugin-read` refuse to return the body of a content-producer-owned authoring skill — `professional-document`, `a4-print-documents`, `publish-site` — when the caller is the admin seat (`MAXY_SESSION_ROLE=admin` with no `MAXY_SPECIALIST`). The refusal relays one line — "This deliverable is owned by content-producer. Dispatch it with the Agent tool and subagent_type content-producer." — so the admin's next action is the dispatch, not inline authoring. `plugin-read` extracts the skill slug from a `skills/<slug>/…` file path (or a slug buried in `pluginName`), so the raw-SKILL.md read path can't bypass the gate; `PLUGIN.md` and plain `references/*` reads are untouched. content-producer's own spawns carry `MAXY_SPECIALIST=content-producer` and resolve these skills normally (it reaches them via the native `Skill` tool regardless), and the public seat is out of scope. The gate is a pure name+env check before any filesystem read and fails open for any non-admin or unidentified seat — it never bricks a legitimate load. Each block emits `[admin-skill-gate] role=admin skill=<name> decision=block reason=content-producer-owned`. This is the enforcement layer; the IDENTITY.md delegation paragraph documents the intent but is not the gate ([[feedback_deterministic_means_remove_llm]], [[feedback_doctrine_paragraph_is_not_a_gate]]). Skills the admin invokes directly are deliberately excluded: `unzip-attachment` (its SKILL.md says "Invoked by the admin agent directly — admin owns all unzipping", then routes the extracted tree to a specialist) and `deck-pages` (admin-owned, a content-producer refusal target). The set is each skill's own SKILL.md "Invoked by" declaration, not the admin plugin's skill catalogue (which lists these admin-hosted skills for discovery — the catalogue is the inline path this gate closes).
109
+ **Admin-seat authoring-skill gate.** `skill-load` and `plugin-read` refuse to return the body of a content-producer-owned authoring skill — `professional-document`, `a4-print-documents`, `publish-site` — when the caller is the admin seat (`MAXY_SESSION_ROLE=admin` with no `MAXY_SPECIALIST`). The refusal relays one line — "This deliverable is owned by content-producer. Dispatch it with the Agent tool and subagent_type content-producer." — so the admin's next action is the dispatch, not inline authoring. `plugin-read` extracts the skill slug from a `skills/<slug>/…` file path (or a slug buried in `pluginName`), so the raw-SKILL.md read path can't bypass the gate; `PLUGIN.md` and plain `references/*` reads are untouched. content-producer's own spawns carry `MAXY_SPECIALIST=content-producer` and resolve these skills normally (it reaches them via the native `Skill` tool regardless), and the public seat is out of scope. The gate is a pure name+env check before any filesystem read and fails open for any non-admin or unidentified seat — it never bricks a legitimate load. Each block emits `[admin-skill-gate] role=admin skill=<name> decision=block reason=content-producer-owned`. This is the enforcement layer; the IDENTITY.md delegation paragraph documents the intent but is not the gate ([[feedback_deterministic_means_remove_llm]], [[feedback_doctrine_paragraph_is_not_a_gate]]). Skills the admin invokes directly are deliberately excluded: `unzip-attachment` (its SKILL.md says "Invoked by the admin agent directly — admin owns all unzipping", then routes the extracted tree to a specialist) and `deck-pages` (admin-owned, a content-producer refusal target). The set is each skill's own SKILL.md "Invoked by" declaration, not the admin plugin's skill catalogue (which lists these admin-hosted skills for discovery — the catalogue is the inline path this gate closes).
110
110
 
111
111
  **Three-store admin auth invariant.** `admin-add` writes to all three identity stores (`users.json` PIN auth at the persistent `~/{configDir}/users.json` location , `account.json` `admins[]` role, Neo4j `:AdminUser`/`:Person` graph identity) with per-leg `[admin-auth-store]` log lines plus the `[admins-write]` chokepoint line, and returns `is_error: true` on any leg failure naming what's already written. `admin-update-pin` writes `users.json` only and emits the same `[admin-auth-store]` line. **Single chokepoint:** every `users.json` + `account.json admins[]` mutation routes through `platform/lib/admins-write` (`writeAdminEntry` for admin-add/set-pin, `removeAdminFromAccount` for admin-remove); the static check `grep -rnE 'admins\.push\|config\.admins\s*=' platform/ \| grep -v lib/admins-write` returns 0. Direct `Edit`/`Write` on `account.json` is forbidden by IDENTITY.md doctrine — mutations go through `account-update`, `plugin-toggle-enabled`, or the `admin-*` tools. **Install + boot invariants:** the installer and admin-server boot walk every account.json admins[] vs users.json; `[install-invariant]` / `[admin-invariant] direction=… userId=<id8> source=<file>` fires per divergence with a `check complete divergences=<n>` summary. Log-only — does not block. See `.docs/agents.md` § "Three-store admin auth invariant" for the full contract.
112
112
 
@@ -144,20 +144,20 @@ Tools are available via the `admin` MCP server.
144
144
 
145
145
  - `hooks/webfetch-preflight.mjs` — short-circuits WebFetch on JS-SPA shells with a structured `WEBFETCH_CANNOT_READ_JS_SPA` error so the agent surfaces a loud failure to the owner instead of paying the 60s extraction timeout. Fail-open on any internal error.
146
146
  - `hooks/askuserquestion-investigate-gate.sh` — PreToolUse matcher=`AskUserQuestion`. Blocks the question (exit 2) when no read-only investigation tool has fired since the latest real user turn in the session JSONL. The structural fix for the failure class where the agent fabricates a menu before evidence-gathering (session `c085ec2c-46fb-4b73-8865-68cf85866ea8` 2026-05-22 — "change remote access password" → invented options "Admin PIN / Cloudflare tunnel / WiFi password" with zero prior tool_use; post-correction the agent immediately fired `remote-auth-status` → `ToolSearch` → `remote-auth-set-password`, proving it knew the moves). **Allowlist** (exact, with trailing `__<tool>` suffix-match for namespaced `mcp__plugin_<plugin>_<server>__<tool>` aliases): `ToolSearch`, `Grep`, `Glob`, `Read`, `LS`, `NotebookRead`, `Bash`, `WebFetch`, `WebSearch`, plus the read-only admin / memory MCP tools (`*-status`, `*-list`, `*-read`, `skill-find`, `memory-find-candidates`, `profile-read`, `conversation-list`, `memory-list-attachments`, `memory-read-attachment`). **Block message:** `Blocked: AskUserQuestion requires at least one investigation tool (ToolSearch, Grep, Read, *-list, *-status, *-read, skill-find, ...) earlier in this turn. Search the operator's literal phrase first.` **Log line** (stderr, one per call): `[ask-gate] decision=<allow|block> sessionId=<id8> seen=<csv|-> reason=<allowlist-hit|no-investigation|fail-open-no-transcript|fail-open-parse-error>`. **Fail-open** on missing transcript or parse error — nudges, never bricks the UI.
147
- - `hooks/mcp-tool-missing.sh` — **PostToolUse hook on `mcp__.*` (Task 502, directive 3).** Defence-in-depth for the `No such tool available: mcp__…` failure class that the Task 502 name-binding is built to eliminate. Fires on any MCP tool call; no-op unless the `tool_response` carries `No such tool available` AND the qualified name resolves to a maxy plugin (read from the generated `hooks/lib/maxy-mcp-plugins.txt`). On a maxy match it logs one deterministic `[mcp-tool-missing] server=<server> tool=<tool>` line and exits 2 with a fixed envelope on stderr, so the agent relays a named server-unavailable failure instead of narrating "warming up" or blind-retrying. A missing non-maxy bridge tool (Playwright etc., upstream-owned) passes through (exit 0). The maxy-plugin list is regenerated and gate-diffed by `platform/scripts/check-canonical-tool-names.mjs`.
148
- - `hooks/post-tool-use-agent.sh` — **PostToolUse hook on `Agent` (Task 560).** Drains any subagent hook-decision buffers under `~/.maxy-code/logs/hook-decisions/` modified since this parent's previous PostToolUse-Agent fire (cursor file keyed by parent session id), prints one `[hook-propagate]` line per record to stdout — Claude Code attaches the stdout as a `hook_success` attachment on the parent JSONL, making the records grep-queryable from the parent session alone (Task 559 motivating case). Rotates consumed buffers to `consumed/`. Emits one `[hook-propagate-census] parentSession=<…> subagentHooksObserved=<N> attachmentsEmitted=<M>` line per fire to stdout and server.log; `N != M` is the propagation regression signal. The companion emitter library `hooks/lib/hook-emit.sh` is sourced by `post-tool-use-agent.sh` and any other hook that records a block decision (4 KB stderr truncation, `truncated=true` set on the record).
149
- - `hooks/admin-authoring-observer.sh` — **PostToolUse hook on Write and Edit (Task 486).** Observation only — never blocks; exits 0 on every path. Fires when the admin agent (not a specialist subagent — gated by `MAXY_SPECIALIST` env) writes or edits a file under `<accountDir>/output/`. Walks the session transcript from the latest real-user turn forward to detect any prior `Task` `tool_use` whose `subagent_type` starts with `specialists:`. Emits one stderr line `[admin-authoring] inline-write path=<rel> priorSpecialistSpawnInTurn=<true|false|unknown>`. A `false` value on a long-form prose file is the regression signal Task 486 was designed to make visible — the BioSymm proposal session (admin authored a customer-facing proposal inline despite content-producer being installed) is the failure mode this surfaces mechanically. Mechanical enforcement (refuse the write, force a re-spawn) is deferred per the task spec.
150
- - `hooks/prompt-optimiser-directive.sh` — **UserPromptSubmit hook (Task 698).** Injects the standing prompt-optimiser restatement directive plus the per-turn three-tier routing ladder as `additionalContext`: **(1)** delegate to the specialist that owns the deliverable via the Agent tool — the brief states the outcome plus binding constraints, never lines/anchors/literal text; **(2)** only if none fits, load an admin-usable skill with `skill-load`; **(3)** only if neither fits, author inline — freestyle is the named last resort. Re-emits the full agent roster (`agents/admin/AGENTS.md`) and the full admin-usable skills list (`agents/admin/ADMIN-SKILLS.md`) every turn by reading the two generated files from the account dir (the hook fires with the account dir as cwd); it never walks the plugins tree per turn. Fail-open is **visible**: a missing list logs `[prompt-optimiser] missing=<AGENTS.md\|ADMIN-SKILLS.md> emitting-partial` to stderr and the ladder still injects. The trivial-turn skip (one-word confirmation, slash-command, direct continuation) is unchanged. **Staleness:** `ADMIN-SKILLS.md` is regenerated only by `setup-account.sh`; a plugin add/remove since the last setup leaves the list stale — compare `ADMIN-SKILLS.md` mtime against the newest `SKILL.md` mtime and re-run setup to refresh. The list generator is `platform/scripts/lib/admin-skills-bootstrap.sh`; it logs `[admin-skills] scanned=<N> admin-usable=<M> no-declaration=<K>` (failure signature: `admin-usable=0` while `scanned>0`, or any `missing-declaration` line). **Task 719:** the directive now carries a standing CAPABILITY-QUESTIONS-ARE-OWNED-WORK clause (how-to / "do you have instructions for X" / config questions about platform features are answered from the owning specialist or plugin tool/reference, never from training memory), and the hook appends a durable `<ts> [prompt-optimiser-directive] injected len=<n> session=<id>` breadcrumb to `$LOG_DIR/prompt-optimiser-directive.log` so per-turn injection is greppable, not stderr-only. **Task 753:** the directive is **suppressed on native channel turns** — when the parsed `.prompt` starts with the `<channel source=` event marker, the hook logs `[prompt-optimiser-directive] skipped reason=channel-turn session=<id>` to stderr and exits without injecting, because the channel service already reframes the inbound into a select-and-dispatch turn (`composeAdminContent`, see `.docs/whatsapp-inbound-lifeline.md`). Marker-matched at start-of-prompt only, so an admin/Terminal prompt that merely mentions "channel" still gets the directive; fail-open injects if the prompt cannot be parsed.
151
- - `hooks/prompt-optimiser-compliance.sh` — **Stop hook (Task 719).** After each admin turn, reads the just-finished turn from `transcript_path` and appends `<ts> [prompt-optimiser-compliance] directive-fired no-route-taken session=<id8> prompt="<clip>"` to `$LOG_DIR/prompt-optimiser-directive.log` (and stderr) when the routing directive fired, the prompt was non-trivial (not a slash-command, not a one-word confirmation), and the turn took **no route** — no `Agent` dispatch, no `Skill` load, no `ToolSearch`, no `mcp__*` tool call. This is the standing compliance signal that surfaces the session-`da0b12d4` failure class (agent answers a capability question from memory) as a visible event instead of a silent stale answer. Directive-fired is detected by the marker `PROMPT-OPTIMISER DIRECTIVE` in the turn slice, so it is robust to the CC-version difference in how `UserPromptSubmit` `additionalContext` is recorded (`attachment`/`hook_success` vs `hook_additional_context`). **Known limitation:** "direct continuation of the prior turn" is not detectable from the transcript, so a continuation turn that legitimately needs no route can be flagged; treat the log as a review signal, not a gate. **Fail-open:** no python3, no `transcript_path`, or an unreadable transcript → exit 0, no output. Lives in the same log as the directive breadcrumb, so a single `grep` interleaves "fired" and "no-route" into one per-session timeline; cross-check via `platform/scripts/logs-read.sh <sessionKey>`. This is a lightweight transcript read, not a per-turn spawn (contrast the Task-626 turn recorder below).
152
- - **Turn recorder — removed entirely (Task 626).** The `turn-completed-graph-write.sh` Stop hook, the `/api/admin/claude-sessions` loopback bypass it relied on, the `[turn-recorder]` emitters, the envelope walker, and the recorder-auto-archive subscriber are deleted. It had been dormant since Task 214 (never re-registered in settings.json); the admin now writes to the graph by delegating to `database-operator` via the Task tool inside the live session, and the on-demand `/insight` pass (`skills/insight/SKILL.md`, a registered admin skill — Task 641) is the per-session review. There is no per-turn spawn.
147
+ - `hooks/mcp-tool-missing.sh` — **PostToolUse hook on `mcp__.*` (directive 3).** Defence-in-depth for the `No such tool available: mcp__…` failure class that the name-binding is built to eliminate. Fires on any MCP tool call; no-op unless the `tool_response` carries `No such tool available` AND the qualified name resolves to a maxy plugin (read from the generated `hooks/lib/maxy-mcp-plugins.txt`). On a maxy match it logs one deterministic `[mcp-tool-missing] server=<server> tool=<tool>` line and exits 2 with a fixed envelope on stderr, so the agent relays a named server-unavailable failure instead of narrating "warming up" or blind-retrying. A missing non-maxy bridge tool (Playwright etc., upstream-owned) passes through (exit 0). The maxy-plugin list is regenerated and gate-diffed by `platform/scripts/check-canonical-tool-names.mjs`.
148
+ - `hooks/post-tool-use-agent.sh` — **PostToolUse hook on `Agent`.** Drains any subagent hook-decision buffers under `~/.maxy-code/logs/hook-decisions/` modified since this parent's previous PostToolUse-Agent fire (cursor file keyed by parent session id), prints one `[hook-propagate]` line per record to stdout — Claude Code attaches the stdout as a `hook_success` attachment on the parent JSONL, making the records grep-queryable from the parent session alone. Rotates consumed buffers to `consumed/`. Emits one `[hook-propagate-census] parentSession=<…> subagentHooksObserved=<N> attachmentsEmitted=<M>` line per fire to stdout and server.log; `N != M` is the propagation regression signal. The companion emitter library `hooks/lib/hook-emit.sh` is sourced by `post-tool-use-agent.sh` and any other hook that records a block decision (4 KB stderr truncation, `truncated=true` set on the record).
149
+ - `hooks/admin-authoring-observer.sh` — **PostToolUse hook on Write and Edit.** Observation only — never blocks; exits 0 on every path. Fires when the admin agent (not a specialist subagent — gated by `MAXY_SPECIALIST` env) writes or edits a file under `<accountDir>/output/`. Walks the session transcript from the latest real-user turn forward to detect any prior `Task` `tool_use` whose `subagent_type` starts with `specialists:`. Emits one stderr line `[admin-authoring] inline-write path=<rel> priorSpecialistSpawnInTurn=<true|false|unknown>`. A `false` value on a long-form prose file is the regression signal this hook was designed to make visible — the BioSymm proposal session (admin authored a customer-facing proposal inline despite content-producer being installed) is the failure mode this surfaces mechanically. Mechanical enforcement (refuse the write, force a re-spawn) is deferred per the task spec.
150
+ - `hooks/prompt-optimiser-directive.sh` — **UserPromptSubmit hook.** Injects the standing prompt-optimiser restatement directive plus the per-turn three-tier routing ladder as `additionalContext`: **(1)** delegate to the specialist that owns the deliverable via the Agent tool — the brief states the outcome plus binding constraints, never lines/anchors/literal text; **(2)** only if none fits, load an admin-usable skill with `skill-load`; **(3)** only if neither fits, author inline — freestyle is the named last resort. Re-emits the full agent roster (`agents/admin/AGENTS.md`) and the full admin-usable skills list (`agents/admin/ADMIN-SKILLS.md`) every turn by reading the two generated files from the account dir (the hook fires with the account dir as cwd); it never walks the plugins tree per turn. Fail-open is **visible**: a missing list logs `[prompt-optimiser] missing=<AGENTS.md\|ADMIN-SKILLS.md> emitting-partial` to stderr and the ladder still injects. The trivial-turn skip (one-word confirmation, slash-command, direct continuation) is unchanged. **Staleness:** `ADMIN-SKILLS.md` is regenerated only by `setup-account.sh`; a plugin add/remove since the last setup leaves the list stale — compare `ADMIN-SKILLS.md` mtime against the newest `SKILL.md` mtime and re-run setup to refresh. The list generator is `platform/scripts/lib/admin-skills-bootstrap.sh`; it logs `[admin-skills] scanned=<N> admin-usable=<M> no-declaration=<K>` (failure signature: `admin-usable=0` while `scanned>0`, or any `missing-declaration` line). The directive also carries a standing CAPABILITY-QUESTIONS-ARE-OWNED-WORK clause (how-to / "do you have instructions for X" / config questions about platform features are answered from the owning specialist or plugin tool/reference, never from training memory), and the hook appends a durable `<ts> [prompt-optimiser-directive] injected len=<n> session=<id>` breadcrumb to `$LOG_DIR/prompt-optimiser-directive.log` so per-turn injection is greppable, not stderr-only. The directive is also **suppressed on native channel turns** — when the parsed `.prompt` starts with the `<channel source=` event marker, the hook logs `[prompt-optimiser-directive] skipped reason=channel-turn session=<id>` to stderr and exits without injecting, because the channel service already reframes the inbound into a select-and-dispatch turn (`composeAdminContent`, see `.docs/whatsapp-inbound-lifeline.md`). Marker-matched at start-of-prompt only, so an admin/Terminal prompt that merely mentions "channel" still gets the directive; fail-open injects if the prompt cannot be parsed.
151
+ - `hooks/prompt-optimiser-compliance.sh` — **Stop hook.** After each admin turn, reads the just-finished turn from `transcript_path` and appends `<ts> [prompt-optimiser-compliance] directive-fired no-route-taken session=<id8> prompt="<clip>"` to `$LOG_DIR/prompt-optimiser-directive.log` (and stderr) when the routing directive fired, the prompt was non-trivial (not a slash-command, not a one-word confirmation), and the turn took **no route** — no `Agent` dispatch, no `Skill` load, no `ToolSearch`, no `mcp__*` tool call. This is the standing compliance signal that surfaces the session-`da0b12d4` failure class (agent answers a capability question from memory) as a visible event instead of a silent stale answer. Directive-fired is detected by the marker `PROMPT-OPTIMISER DIRECTIVE` in the turn slice, so it is robust to the CC-version difference in how `UserPromptSubmit` `additionalContext` is recorded (`attachment`/`hook_success` vs `hook_additional_context`). **Known limitation:** "direct continuation of the prior turn" is not detectable from the transcript, so a continuation turn that legitimately needs no route can be flagged; treat the log as a review signal, not a gate. **Fail-open:** no python3, no `transcript_path`, or an unreadable transcript → exit 0, no output. Lives in the same log as the directive breadcrumb, so a single `grep` interleaves "fired" and "no-route" into one per-session timeline; cross-check via `platform/scripts/logs-read.sh <sessionKey>`. This is a lightweight transcript read, not a per-turn spawn (contrast the turn recorder below).
152
+ - **Turn recorder — removed entirely.** The `turn-completed-graph-write.sh` Stop hook, the `/api/admin/claude-sessions` loopback bypass it relied on, the `[turn-recorder]` emitters, the envelope walker, and the recorder-auto-archive subscriber are deleted. It had been dormant for a long time (never re-registered in settings.json); the admin now writes to the graph by delegating to `database-operator` via the Task tool inside the live session, and the on-demand `/insight` pass (`skills/insight/SKILL.md`, a registered admin skill) is the per-session review. There is no per-turn spawn.
153
153
 
154
- ## Session identifiers (Task 135)
154
+ ## Session identifiers
155
155
 
156
156
  The metadata pane and admin logs use these three identifier names, matching what Claude Code surfaces:
157
157
 
158
158
  | Operator label | What it is | Manager wire field | Log key |
159
159
  |---|---|---|---|
160
- | `sessionId` | Claude's session — one identity with two phases the manager resolver maps to the same row: bridge suffix (`session_xxx` from `claude.ai/code/<session_xxx>`) and JSONL basename UUID on disk. The manager emits whichever phase is current (bridge preferred). | `sessionId` (collapsed from the earlier three-field surface (see Task 141)) | `sessionId=` |
160
+ | `sessionId` | Claude's session — one identity with two phases the manager resolver maps to the same row: bridge suffix (`session_xxx` from `claude.ai/code/<session_xxx>`) and JSONL basename UUID on disk. The manager emits whichever phase is current (bridge preferred). | `sessionId` (collapsed from the earlier three-field surface) | `sessionId=` |
161
161
  | `accountId` | Account / workspace UUID. | `senderId` (channel-agnostic — same wire field carries phone/email/chat-id on non-admin channels, so its log key stays `senderId=` outside admin flow) | `accountId=` (admin flow) |
162
162
 
163
163
  Full contract lives in [admin-session.md § Session identifiers](../docs/references/admin-session.md).