Skip to content

feat(code-context): helper submits the context file directly (0.2.14) - #36

Merged
alphali merged 3 commits into
mainfrom
claude/direct-submit
Oct 17, 2026
Merged

alphali merged 3 commits into
mainfrom
claude/direct-submit

Conversation

@alphali

@alphali alphali commented Oct 16, 2026

Copy link
Copy Markdown
Collaborator

Part of Change Set code-context-direct-submit (getdoable/trd#617, docs/change-sets/code-context-direct-submit.yaml); needs the doable-mcp url PR for the direct path.

Problem

After answering, the model re-typed the whole screened payload (up to about 15k tokens) into the MCP call submit_code_context_round. That took 2 to 5 min per Round, and the model could change content after the privacy screen.

Change

  • record-round keeps the Round's direct_submission offer in the private frozen Round file. It never prints the token. A missing url or an invalid offer degrades to "unavailable" and does not fail the step.
  • New helper command submit:
    1. It builds and screens the payload as before, and writes context-r<rev>.json.
    2. It POSTs the file with the one-time token.
    3. It sends the same Idempotency-Key that doable-mcp derives (Python json.dumps sorted and compact, reproduced byte for byte), so an MCP fallback is an idempotent replay.
    4. Exit codes: 3 when direct submission is unavailable (the skill then falls back to build-submission + MCP submit + record-submission); 1 on 401, 404, 409 or 422, with a clear next step.
    5. Redirects are refused, so the token cannot reach another host.
  • Files renamed at the owner's request (no "safe" names):
    • local candidate: context-local-r<rev>.json (a legacy submission-r<rev>.json is migrated);
    • outgoing payload: context-r<rev>.json.
  • SKILL.md step 12 uses submit; references, README, SECURITY, PRIVACY, TESTING and CHANGELOG are updated. Version 0.2.14 (main is at 0.2.13).

Policy change (needs owner sign-off)

The helper used to make no network calls. It now makes exactly one POST:

  • redirects are refused;
  • it sends only the token header, no API key;
  • verify-release.mjs enforces that shape: one fetch(, redirect: "error", X-Doable-Submit-Token, and still blocks Authorization, DOABLE_API_KEY, raw sockets and other HTTP clients.

Tests

  • npm test: release verifier Verified doable-code-context@0.2.14, workflow check passes, node tests 6/6.
  • The node tests cover: record-round with and without the offer (including no url); legacy migration; submit success against a stub server (headers, body, an MCP-identical key checked against a Python-computed digest with non-ASCII text, receipt written, token never printed); already submitted; 401, 409 and 422; the MCP fallback.
  • E2E on a local trd + doable-mcp against staging, apob "test core flows", 2 runs:
    • each run printed Direct submission: available, ran submit and got Round submitted;
    • 0 MCP submit calls;
    • the submit phase took 0.7 to 0.8 min, against 2.9 to 3.3 min before.

🤖 Generated with Claude Code

alphali and others added 3 commits October 16, 2026 11:47
A Round in the answer state may carry direct_submission (url, one-time
dcs1. token, expires_at). record-round validates and keeps it in the
private frozen Round file as directSubmission and never prints the token;
a missing url or any invalid offer only disables direct submission.

New `submit --candidate <context-local-r<rev>.json>` runs every privacy
check, writes context-r<rev>.json, and POSTs it with the submit token and
Idempotency-Key direct:round:<round_id>:<payloadDigest> (redirects refused,
120 s timeout). It writes the usual receipt on success, exits 3 when direct
submission is unavailable so the agent falls back to MCP, and exits 1 on
401/404/409/422 with sanitized server detail.

Rename the local candidate to context-local-r<rev>.json (a legacy
submission-r<rev>.json is migrated) and default build-submission --output
and record-submission --payload to context-r<rev>.json. The release
verifier now allows exactly this one fetch call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Step 12 of doable-answer-questions now runs `submit` after
validate-submission and falls back to build-submission + MCP
submit_code_context_round + record-submission only when submit reports
direct submission unavailable (exit 3). Update the answer contract
commands, test-feature skill, PRIVACY, SECURITY, TESTING, and CHANGELOG
for the new file names and the helper's single network call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sion

trd dedupes a Round submission by Idempotency-Key. The direct POST sent
direct:round:<round_id>:<payloadDigest>, so a direct POST that succeeded
but lost its response, followed by the MCP fallback, was not a guaranteed
replay. Send exactly the key doable-mcp computes for
submit_code_context_round: round:<round_id>:<sha256 of Python
json.dumps(payload, sort_keys=True, separators=(",", ":"))>.

pythonJsonDumps reproduces that byte for byte: keys sorted by code point
at every level, compact separators, ensure_ascii escaping of every UTF-16
unit outside 0x20-0x7e as lowercase \uXXXX, integers only. The stableJson
payloadDigest for receipts and the context file envelope is unchanged.

The test posts a payload with Chinese text, an emoji, quotes and a
backslash and checks the key against a digest computed with Python.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@alphali
alphali merged commit f0da6d1 into main Oct 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant