feat(mcp): add manage_adr mode='append' - #1243
Conversation
'update' replaces the whole ADR, so adding a single entry costs a full re-send of the document. For a large ADR that is both expensive and a chance to silently drop existing text on the round-trip: the caller has to reproduce every byte it did not intend to change. 'append' concatenates server-side, so the stored copy is the only source of the prefix. Trailing newlines are trimmed and exactly one blank line is inserted, so repeated appends do not accumulate whitespace; an empty or missing ADR degrades to a plain create. append without 'content' fails with status='missing_content' instead of falling through to the 'get' branch — a caller that meant to write must not receive a success-shaped read. The response carries content_length and appended_length so callers can confirm the write landed without re-fetching the document, which for large ADRs is the expensive part. Tests: append extends without replacing (order + single-blank-line separator + sections still parse), append creates when absent, append without content errors and leaves the ADR untouched, and the mode is advertised in tools/list. Signed-off-by: andis777 <24672074+andis777@users.noreply.github.com>
CI is green — this closes the verification gap flagged in the descriptionThe PR description says compilation was unverified because no C toolchain was available where this was written. CI has now covered exactly that, so please disregard that caveat: all 25 checks pass. Builds and full suites on every target: The four new tests actually executed"Suite passed" is not the same claim as "my tests ran", so I checked. Per-test names are not in the CI logs — a control grep for the pre-existing
One note on the audit, in case it comes up in review: |
|
Thanks for the careful tests and docs. This needs an ADR-model decision before implementation approval. The current |
|
Thanks for this, and sorry for the wait. Queued for review. MERGEABLE with all 28 checks green, +205/-3 across three files. An append mode for |
Problem
manage_adrcan only replace:mode='update'overwrites the stored document in full. Adding a single entry to a long-lived ADR therefore costs a full re-send of the whole document.That is more than an efficiency problem. The caller has to reproduce every byte it did not intend to change, so the prefix is only as safe as the round-trip that carried it — an agent re-emitting ~60 KB of prose to append one paragraph has ~60 KB of opportunity to silently drop or mangle text that nobody asked it to touch. Verifying the result means fetching the document back and diffing it, which doubles the cost again.
Change
Adds
mode='append', which concatenates server-side so the stored copy is the only source of the prefix:appendis safe as a first write.appendwithoutcontentreturnsstatus='missing_content'andisErrorinstead of falling through to thegetbranch — a caller that meant to write must not receive a success-shaped read. (updatehas the same fall-through today; left alone to keep this change scoped.)content_lengthandappended_lengthso callers can confirm the write landed without re-fetching the document.get,update,storeandsectionsare untouched.The
modeenum andcontentnow carry descriptions, mostly soupdate = REPLACE the whole documentis visible at the call site rather than something you learn by overwriting an ADR.Tests
Four cases in
tests/test_mcp.c:tool_manage_adr_append_extends_without_replacing— prefix survives verbatim, new chunk lands after it, exactly one blank line joins them, both sections still parse viamode='sections'.tool_manage_adr_append_creates_when_absent— exact-match assert that no leading blank line is introduced.tool_manage_adr_append_without_content_errors— errors and leaves the stored ADR byte-identical.tool_manage_adr_append_is_advertised— the mode appears intools/list, so callers can discover it instead of continuing to pay for rewrites.Verification I could not do
I was unable to build or run the test suite — no C toolchain was available on the machine this was written on. Please treat compilation as unverified; I would rather flag this than have it discovered in review.
What I did instead:
heap_strdup,SKIP_ONEfromsrc/foundation/constants.h,yyjson_mut_obj_add_uintas used insrc/pipeline/artifact.c,cbm_mcp_handle_tool,ASSERT_STR_EQ), and mirrored the existingsize_t/SKIP_ONEindexing idiom fromadr_list_sections_from_contentto stay within the project's-Werrorsettings.adr_append_contentto a scratch script and ran the boundary cases: CRLF endings, multiple trailing newlines, empty existing content, content consisting only of newlines, empty addition, and four appends in a row (no triple newlines accumulate). All behaved as asserted in the C tests.scripts/security-audit.sh: passes. It reportssrc/mcp/mcp.c has 17 file read operations (expected max 15), but that is pre-existing —git show HEAD~1:src/mcp/mcp.c | grep -c 'fopen\|fread\|read_file'is also 17. This change adds no file reads.Happy to adjust naming (
appendvsadd), the separator policy, or themissing_contentstatus if you'd prefer different conventions.🤖 Generated with Claude Code