Skip to content

fix(capabilities): add openclaw.compat.pluginApi to manifest and surface in host_compat#414

Draft
plind-junior with Copilot wants to merge 51 commits into
mainfrom
copilot/fix-ci-test-py3-11
Draft

fix(capabilities): add openclaw.compat.pluginApi to manifest and surface in host_compat#414
plind-junior with Copilot wants to merge 51 commits into
mainfrom
copilot/fix-ci-test-py3-11

Conversation

Copilot AI commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Two tests added in #237 were failing because openclaw.plugin.json was missing the "openclaw" key that _load_host_compat() reads to populate kb.capabilities.host_compat.

Changes

  • openclaw.plugin.json — adds "openclaw": {"compat": {"pluginApi": ">=2026.6.0"}}, consistent with the identical value already declared in package.json
  • tests/test_openclaw_plugin_manifest.py — removes "openclaw" from the dead-dialect-fields guard; it's now a live field carrying the compat declaration, not a stale pre-2026.6 relic

The capabilities.py implementation (_PLUGIN_MANIFEST_PATH, _load_host_compat(), host_compat= in capabilities()) and the Capabilities.host_compat model field were already in place on the test branch — the manifest was the only missing piece.

Tet-9 and others added 30 commits June 24, 2026 11:00
Partial crystallize runs that already wrote session-{id} summary pages
failed on retry with "page already exists". Upsert the summary page
and derive its artifact list from all approved session proposals.

Fixes #139

Co-authored-by: Cursor <cursoragent@cursor.com>
the non-empty contract for `Claim.text`, `Entity.name`, and `Page.title`
lived only in the `propose_*` helpers, so `store.put_*`, `store.update_*`,
and bundle/sync import (via `_validate_content`) all silently accepted
empty or whitespace-only values and landed artifacts carrying zero of the
field's semantic content.

add `@field_validator`s on each field, mirroring the existing
`Claim.evidence` min-citation validator (#81/#82): they reject blank input
at construction time, so every write path — direct construction,
put/update, and import — inherits the check at once. the validators gate
on emptiness only and preserve surrounding whitespace of non-blank values.

`Source.locator` is deliberately left out of scope — its format question
is bigger and deserves its own issue, as the report notes.

closes #155
address review on #300: the three #155 field validators
(Claim.text / Entity.name / Page.title) were structurally identical
strip-checks. extract a module-level `_require_non_empty(v, label)` helper
they all delegate to, keeping the per-field error messages. also fix a
mid-entry sentence casing in the CHANGELOG. no behaviour change.
Unexpected exceptions in handle_request() included full stack traces
in the JSON error envelope, exposing paths and internals to HTTP /rpc
clients. Log server-side and return message only.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ding queue

a long `kb.list_pending` forces the reviewer to reconstruct, per proposal,
whether the claim fits the existing kb, whether its citations resolve,
whether it duplicates something already filed, and whether it contradicts
an approved claim. this adds an optional triage pass that scores each
pending proposal on those four signals and attaches a `_meta.vouch_triage`
block (recommendation/score/signals/rationale) to help a reviewer
prioritize, without ever deciding anything itself.

read-only by construction: the pass never calls proposals.approve/reject,
store.put_*, or store.move_proposal_to_decided — a human still calls
kb.approve/kb.reject. citation_quality reuses proposals._payload_block_reason;
duplication_risk reuses the propose-time embedding similarity path
(embeddings.similarity.find_similar_on_propose) and degrades to a difflib
heuristic when the embeddings extra isn't installed. fit uses a separate,
lower-threshold embedding search so a near-duplicate hit doesn't also
inflate fit and cancel out its own duplication penalty.

opt-in via `triage.enabled: true` in config.yaml (default false). registered
at all four kb.* surface sites (server.py, jsonl_server.py, capabilities.py,
cli.py) plus `vouch triage [proposal-id...]` with `--json` and `--reverse`.
vouch diff shipped CLI-only in 0.1.0, an explicit non-goal at the time
("MCP/JSONL parity ... kb.* surface unchanged"). that leaves it the only
read method skipping the four-site registration convention documented in
CLAUDE.md, so agents talking MCP/JSONL have no way to fetch a revision
diff. adds the kb_diff MCP tool, the kb.diff JSONL handler, and the
capabilities.METHODS entry, next to the other by-id read tools
(kb_read_claim/kb_read_page) with the same unrestricted-read posture.

also makes new_id optional for a superseded claim: diff_artifacts and
the CLI both resolve it from superseded_by when omitted, erroring
clearly when there's no successor (pages still require an explicit
new_id — they have no successor pointer). closes #327.
…eback-leak

