Three follow-up NITs from the post-fold-in review:
(1) Disambiguate M.post return shape: (body, status_code) on transport
success regardless of status; (nil, errmsg) on libcurl failure
stays unchanged. Phase 1 callers reading only the first slot are
unaffected.
(2) Note that the M.post extension requires extending ffi.cdef to
include curl_easy_getinfo + CURLINFO_RESPONSE_CODE (decimal
2097154, CURLINFOTYPE_LONG | 2) and a long[1] out-param shim.
Implementation detail the commit #1 author will need.
(3) Move the tool-result content-flattening rule from §12 risk note
into §4 normative spec (forward-referenced both ways) — §4 is
where a future reader looking for the tool-invocation contract
will scan.
No design changes; clarifications only.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Independent review of the formulate+analyze+plan draft surfaced design
gaps that would have shipped as silent bugs. Resolutions applied:
BLOCKERs:
B1 context.lua impact widened — Phase 1 :append asserts content and
discards extra fields. Need (a) shape-per-role assert, (b) preserve
tool_calls/tool_call_id on store, (c) emit from to_messages().
B2 ffi/curl.M.post extended to return (body, status_code). lmcp's
401 returns a non-JSON-RPC body that would have been mis-decoded.
B3 §3 typo schema -> inputSchema.
B4 pending_exec_output × tool-call sub-loop interaction specified.
B5 §3/§12 broker dependency contradiction — broker takes opts.tools
from caller; no layering inversion.
CONCERNs:
C1 M.chat return polymorphism dropped (no consumer).
C2 tool_calls[].index absent fallback: default to 0.
C3 Re-injection stores accumulated text, not hard-coded empty.
C4 :mcp connect failure: no auto-retry, status-log once.
C5/C7 JSON-RPC error AND argument-parse failure both synthesize a
role:"tool" turn — keeps strict-template alternation legal
exactly the way PHASE0 §6 demanded for exec output.
C6 §9 confirms §4 amendment is additive (preserves §3 invariant).
NITs:
N3 protocolVersion fallback (lmcp doesn't negotiate).
N4 Alternation assert in Context:append.
N7 Model-routing bug filed as aish#23.
N8 Day-one fallback test for use_tool_role=false in commit #3.
Manifest status: Plan (review folded). Status line and Resolutions
sections updated; commit-by-commit roadmap reflects revised specs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bottom-up: mcp.lua → safety.lua → context.lua → renderer.lua → broker.lua
→ repl.lua → config.lua. Same cadence as Phase 0/1.
Risks called out explicitly:
- Empty tools array → omit field entirely (some servers reject [])
- isError:false on actual failure (baseline §3 finding) → pass content
through regardless; let model read error text
- JSON-RPC error from tools/call → aish status only, no tool turn
appended, no model recovery
- max_tool_depth=8 cap on tool-call sub-loop
- Argument JSON streaming may yield malformed JSON → status warn + skip
- Q18 fallback (use_tool_role=true default; prefix-injection plumbed
but dead-coded; verify can flip)
- Connect-at-startup is sequential (~30ms × N); fine for N≤3
Two items left open for review: Q18 default flip vs ship-true-flip-on-fail,
and whether :mcp connect should re-fetch tools after the initial cache.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live-probed against lmcp v0.5.4 (boltzmann) + hossenfelder broker proxy:
Transport simpler than spec:
- lmcp only implements POST-per-RPC with Connection: close; no held-open
SSE channel. Combined with capabilities.tools.listChanged=false, no
client-side listener is needed in v1. Drops the planned M.get_sse
addition to ffi/curl.lua — Phase 1's M.post covers MCP.
Bearer auth is universal across the fleet — config schema grew
auth_token (literal) and auth_env (env-var indirection) fields per
server, mirroring PHASE0 §10's key_env convention.
Streaming tool_calls delta shape verified — accumulator by `index`,
function.arguments arrives as chunked JSON-string. Matches the
formulate-phase assumption in §5.
Resolutions:
Q17 transport abstraction — POST-only, no SSE channel for lmcp.
Q21 error mapping — result.isError (model-recoverable, feed
back as tool turn) vs JSON-RPC error
(unknown method/tool, transport-level).
Q18 role:"tool" turn — accepted at protocol level (live-probed).
Mistral-nemo template verification
blocked by the hossenfelder model-field
routing bug; full closure carried to
Phase 7 verify.
Open-end recorded in §11: the hossenfelder proxy routes every request
to the loaded fast model regardless of model field, blocking Phase 2
testing against mistral-nemo specifically. Parallel to the SSE
buffering issue at marfrit/aish#15; same root (boltzmann proxy code).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>