aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-07-13-runtime-portability-inventories.org
blob: b9e8244fe6822957e64bcca6423930a42c97fdab (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#+TITLE: Runtime Portability — Hook, MCP, and Memory Inventories
#+AUTHOR: Craig Jennings
#+DATE: 2026-07-13

Decision-prep for the generic-agent-runtime arc (spec: [[file:2026-05-28-generic-agent-runtime-spec.org]]). Three of the eight gap child tasks are inventories; this doc is their deliverable. Each section ends with a verdict and any decision left for Craig.

* Hook parity inventory

What's wired today (global =.claude/settings.json=, symlinked to =~/.claude/settings.json=):

- PreToolUse AskUserQuestion hard-deny — blocks the popup-choice tool, forcing inline numbered options per =interaction.md=. Exists because prose alone failed.
- PreCompact =precompact-priorities.sh= — saves priorities before context compaction.
- SessionStart =session-title.sh= (cosmetic title) and =session-clear-resume.sh= (resume-after-/clear plumbing).
- Stop =ai-wrap-teardown.sh= — sentinel-gated session teardown.

Shipped but unwired (reference copies in =hooks/= + =settings-snippet.json=): =git-commit-confirm.py=, =gh-pr-create-confirm.py=, =destructive-bash-confirm.py=. Not active under Claude either, so they impose no parity requirement today.

Language bundles wire PostToolUse validators per edit (=validate-bash.sh=, =validate-el.sh=, =validate-go.sh=, plus python/typescript equivalents) — and every bundle also ships the same validation as a =githooks/pre-commit=.

Per-hook portability verdicts:

- AskUserQuestion deny: moot off Claude. The popup tool is Claude Code's; Codex-style and local harnesses have no equivalent tool to deny. No port needed.
- PostToolUse validators: covered off Claude. The bundles' git pre-commit hooks run the same validators, so enforcement survives at commit time instead of edit time. Acceptable downgrade, no work needed.
- SessionStart clear-resume: not portable as-is; /clear semantics are Claude's. This is the same surface as the "session plumbing per runtime" child task — resolve it there, not as a hook port.
- session-title: cosmetic, skip on other runtimes.
- PreCompact priority-save: genuine gap. Codex-style harnesses compact without a hook point. Downgrade is prose (the session-context write triggers in protocols.org already say "save before compaction"), which is weaker but is also the pre-2026 Claude behavior. Accept prose, or shorten sessions on those runtimes.
- Stop wrap-teardown: partial gap. Codex has a turn-completion notify command that could run the same sentinel-gated script; local harnesses vary. Port the script trigger where a hook point exists; where none does, teardown becomes a manual step in the wrap phrase.

Verdict: only PreCompact and Stop carry real porting work, and both have acceptable degraded modes. Nothing here blocks a pilot.

* MCP portability check

Locally-configured servers (user scope, =~/.claude.json=): linear, notion, figma, slack-deepsat, google-calendar, google-docs-personal, google-docs-work, drawio, google-keep. No project-scope servers anywhere. All are ordinary MCP server definitions, portable to any MCP-speaking harness (Codex CLI reads them from its config; most local harnesses that matter speak MCP now). Port = translate the server blocks + carry the auth material. Mechanical.

claude.ai-managed connectors (session-injected, not in local config): Gmail, and the claude.ai variants of Calendar/Drive. These do not travel. Overlap analysis: calendar and docs are already covered by the locally-configured servers; Gmail's workflow-preferred path is already =cmail-action= (local CLI), so the managed Gmail connector is convenience, not dependency.

Named gap: signal-mcp. protocols.org "Paging Craig" names it as the only supported away-from-desk page path, and it is not in any local config — it lives claude.ai-side. Off Claude there is currently no page channel at all. This lands squarely on the open Signal-pager task ([#C], todo.org): the signal-cli runbook that task produces would be the runtime-neutral paging path. Recommend noting runtime-portability as an added motivation on that task.

Verdict: portable except paging; paging's fix is already a filed task.

* Memory story for non-Claude agents

Claude auto-memory (=~/.claude/projects/<enc>/memory/=, MEMORY.md index) is harness-owned: written by Claude's memory tooling, loaded by its session start. A non-Claude agent should neither read nor write it.

The designed cross-agent store already exists: the org-roam KB (=knowledge-base.md= — reading is plain rg over files, writing is one node per fact, both runtime-neutral). Project state lives in file artifacts every agent reads anyway: todo.org, notes.org, session anchors, docs/.

One wording gap: knowledge-base.md's "Capture, then promote" section names harness memory as the capture layer. For a non-Claude agent the capture layer is the session log itself. Recommended one-sentence addition there: an agent without harness memory captures into its session log and promotes from it at wrap-up. Shared-asset edit, needs approval.

Verdict: no build. One approved sentence in knowledge-base.md closes it.

* Skill and command parity

What rulesets ships: 11 skills (SKILL.md, model-invokable — add-tests, debug, five-whys, flush, frontend-design, pairwise-tests, playwright-js, playwright-py, review-code, root-cause-trace, voice) and 18 commands (plain .md prompts under =.claude/commands/=, user-invoked). Every body is markdown instructions; nothing executable lives in the registration layer.

The load-bearing observation: a =/name= reference is just a pointer to a file on disk. Any harness that reads files can execute all 29 artifacts through one resolution rule in its bootstrap entry file: "a skill or command reference (=/voice=, =/review-code=, =/brainstorm=) resolves to =~/.claude/skills/<name>/SKILL.md= or =~/.claude/commands/<name>.md= — read the file and follow it." That single sentence, emitted by the instruction-bootstrap install target, makes the whole library portable with zero per-skill work. commits.md's existing "/voice unavailable — walk the patterns inline" fallback generalizes the same way.

What that rule does not carry:

- Auto-invocation. Claude Code triggers skills from their descriptions; other harnesses won't. The high-value auto-triggers (voice and review-code inside the publish flow) don't actually need it — commits.md invokes them by name at fixed flow points, and the resolution rule covers a by-name invocation. The convenience triggers (debug, frontend-design firing on topic match) degrade to on-request. Acceptable.
- Native slash registration. Codex-style harnesses have their own custom-prompt mechanism; the 18 commands could also be registered there for ergonomics (an optional install nicety, not a requirement — the resolution rule already works).
- flush. Its mechanics are Claude Code's (/clear, the self-inject resume hook). Explicitly not ported; the session-plumbing child owns that surface.
- Harness built-ins (code-review ultra, plan mode, artifacts). Not rulesets' to port; flows that name them need per-runtime alternatives or graceful absence, same as today when they're unavailable.

Verdict: no per-skill porting matrix needed. One resolution sentence in the bootstrap entry file (instruction-bootstrap child), optional native registration as an install nicety, flush explicitly excluded.

* Session plumbing per runtime

Four pieces, examined:

- =session-context-path= is already runtime-aware by design — the =AI_AGENT_ID= shape carries a runtime segment (=host.project.runtime.epoch=) and the resolver is pure bash. Nothing to do.
- The anchor cycle (suspend entry, startup's interrupted-session recovery, wrap-up's archive rename) is plain files driven by workflow prose. Any agent that reads protocols.org executes it identically. Nothing to do.
- =self-inject.sh= is harness-agnostic: it types arbitrary strings into a tmux pane. The Claude-bound part is only the *payload* — "/clear" plus a resume line that leans on the SessionStart hook. A Codex auto-flush is a payload variant: the clear command differs (confirm the exact one when wiring) and the second injected line must itself carry the resume instruction ("read .ai/session-context.org and resume") since no hook fires. Arguably simpler than the Claude path.
- =session-clear-resume.sh= (the SessionStart hook) is Claude-only, and the payload variant above makes it unnecessary off Claude.

Verdict: no build. The flush *skill* stays Claude-official; a codex auto-flush is a documented payload variant to wire the day a codex session actually wants it, not before.

* Decisions for Craig

All four approved by Craig, 2026-07-13:

1. PreCompact downgrade off Claude: prose-only "save before compaction" accepted.
2. Stop-teardown off Claude: port via Codex notify where available, manual teardown elsewhere.
3. Runtime-portability motivation note added to the Signal-pager task.
4. knowledge-base.md capture-layer sentence added (non-Claude runtimes capture into the session log and promote from there).