fix(http): stop leaking tracebacks in JSONL/RPC error responses
#361 inserted `_log = logging.getLogger(...)` between two import
groups in jsonl_server.py. that statement-among-imports trips ruff's
E402 on every import that follows it, so `ruff check src tests` — the
lint gate in ci — now fails on the whole repo. move the logger
definition below the imports (where storage.py already keeps its own
module logger); no behaviour change.
.codex/config.toml is codex's primary config file, so the plain-copy
path silently skipped any project where codex was already configured
and vouch never got wired. add a toml_merge entry flag mirroring
json_merge: parse the existing destination with tomllib, deep-merge
the template's tables into it (existing user values always win on
conflict), and write back. flip the codex T1 entry to toml_merge.

writing uses a minimal hand-rolled serializer (tables, arrays, inline
tables in arrays, scalars, datetimes) so the dependency set stays
unchanged; the output must survive a tomllib round-trip back to the
merged data, and anything the serializer can't faithfully re-emit
degrades to skipped rather than risking the user's config.

closes #384
feat(diff): register kb.diff at all four kb.* surface sites
feat(review): kb.triage_pending — advisory triage scoring for the pending queue
codex reads AGENTS.md for project instructions the way cursor does,
but the codex adapter stopped at T1, so a codex session got the kb
tools with no standing guidance on recall-first or the review gate.
ship adapters/codex/AGENTS.md.snippet as a T2 tier with the standard
fence markers, kept in lockstep with cursor's snippet modulo the host
name (enforced by a sync test).

also close the edited-fence gap in _install_fenced per the ticket's
acceptance criteria: a fence body that drifted from the shipped
snippet is replaced within the markers (reported as merged) instead
of being skipped forever, while user content outside the fence stays
untouched. a begin marker without an end marker is treated as corrupt
and left alone.

closes #385
claude-code T3 ships nine slash commands; codex users got none of the
guided flows. ship them as codex skills under the project-local
.codex/skills/ as a T3 tier.

scope decision the ticket asked to resolve first: codex loads custom
prompts only from ~/.codex/prompts/ (user-global) and has deprecated
them upstream in favour of skills, which do have a project-local home
at <project>/.codex/skills/ in trusted projects. the #179 rule forbids
a project-scoped install from touching home-directory state, so
prompts are out and skills are in — recorded in the manifest comment
and the adapter readme, with a manual-copy pointer for users who still
want ~/.codex/prompts.

the SKILL.md files are referenced from the openclaw mirror rather than
duplicated (same reuse pattern openclaw itself applies to the
claude-code commands), so one edit updates every host. a parametrized
sync test asserts each installed codex skill body equals the matching
claude-code command body, and a scope test asserts every installed
path stays under the project target.

closes #386
session auto-capture was claude-code-only: hooks drive capture observe
and capture finalize live. codex has no hook stream, but it persists
every session as a rollout jsonl under $CODEX_HOME/sessions containing
user messages, tool calls, and outputs — everything the existing
rollup needs, just after the fact.

new cli command `vouch capture ingest-codex [<rollout> | --latest]`
parses one rollout through a small dedicated parser (codex_rollout.py)
that maps function_call records into the same observation shape
capture.observe produces — shell commands with failure detection from
exit codes, apply_patch heredocs surfaced as file edits, mcp tools
under their own names, session mechanics skipped — then reuses the
existing build_summary_body -> propose_page rollup. one code path from
observation to proposal, two front doors.

