refactor(pair2-mcp): polish tail — 4 follow-on beads (rf2-b05ld)#1027
Merged
Conversation
added 3 commits
May 14, 2026 15:15
…rivials (rf2-b05ld) Cluster of P2/P3 polish surfaced by the rf2-7hie3 and rf2-b05ld audits. Each is small enough that none justifies a standalone bead; batched here so a future contributor reads one diff instead of twelve. - subscribe: lift `merge-drain` + new `drain-produced-output?` predicate to public; single-source the "what counts as a tick" contract across the merge-state path AND the emit gate (was an inline OR at two sites). Adds 11 unit tests pinning the state- merge contract (rf2-c0h8p). - nrepl: `attach-handlers!` data-callback folded buf-concat + frame- split into one atomic `swap!` — prior two-swap shape left a window where a second `data` event could double-decode the same frame (NR1 / N2). `read-edn-safe` logs to stderr on catch so silent EDN parse failures surface in the dev console (NR2 / N4). - precheck: replace `:rf.mcp.cache/operating-frame` sentinel keyword with a tagged 2-tuple — `[:explicit <kw>]` / `[:operating-frame nil]`. `precheck-form` now dispatches on the tag, not a magic value. Renamed `precheck-frame` → `precheck- target` (CACHE1). - wire: cache `default-build-id` env-var read in a delay (was 11+ reads per tool dispatch ⇒ 1 at first call). New `arg-keyword` helper folds the `(some-> (wire/arg args :k) keyword)` pattern recurring across five tool bodies (T5 / T18). - args: `parse-modes-arg`'s JS-object discriminator collapses to `object?` (ARGS1, -4 LoC). `parse-frames-arg` typo-swallow behaviour documented (ARGS3). - tail-build: lift the 300ms soft-delay + 100ms poll cadence to named `def`s (T14). Quality gates: pair2-mcp test suite green (325/325, +11 merge- drain tests). implementation cljs gate green (1818/1818). Refs: rf2-c0h8p, rf2-lhue6, rf2-n5j96.
…mily, descriptor polish (rf2-agcq2, rf2-kaof4) Two related spec gaps surfaced by the rf2-b05ld / rf2-7hie3 audits; batched together because both touch the wire-vocabulary surface. - spec/Tool-Pair.md: add a §MCP-side wire-marker vocabulary enumeration table — every `:rf.mcp/*` / `:rf.size/*` marker the MCP-server layer emits (cache-hit / overflow / dedup-table / diff-from / summary / large-elided / cursor-stale + the framework-side :rf/redacted sentinel) listed with mechanism, emit site, and top-level keys. Prior wording named two markers in passing without enumerating the family. - tools/pair2-mcp/spec/003-Tool-Catalogue.md: add §Universal: `:typicalTokens` on every tool descriptor — the rf2-6sddv slot was emitted on every descriptor in `descriptors_data.cljs` but not enumerated in the catalogue spec. - tools/pair2-mcp/src/.../descriptors_data.cljs: drop the "legacy" framing on `epochs-mode "full"`. The mode is the time-travel-restore mode — describing it as "legacy" mislabels current contract. Wire shape unchanged. - tools/pair2-mcp/src/.../sensitive.cljs: trim the defensive ":epochs may inherit in future" parenthetical on `scrub-snapshot-sensitive` — epoch-record stamping is current contract per rf2-isdwf, not a future possibility. - tools/pair2-mcp/src/.../cap.cljs: document the intentional `:isError` pass-through asymmetry vs `cache/apply-cache`. `apply-cache` short-circuits on `:isError` (transient failures must not poison the cache); `apply-cap` measures and (if over budget) wraps an error like any other payload (oversize bytes are oversize bytes regardless of success / failure). Quality gates: pair2-mcp tests green (325/325). Refs: rf2-agcq2, rf2-kaof4.
… max-tokens pairing + catalogue-entry contract (rf2-uebll) Causa-MCP's DESIGN-RATIONALE.md Lock #9 §Why promised a follow-up alignment bead to lift wire-pipeline expansions back into pair2-mcp Principles wording where the two servers converge. Cross-server posture: agents "learn the slot on one server, get the same slot on the others", so pair2-mcp's spec wording matches causa-mcp's where the two converge. Two normative additions to §Tight token budget per response §The wire-boundary cap: - **`max-tokens` / `:max-tokens` notation pin** — the arg name is fixed cross-server. `max-tokens` is the JSON-RPC `arguments` key (string, kebab-case, the wire shape an agent host sends); `:max-tokens` is the parsed CLJS keyword inside the runtime. Pair2-mcp, story-mcp, and causa-mcp all parse the same wire slot to the same in-process key. - **Catalogue-entry contract (normative)** — every tool entry in 003-Tool-Catalogue.md MUST declare: (1) which of the eight mechanisms apply, (2) the `:typicalTokens` hint, (3) the cap-reached behaviour, (4) default mode / limit / dedup / elision values. Aligns with causa-mcp's catalogue-entry contract so agent per-tool budget projections work uniformly across both servers. Path slicing (mechanism 2), structural dedup (mechanism 5), :rf.mcp/overflow and :rf.mcp/summary shapes are already documented at pair2-mcp Principles §Path slicing / §Structural dedup / §The wire-boundary cap — no change needed. Quality gates: pair2-mcp tests green (325/325). Refs: rf2-uebll. Resolves causa-mcp DESIGN-RATIONALE Lock #9 §Why follow-up commitment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Four follow-on beads landed atop the rf2-pair2-batch (#1016) polish cluster — all
on the
tools/pair2-mcp/surface.rf2-c0h8p — merge-drain + drain-produced-output? unit tests (subscribe
streaming loop). 11 tests pinning the state-merge contract; lifted both to
public so the test ns can pin directly.
rf2-lhue6 / rf2-n5j96 — cosmetic polish / trivials cluster:
merge-drainexposed as public;drain-produced-output?predicate single-sources the tick-gate contract across the merge-state path and the emit gate.
nrepl/attach-handlers!data callback folds buf-concat + frame-split intoone atomic
swap!(NR1) — closed a race window where a seconddataeventcould double-decode the same frame.
read-edn-safelogs to stderr on catch (NR2) — silent parse failures nowsurface in the dev console.
precheck/precheck-frame→precheck/precheck-targetreturning a tagged2-tuple (
[:explicit <kw>]/[:operating-frame nil]); sentinel keyword(
:rf.mcp.cache/operating-frame) gone.wire/default-build-idcached in a delay at namespace load — was 11+env-var reads per tool dispatch (T5).
wire/arg-keywordhelper folds the(some-> (wire/arg ...) keyword)pattern across five tool bodies (T18).
args/parse-modes-argJS-object discriminator collapses toobject?(ARGS1).args/parse-frames-argtypo-swallow behaviour documented (ARGS3).tail-build300ms soft-delay + 100ms poll cadence lifted to nameddefs.rf2-agcq2 / rf2-kaof4 — spec amendments:
spec/Tool-Pair.mdadds a §MCP-side wire-marker vocabulary enumerationtable — every
:rf.mcp/*/:rf.size/*marker the MCP-server layer emitslisted with mechanism, emit site, and top-level keys.
tools/pair2-mcp/spec/003-Tool-Catalogue.mdadds §Universal:typicalTokenson every tool descriptor.descriptors_data.cljs: drop "legacy" framing onepochs-mode \"full\"(it's the time-travel-restore mode, not legacy).
sensitive.cljs: trim the defensive ":epochs may inherit in future"parenthetical (rf2-isdwf made it current contract).
cap.cljs: document the intentional:isErrorpass-through asymmetryvs
cache/apply-cache.rf2-uebll — lift causa-mcp wire-pipeline expansions back into pair2-mcp
Principles per causa-mcp's DESIGN-RATIONALE.md Lock test: CLJS coverage for hierarchical/always/after/invoke machines (rf2-pc82) #9 §Why follow-up:
max-tokens/:max-tokensnotation pin (the JSON-RPC arg name + parsedCLJS keyword pairing is fixed cross-server).
which mechanisms apply, the
:typicalTokenshint, the cap-reachedbehaviour, and the default knob values.
Deferred (not in this PR)
[v count]from server. Medium-riskwire-shape change requiring careful design across snapshot + get-path eval
forms; deserves its own focused bead session.
the benchmark harness; data-driven by design.
frame.cljc; disjoint surface from pair2-mcp polish, separate dispatch.causa-mcp Lock test: trace-stream completeness (rf2-91tl) #6.
Test plan
cd implementation && npm run test:cljs— 1818/1818 pass.cd implementation && npm run test:bundle-isolation— pass.cd tools/pair2-mcp && npm test— 325/325 pass (+11 new merge-draintests on top of prior 314).
python scripts/check_skill_mcp_drift.py— clean.