Conversation
- queue.ts: ix_outbox_session was in SCHEMA_STATEMENTS, so on a legacy
queue.db (created before session_id existed) the index DDL fired
before migrateSchema() got a chance to ALTER ADD the column —
constructor threw "no such column: session_id" and the upgrade path
the migration was designed to support never ran. Moved the index
creation into migrateSchema(), after the column is guaranteed.
New test seeds a legacy DB and asserts the constructor auto-migrates.
- ingest.py: UNIQUE(session_id, turn_count) raised IntegrityError up
the stack as a 500. Wrapped session.commit() in try/except, return
409 with a descriptive detail instead. New route-level test that
pre-empts a second record on the same session/turn.
- ingest-worker.test.ts: replaced a tautological JSON.parse("{}")
assertion with a real round-trip check that body_json preserves the
shared session_id and per-turn turn_count via pickReady.
Tests: llm-client 124 pass (+1 legacy migration test), backend 52
pass (+1 duplicate-turn test). Agent smoke against real Tbox PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- util.ts: new sessionBlobPath(cacheDir, session_id) helper shared by main.ts (write + sync read) and eviction.ts (unlink). Three call sites used to hand-roll the same path; now they can't drift. - session.ts: canonicalize uses Object.create(null) for the output map, so a "__proto__" key in a message writes a regular property instead of mutating the prototype chain. - queue.ts findEvictableSessions: group_concat separator is ASCII unit-separator (0x1F) instead of comma. Can't appear in a UUID record_id, so future-proofs against id-format changes. - backend conftest.py: after create_all, manually creates the partial unique index (active token per user) that Alembic 0001 owns but SQLModel.metadata can't express. Tests now see the same constraints as production. New test_student_token_active_per_user_partial_unique proves the constraint actually fires. Tests: llm-client 124 pass, backend 53 pass (+1). Agent smoke PASS. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three domain subdirs that name real architectural seams, not
descriptive groupings:
- http/ — HTTP server boundary (server, handler, net, proxy).
"http" because that's the actual current protocol
edge; not "transport/http/" (over-abstraction for a
hypothetical second protocol).
- recording/— per-call capture + session correlation. Name
borrowed from rrweb / OpenTelemetry / Sentry Replay,
which use the same term for the same concept.
- outbox/ — the transactional outbox pattern (Chris Richardson,
Microservices Patterns). Was already the right
vocabulary; just promoting it to a directory.
Two file renames inside outbox/ to drop now-redundant prefixes:
- ingest-worker.ts → relay.ts (the outbox-relay role)
- ingest.ts → ingest-client.ts (HTTP client to backend)
main.ts / config.ts / util.ts stay at src/ root (entry + shared).
Tests stay co-located with their source files.
Cross-directory imports are absolute-from-src (./outbox/relay,
../recording/capture, ../http/proxy etc.); same-dir imports keep
their plain ./name form.
Pure rename + path-fix; no behaviour change. typecheck clean,
124 llm-client tests pass, real-Tbox agent smoke still PASS.
Auto-MKCOL was added to the router's sync stage when real Tbox integration landed (commit 85ca416). The fake WebDAV in smoke_e2e.sh wasn't updated: it returned 200 with DAV header for everything non-PUT, but webdav-client's recursive createDirectory expects 201 on MKCOL. So MKCOL hung / errored, sync never advanced, and the record stayed pending. Also bumped sleep 3 → sleep 8 to match the other two smokes (3 stages × ~1s worker poll = 3-5s typical, 8s headroom). Now: 1/3 fake-everything, 2/3 real-Tbox single, 3/3 real-Tbox agent — all PASS.
Hands-on tests against models.sjtu.edu.cn/api/v1 driving the router
through opencode (with oh-my-opencode plugins, Sisyphus orchestrator,
deepseek-chat / deepseek-reasoner / qwen). 50 captures across 5
scenarios; all `verified=true`.
Highlights:
test 1 (small task): 17 captures from 1 trivial user prompt —
Sisyphus loops + N "What did we do so far?"
sub-agents + title gen + summarizers.
test 2 (multimodal): router preserves `content: [text|image_url]`
end-to-end; opencode's Read tool strips images
for unknown-capability custom providers.
test 3 (skill): opencode injects `<skill>` metadata into the
system prompt; agent uses descriptions, rarely
loads SKILL.md content.
test 4 (MCP): registered fs MCP; agent never reached it —
context overflow loop from plugin + tool
schemas exceeding deepseek-chat's 65k window.
Exposed router gap: `tools` not preserved
(fixed in commit 509a670).
test 5 (compression): each opencode-internal sub-task (summary,
title-gen) inherits the FULL previous
assistant output — title gen alone produced
a 371KB blob.
Net router verdict: works correctly on every capture across all 5
tests. The router is unaffected by upstream/client choices; what
varies is how the agent platform chunks its work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two test files exercise the capture / blob / sync paths at realistic
agent-platform sizes (1-5 MB), so a future "let's add a size cap"
refactor can't silently regress the audit promise.
src/recording/large-payload.test.ts (5 new unit tests):
- 1 MB user message → blob preserves verbatim, sha256 valid, <500ms
- 1 MB+ tools array (200 × 8 KB schemas) → all 200 survive round-trip
- 1 MB SSE response → preserved as raw string including [DONE]
- SessionTracker prefix-extends a 1 MB conversation across two turns
in <1s (catches an O(N²) regression in messagesEqual)
- 10 concurrent 1 MB sessions in the LRU → 10th classify <2s
src/main-sync.test.ts (+1 integration test):
- 2 MB cache file → sync stage reads + uploads byte-identical to fake
WebDAV; deterministic sha match.
Realistic upper bound: at ~5 MB we're beyond a single 1M-token
context window, so the upstream itself would reject before we see it.
These tests cover the band of inputs we'd actually receive.
Total: llm-client 134 tests pass (was 128).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The handler `_cmd_record_list` was already filtering on args.assignment
since the assignment-dimension commit (6de0a61), but the argparse
`add_argument("--assignment", ...)` had been dropped in a rebase along
the way — every `record list` call crashed with AttributeError.
Caught while walking through a live demo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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
Validation