the rollout format is not a stable public contract: unknown record
types are tolerated, and unreadable, compressed, or meta-less files
degrade to a CodexRolloutError with an actionable message and a
non-zero exit, never a stack trace. re-ingesting a session is a no-op
keyed on the rollout's session id; --latest resolves the newest
rollout whose recorded cwd matches the current project. proposals are
attributed to the codex actor (VOUCH_AGENT wins when set), respect
capture's enabled/min_observations config, and never touch approve().

fixture rollouts use placeholder data only, enforced by a test.

closes #387
feat(adapters): toml_merge install strategy for codex config.toml
feat(adapters): codex T2 — AGENTS.md fenced snippet
feat(adapters): codex T3 — skills mirroring the vouch slash commands
LGTM! Eager to see how it works with Codex!
with `vouch capture ingest-codex` available, codex can self-capture
the way claude-code T4 does. the ticket assumed the `notify` setting
in config.toml, but codex only honours notify in user-global config —
it is a restricted key in project-local layers — and the #179 rule
forbids touching ~/.codex. codex's hooks system is the project-local
equivalent: `.codex/hooks.json` (loaded in trusted projects) fires
Stop when a turn completes, with a payload carrying the session id and
transcript path. T4 ships that file through json_merge, so an existing
user hooks.json is deep-merged into, never overwritten, and re-runs
don't duplicate the hook.

the handler is a new --hook mode on ingest-codex: read the stop
payload from stdin, resolve the session's rollout (transcript_path
when present, else by session id in the rollout filename), ingest
idempotently, and exit 0 no matter what — the same never-break-the-
host rule capture observe follows.

stop fires per turn, not per session end, so the finalize policy the
ticket left open is resolved as idempotent re-ingest: the dedup guard
now refreshes a session's still-PENDING proposal in place when the
rollout grew (same id, updated summary, audit-logged), reports an
unchanged rollout as a no-op, and never resurrects a proposal a human
already decided. storage gains update_proposal, a pure-I/O in-place
rewrite of a pending proposal file.

wiring only — everything still lands through propose_page; nothing
touches approve().

closes #388
…otency

LGTM! Apologies for the delayed review on my end.
tests/test_openclaw_plugin_load_real.py set the pattern: exercise the
real host cli when it's on PATH, skip cleanly where it isn't. the
codex adapter had no equivalent, so regressions in the shipped config
only surfaced when a user hit them — the old T1 silent-skip no-op is
exactly the class of bug a live gate would have caught.

the new suite installs the adapter into a temp project at T4, marks
the project trusted inside an isolated CODEX_HOME, and asserts through
`codex mcp list --json` that the vouch server is visible: next to a
pre-seeded unrelated server on the merge path, alone on the fresh
path, and absent for an untrusted project (the config must stay inert
where codex says it does). a full-tier check covers the fenced
AGENTS.md, the skills, and the Stop hook, and an isolation check pins
every written artifact under the temp project.

assertions target the observable contract — server listed, config
parses, snippet present — not codex internals, so version bumps
shouldn't break the suite. no network, no credentials, never touches
the real ~/.codex; runs in the normal pytest invocation.

closes #389
three surfaces went stale once the codex tier work landed. the
adapters table row described codex as a one-file config.toml snippet;
it now lists the tiered install. the codex adapter readme led with the
manual ~/.codex edit; it now leads with `vouch install-mcp codex`,
explains the project-local .codex/config.toml choice and the trust
requirement, summarizes what each tier adds (mcp wire / AGENTS.md
snippet / skills / capture hook) with the merge-safety guarantees, and
keeps the manual edit as an explicit fallback section. getting-started
treated codex as a one-liner aside inside the claude instructions; the
install section now shows both hosts side by side.

the prompts-location decision from the T3 ticket and the notify
restriction from the T4 ticket stay documented in their sections. no
code; placeholder examples only.

closes #390
LGTM! Apologies for the delayed review on my end.
feat(adapters): codex T4 — hook wiring for automatic session capture
test(adapters): live codex install gate — real-cli verification that skips when codex is absent
plind-junior and others added 7 commits July 7, 2026 16:40
full docstrings for every exposed tool are paid on every turn. under minimal
and standard, trim each tool's description to its first line (full keeps the
complete docstrings), cutting the per-turn context cost.
note VOUCH_TOOL_PROFILE / mcp.tool_profile (default minimal, values
minimal|standard|full) in the transports reference and the claude-code
adapter guide; this repo has no mintlify/ tree yet so both live under
docs/ and adapters/claude-code/ instead.
_dedupe_near_duplicates sorted by score and returned that score-sorted
order, which fought graph-expansion: build_context_pack appends
decayed-score neighbours after the ranked hits, and fused primary hits
now carry small rrf scores. re-sorting let an irrelevant depth-2
neighbour outrank a real match, and the max_chars tail-pop then evicted
the real match instead of the neighbour.

keep the keep-decision in descending-score order so the highest-scored
member of a near-duplicate cluster still survives, but return survivors
in the caller's original order so budget eviction drops the tail
(appended neighbours), not the ranked hits.
summarize this branch's user-visible changes under [Unreleased]: the
minimal-by-default mcp tool profile, rrf fusion for auto/hybrid
retrieval, and the per-prompt auto-recall hook in the claude-code
adapter.
merges the friendlier-mcp-surface feature into the test staging branch:
minimal mcp tool profile by default, fusion-by-default retrieval with
near-duplicate suppression, per-prompt auto-recall hook, compact tool
descriptions, and a real mcp<->methods parity test. also pulls in the 8
main commits the test branch was missing (our base is current main).
changelog and test_capabilities conflicts resolved by keeping both sides.
…rofile

install-mcp now writes a fourth hook (UserPromptSubmit -> vouch context-hook),
and recall fires per prompt, not only at session start. also note the kb.* mcp
surface defaults to a lean profile that widens via VOUCH_TOOL_PROFILE.
@github-actions github-actions Bot added the size: XS less than 50 changed non-doc lines label Jul 7, 2026
@github-actions github-actions Bot added docs documentation, specs, examples, and repo guidance cli command line interface dual-solve dual-solve orchestration review-ui browser review ui adapters agent host adapters and install manifests mcp mcp, jsonl, and http surfaces storage kb storage, migrations, schemas, and proposals retrieval context, search, synthesis, and evaluation sync sync, vault mirror, and diff flows schemas json schemas and generated schema assets tests tests and fixtures size: XL 1000 or more changed non-doc lines and removed size: XS less than 50 changed non-doc lines labels Jul 7, 2026
…ace in host_compat (#237)

add `openclaw.compat.pluginApi` to `openclaw.plugin.json` so that
`_load_host_compat()` in `capabilities.py` can read it and surface it in
`kb.capabilities.host_compat`. this closes the host-compat drift detection
introduced by the new `test_capabilities_host_compat_*` tests.

also remove `"openclaw"` from the dead-dialect-fields list in
`test_manifest_carries_no_dead_dialect_fields` — the `openclaw` key is now
a live declaration (`compat.pluginApi`), not a stale pre-2026.6 relic.
@github-actions github-actions Bot added the openclaw openclaw integration label Jul 7, 2026
Copilot AI changed the title [WIP] Fix failing GitHub Actions job ci / test (py3.11) fix(capabilities): add openclaw.compat.pluginApi to manifest and surface in host_compat Jul 7, 2026
Copilot AI requested a review from plind-junior July 7, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

adapters agent host adapters and install manifests cli command line interface docs documentation, specs, examples, and repo guidance dual-solve dual-solve orchestration mcp mcp, jsonl, and http surfaces openclaw openclaw integration retrieval context, search, synthesis, and evaluation review-ui browser review ui schemas json schemas and generated schema assets size: XL 1000 or more changed non-doc lines storage kb storage, migrations, schemas, and proposals sync sync, vault mirror, and diff flows tests tests and fixtures

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants