Skip to content

feat(trajectory): receive and upload bug report packages - #926

Merged
0xKT merged 20 commits into
mainfrom
feat/bug_report_receiver
Oct 13, 2026
Merged

0xKT merged 20 commits into
mainfrom
feat/bug_report_receiver

Conversation

@forrestjgq

Copy link
Copy Markdown
Contributor

Summary

Bug Report Packages can now leave the machine and be received on the other end.

Receiver. raven trajectory receiver --save-dir DIR runs a standalone aiohttp
service that takes packages in, checks them in a stream without extracting
anything (bounded decompression with a per-header cap, sparse members refused,
member rules, manifest hashes, the needs_review / risk_accepted invariant),
stores the original bytes under <save-dir>/<UTC day>/ next to a receipt
sidecar, and answers idempotently by report id (same bytes: the stored receipt;
different bytes: 409). The sidecar is written before the package is renamed into
place, so a package in a date directory is always a committed one; a damaged
sidecar is rebuilt from the package. There is no authentication by design: abuse
is bounded by a per-address token bucket, a concurrency cap, daily byte and
package budgets, a free-disk reserve and idle/total timeouts, and the budget is
settled by what the commit task actually did, not by the HTTP outcome.
X-Forwarded-For is believed only behind a --trusted-proxy network.

Upload module. raven/trajectory/upload.py carries no user interface, so the
CLI, the interactive browser and a future web page call the same functions. The
destination is one string: DEFAULT_UPLOAD_URL as shipped (empty here, meaning
no upload) overridden by RAVEN_BUGREPORT_URL (set it empty to switch uploading
off). A URL must be https or http on this machine unless
RAVEN_BUGREPORT_ALLOW_HTTP=1. The whole exchange runs under one deadline that
cancels the request, the response is read as raw bytes under a cap (compressed
answers refused), a receipt counts only when every field agrees with the
request, and the outcome is written into record.json.upload.

Wiring. report-bug and the browser's Report a bug resolve the URL before
any side effect, disclose Will upload to <origin> on the confirmation page,
upload once the package lands, and on failure keep the package and name the
retry command. The browser's Bug reports screen gains an Upload action and
raven trajectory upload-bug-report REPORT_ID sends a filed report again.

Docs: a "Send and receive bug reports" section on the trajectory debugging page
(en + zh), two CONTEXT.md terms (Bug Report Receiver, Upload Receipt), CHANGELOG.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

  • uv run pytest tests/test_trajectory_package_check.py tests/test_trajectory_receiver.py tests/test_trajectory_upload.py tests/test_cli_trajectory_commands.py tests/test_cli_trajectory_browse.py tests/test_trajectory_bugreport.py tests/test_docs_site.py -q -> all pass (about 480 new tests across them)

  • uv run pytest tests/integration/test_bug_report_upload_e2e.py -q -o addopts="" -> 3 passed (a real receiver process, a real report-bug run, a real retry, SIGTERM shutdown)

  • uv run pytest tests/ -q -p no:cacheprovider -> 28620 passed, 52 failed, 144 skipped; the 52 failures are pre-existing on this machine (network/proxy/NAT64-dependent tests) and identical to the main baseline

  • uv run ruff check . && uv run ruff format --check . -> clean

  • make lint-imports -> 10 contracts kept; make check-source-language, make check-large-files -> pass

  • make docs-build (mkdocs --strict) -> pass

  • Manual: receiver + report-bug --yes (201), repeat (200), different content under the same id (409), the interactive browser driven through a pty, failure with the receiver down and retry with upload-bug-report, a 128 MiB upload refused with 413 after ~2 MB both directly and through an nginx container (client_max_body_size). The nginx-to-receiver hop itself could not be exercised on this machine (rootless docker cannot reach host services).

  • Relevant tests pass locally

  • Relevant lint / type checks pass locally

  • User-facing docs or screenshots are updated when needed

Risk

  • Off by default: with RAVEN_BUGREPORT_URL unset and the shipped default empty, report-bug and the browser behave exactly as before; the receiver is a separate process nobody runs by accident.

  • The receiver authenticates nobody on purpose; the docs require a TLS proxy for anything beyond loopback and say not to expose the port publicly. Packages are redacted for credentials, not anonymized, so --save-dir is sensitive.

  • Rollback: unset the variable and stop the receiver. The new record.json.upload fields are optional and older readers ignore them; the receiver's on-disk layout is plain files by day.

  • Security impact considered

  • Backward compatibility considered

  • Rollback path is clear for risky changes

Related Issues

N/A

forrestjgq and others added 12 commits October 12, 2026 09:27
A Bug Report Receiver cannot trust the archive it is handed, so the
package is validated by streaming through it once: nothing is extracted,
member bytes are hashed in passing and compared with the manifest in
bugreport.json, and the decompressed volume is bounded before it is
produced. The bounded gunzip layer exists because tarfile hands a PAX or
GNU extended header's declared size straight to one read() call, so a
header claiming gigabytes would be decompressed whole into memory.

Checks run in archive order: size, gzip and tar framing, member type and
name rules (one root directory named by a report id, no links or special
files, no directory that carries data, no duplicate names), the metadata
file's schema and id, the manifest against the hashed members, the
needs_review/risk_accepted invariant, and a proper end-of-archive block
with nothing but padding after it. Absolute paths found in the metadata
are reported as notices rather than rejected.

The report id pattern is now public as REPORT_ID with is_report_id(),
and record.json's upload block gains a state enum ("", uploaded, failed)
with optional uploaded_at and error fields, validated when present.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
tarfile expands every sparse flavour (old GNU type S, PAX GNU.sparse.*
0.0, 0.1 and 1.0) before it hands the member back, and a 1.0 map is read
from the data stream in 512-byte pieces into a list whose size the
per-read cap cannot see; a few hundred compressed bytes could allocate
tens of megabytes per map entry batch before any check ran. The
inspector now reads with a TarInfo subclass that cuts each sparse handler
off at its entry, and only plain file types or directories pass the type
check.

Parser errors are now converted at the tarfile and JSON boundaries: a
bare ValueError from tarfile internals (malformed PAX numbers) or from
the JSON decoder (an integer past its digit limit) used to escape as
something other than PackageError.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The Bug Report Receiver is a standalone aiohttp service that takes a Bug
Report Package in, checks it with package_check without extracting it,
and lands the original bytes under <save_dir>/<UTC date>/ beside a
receipt sidecar. It authenticates nobody and serves no listing or
download; reachability is the network's job.

The sidecar is written first and the package is renamed into place last,
so a package in a date directory is a committed one: the same bytes again
return the stored receipt, different bytes are refused with 409, and a
sidecar that is missing or fails its contract is rebuilt from the package
at startup or on lookup. A sidecar without a package is a failed commit
and is removed. Uploads of one report id are serialised by a reference
counted lock so a late arrival never gets a second lock.

Inspection runs in a worker thread; cancelling the request stops it
cooperatively through the new should_stop hook and waits for it before
the temporary file is removed. Content-Encoding is refused, chunked
bodies need a length, and every answer is JSON.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The receiver created a day's directory behind an exists() check with a
plain mkdir, and commits run in worker threads serialised only per
report id. Two different reports arriving first thing in a UTC day both
passed the check, one mkdir raised FileExistsError, and that legitimate
upload answered 500 with its package dropped. The directory is now
created idempotently: a lost race is fine, an existing non-directory
still fails, and the 0700 mode is applied only by the creator.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
Admission now runs before a byte of body is read: a global concurrency
slot (503 without queueing), a token bucket per client address (429
with Retry-After), the daily byte and package budgets (507 / 429), and a
free-disk reserve that counts uploads still in flight (507). A request
is charged to the UTC day it was admitted on and lands in that day's
directory, and the ledger settles by what the commit task actually did:
a package renamed into place while the request was timing out or its
client had gone is still counted, and only an uncommitted upload gives
its reservation back.

Behind a trusted proxy the client is read from X-Forwarded-For right to
left, skipping only hops that are our own proxies and stopping at the
peer when a hop is not an address at all, so a forged chain cannot pick
its own rate-limit bucket. Idle and total timeouts answer 408 and stop
the inspection thread cooperatively. Every request logs one line
without body content.

serve() runs the application on a TCP site with handler cancellation
on, hands the bound address to a ready callback (port 0 is allowed),
stops on SIGTERM/SIGINT or an event, and restores the signal handlers
it replaced; run_receiver() is the blocking entry point.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
Settling a request removed the temporary file first and only then
charged or released the reservation and freed the concurrency slot, so
an unlink that failed for any reason other than absence skipped both. A
few such failures would pin every slot until the process restarted. The
steps are now nested so none can skip the next: the commit thread is
awaited and the budget settled by what it did, the temporary file is
removed or logged and left for the next startup, and the slot is always
released.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
raven/trajectory/upload.py is the client side of the Bug Report Receiver
and carries no user interface of its own: it decides where a package
goes, sends it, checks the receipt, and writes the outcome into the
report's record, so the report-bug command, the interactive browser and
a future web page call the same functions. The destination is one
string: DEFAULT_UPLOAD_URL as shipped (empty here, meaning no upload)
overridden by RAVEN_BUGREPORT_URL when that variable is set, even to an
empty value. A configured URL must be https or http on this machine
unless RAVEN_BUGREPORT_ALLOW_HTTP says plaintext is acceptable.

The whole exchange runs under one wall-clock deadline that cancels the
request outright (per-operation socket timeouts cannot bound a peer
that trickles bytes), the response is read as raw bytes under a size
cap with compressed answers refused rather than inflated, and a receipt
counts only when every field agrees with the request. A failed attempt
is recorded as failed unless the report was already uploaded; a package
that vanished or changed is refused without touching the record.

report-bug and the browser's Report a bug resolve the URL before any
side effect, disclose "Will upload to <origin>" on the confirmation
page, upload once the package lands and show the receipt or the failure
with the retry path. The browser's Bug reports screen gains an Upload
action, and raven trajectory upload-bug-report uploads an existing
report again.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…they escape

A receiver URL was only checked for scheme and host, so a malformed IPv6
literal raised a bare ValueError into the command, and a port that was
not a number or out of range passed and then failed inside httpx after
the package had been collected, pinned and packed. The URL is now parsed
the way the request will parse it, port included, and every parse error
is a configuration error raised before any side effect.

A response nested deeply enough to overflow the JSON decoder raised
RecursionError past the upload error boundary; it now counts as not
JSON, a malformed receipt on success and a plain status on failure.

The test fixtures stop system-configured proxies as well as environment
ones (urllib falls back to the macOS settings when the environment names
none), through one shared helper.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
raven trajectory receiver --save-dir DIR runs the Bug Report Receiver on
the command line: bind address and port (0 picks one and prints it),
trusted proxy networks, the package and daily limits in MiB, the
free-disk reserve, rate limiting, concurrency and timeouts all map onto
ReceiverConfig, a bad CIDR is a usage error, and a save dir the process
cannot prepare is reported in one line instead of a traceback.

The trajectory debugging page gains a "Send and receive bug reports"
section in both languages: what the receiver stores and where, the
deployment rules (loopback by default, a TLS proxy for anything else,
the nginx location and --trusted-proxy), the RAVEN_BUGREPORT_URL table
the clients read, the retry command, and what is and is not checked. An
integration test drives a real receiver process, a real report-bug run
and a real retry over the printed address and the environment variable.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The listening address, the save directory, an upload failure and the
retry command were printed through rich's wrapping, which splits a long
path or the upload-bug-report command across lines at eighty columns and
leaves nothing a reader can copy. They are now printed soft-wrapped.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
The subsections of the new "Send and receive bug reports" section had no
pinned anchors on the Chinese page, so Markdown would have numbered them
and the links the English page gets for free would not resolve there.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
… never listens

The "Cannot prepare" diagnostic was the one line still printed through
rich's wrapping, so a long temporary path split "Permission denied" over
two lines; it is soft-wrapped like the other diagnostics, and the test
now forces a forty-column console and a deep path.

The end-to-end helper started the receiver with a blocking reader thread
and a per-line timeout, so a child that never listened was left running
with its reader stuck. It now uses an asyncio subprocess under one
deadline for the whole start, kills and waits for the child on every
exit but success, and reads both streams for the diagnostics, since the
command reports a refused directory on stdout. Two tests cover a silent
child and a refusing one.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: malformed JSON and receiver URLs still escape the new validation boundaries.

I reviewed the full github/main...HEAD diff and the surrounding package builder, receiver/store, upload client, CLI/browser callers, tests, and commit history. I also checked AGENTS.md/CLAUDE.md, the trajectory terms added to CONTEXT.md, backward compatibility for existing records and upload-disabled installs, architecture boundaries, and whether tests were weakened; I found no separate issues in those areas.

Verification:

  • uv run pytest tests/test_trajectory_package_check.py tests/test_trajectory_receiver.py tests/test_trajectory_upload.py tests/test_trajectory_bugreport.py tests/test_cli_trajectory_commands.py tests/test_cli_trajectory_browse.py -q: 627 passed after uv sync --extra dev. The initial run had 627 fixture-setup errors because the declared everos-memory dev extra was absent.
  • uv run pytest tests/integration/test_bug_report_upload_e2e.py -m integration --override-ini norecursedirs='' -q: 3 passed.
  • Source-language, large-file, Ruff, and git diff --check checks passed. (make is unavailable in this environment, so I ran the repository check scripts directly.)

Comment thread raven/trajectory/package_check.py Resolved
Comment thread raven/trajectory/receiver.py Resolved
Comment thread raven/trajectory/upload.py Resolved
…n boundaries

A bugreport.json nested deeper than the interpreter allows made
inspect_package raise RecursionError through the receiver, which answered
500 instead of 400 bad_package; the same depth in a receipt sidecar made the
startup repair raise instead of rebuilding the sidecar. Both decoders now
treat decoder recursion as invalid JSON.

The upload URL validator refuses credentials (without echoing them) and any
query or fragment, since the endpoint path appended to such a URL would be
swallowed; the disclosure line strips userinfo regardless.

The tarfile ValueError test patches TarInfo.fromtarfile, the entry that
TarFile.next calls on every Python version; frombuf is bypassed by the
private _frombuf since CPython 3.12.15, which left the test asserting
nothing on CI.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: URL validation must stop echoing rejected secrets.

The package-metadata and sidecar-recursion findings are fixed, verified, replied to, and resolved. The URL thread remains open because malformed credential-bearing URLs can fail during urlsplit or parts.port before the credential check and the generic error echoes the complete URL; query/fragment rejection also echoes query tokens.

For this revision I reviewed the complete delta from 674371624e2e, rechecked it against the current github/main...HEAD change and the affected package parser, receiver/store, uploader, CLI/browser callers, docs, tests, and history. I covered AGENTS.md/CLAUDE.md and CONTEXT.md rules, architecture boundaries, backward compatibility, and test-strength changes; no other issue survived refutation.

Verification:

  • uv run pytest tests/test_trajectory_package_check.py tests/test_trajectory_receiver.py tests/test_trajectory_upload.py tests/test_trajectory_bugreport.py tests/test_cli_trajectory_commands.py tests/test_cli_trajectory_browse.py -q: 641 passed.
  • uv run pytest tests/integration/test_bug_report_upload_e2e.py -m integration --override-ini norecursedirs='' -q: 3 passed.
  • Ruff, source-language, large-file, and git diff --check checks passed.

Every UploadConfigError used to quote the URL as typed, so a parse failure
(a bad port, a broken IPv6 host), a wrong scheme, a port out of range or
the query rule echoed credentials and query strings onto the terminal.
The messages now show the URL cut on the string before any parsing:
userinfo and everything from the first "?" or "#" are dropped, leaving
scheme, host, port and path, which is what a reader needs to spot the typo.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; suggestions only, and they are marked inline.

The reported URL leaks are fixed: malformed host/port and IPv6 failures redact userinfo, and query/fragment values are elided before the error reaches the CLI or record path. The original URL thread is resolved.

Nonblocking follow-up: _shown still exposes text before an unescaped ? or # that appears before @, and the raw port-parser exception can repeat that token. This needs a deliberately malformed credential URL and has an immediate configuration escape hatch, so it does not meet the fourth-round blocking bar.

Coverage: I reviewed the current delta against the previously reviewed full branch diff, the repository rules (AGENTS.md/CLAUDE.md and runtime context), affected callers and history, backward compatibility, the new tests for weakening, and the environment-to-validation-to-CLI/record security boundary. Verification: uv run pytest on the six focused unit files (662 passed); uv run pytest -m integration tests/integration/test_bug_report_upload_e2e.py -q (3 passed); Ruff, source-language, large-file, and git diff --check all passed.

A credential URL with an unescaped "?" or "#" before the "@" rendered its
userinfo, because the query cut ran before the userinfo cut, and the port
parser's own message quoted the same token back. The renderer now removes
everything up to the last "@" before the first "/" first, then cuts at the
first "?" or "#", and parse failures no longer append the parser's text.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers; this can merge as far as I am concerned.

The remaining URL-error disclosure is fixed: malformed userinfo is removed before query/fragment truncation, and parser-controlled error text is no longer copied into the user-facing message. Adversarial checks across the previous and newly added URL shapes did not expose the test secrets.

Coverage: I reviewed the revision delta against the previously reviewed full branch diff, repository rules and runtime context, affected CLI/record callers and history, backward compatibility, the security boundary, and the test changes for weakening. Verification: the six focused unit files passed (670 tests), the real-process upload integration file passed (3 tests), and Ruff, source-language, large-file, and git diff --check passed. All threads I opened remain resolved.

Comment thread raven/trajectory/package_check.py Resolved
Comment thread raven/trajectory/receiver.py Resolved

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two newly reported package resource-boundary failures remain unresolved.

This updates my whole-record stance after the new comments: one thread reports recursive tar extended headers escaping as RecursionError with an HTTP 500 and excess memory use; the other reports notice amplification creating sidecars outside the configured daily-byte accounting. These are @0xKT's findings, not mine, so I am not re-grading them, replying in their threads, or resolving them. The author and that reviewer need to settle the reported facts.

The unchanged revision's existing relevant tests still pass: 386 package/receiver/upload/bug-report unit tests and 3 real-process integration tests. That green run does not exercise the new reproductions and therefore does not answer the blockers. All threads I opened remain resolved.

…he receiver

tarfile fetches the header after a PAX or GNU long header by recursing once
per header and holds the whole chain at once, so an archive chaining
thousands of them escaped inspect_package as RecursionError (answered 500,
not 400) and held depth times the per-read cap in memory. _StrictTarInfo
now counts the extended headers before a member on the TarFile and refuses
more than four with header_chain_too_long; the tar boundary also maps a
RecursionError to a PackageError in case another shape ever reaches it.

A small package whose metadata is mostly absolute paths produced one notice
per hit, growing the sidecar to tens of megabytes that the daily byte budget
never saw. The path scan now stops after MAX_NOTICES hits (the scanner takes
a limit), the sidecar digest keeps the first 1000 characters of each field,
the daily ledger counts a sidecar with its package both when a commit is
settled and when a day is backfilled, and the docs say so.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two resource-boundary threads remain open pending their reviewer's confirmation.

I independently verified the new revision's substance. Extended-header chains are cut off before recursive accumulation, RecursionError is contained as a package error, and the HTTP regressions cover 400 responses. Metadata scanning and digest fields are bounded, the former multi-megabyte sidecar amplification is reduced below 8 KiB in the regression, and landed/backfilled sidecar bytes are included in the ledger. I found no new blocker of my own.

Named nonblocking follow-up: admission still reserves only the declared package bytes, so settlement can put landed_bytes above max_bytes by the bounded sidecar size (a direct ledger check with max 100, reserve 90, settle 110 ends at 110). This change introduces that accounting edge and it is reachable near the limit, but the now-bounded sidecar overhead and existing free-disk floor give operators a working escape hatch, so it does not meet the late-round blocking bar. This is evidence about the implementation, not a re-grading of @0xKT's open finding.

Coverage: I reviewed the eight-file delta against the previously reviewed branch, repository rules and runtime context, affected callers and history, backward compatibility, the resource-boundary architecture, and the tests for weakening. Verification: six focused unit files passed (684 tests), the real-process integration file passed (3 tests), and Ruff, source-language, large-file, and git diff --check passed. All threads I opened remain resolved; I did not resolve or reply in the other reviewer's threads.

forrestjgq and others added 2 commits October 13, 2026 08:16
# Conflicts:
#	tests/test_trajectory_bugreport.py
… notices

With the export gate now reading parsed values instead of JSON text, the
receiver's triage notices follow: _check_metadata runs
find_absolute_paths_in_json over the decoded bugreport.json, so a literal
backslash in a description no longer produces a notice, and each notice
names the field it came from. The JSON scanner takes the same limit the
text scanner does, so the notice cap keeps bounding the walk.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two reviewer-owned resource-boundary threads remain open pending their owner's confirmation.

The new parsed-value reconciliation works for ordinary metadata and the existing fixes remain intact. I added two nonblocking crafted-input follow-ups inline: the recursive JSON walker has a narrow decode/walk recursion gap, and its new field-path label can copy an oversized object key into one notice. Neither meets the late-round blocking bar: the first leaves the receiver running after the rejected request, and the second is bounded by the metadata/package limits and counted by the ledger.

Coverage: I separated the current-main merge from the branch commit, reviewed the actual github/main...HEAD diff plus the three-file new commit, repository rules and runtime context, callers/history, backward compatibility, the resource-boundary architecture, and tests for weakening. Verification: six focused unit files passed (701 tests), the real-process integration file passed (3 tests), and Ruff, source-language, large-file, and git diff --check passed. The older threads I opened remain resolved; the two new inline threads are explicitly nonblocking suggestions. I did not resolve or re-grade the other reviewer's threads.

Comment thread raven/trajectory/package_check.py Resolved
Comment thread raven/trajectory/package_check.py Resolved
…ice labels

json.loads accepts nesting close to the interpreter's recursion limit, so
the recursive walk behind find_absolute_paths_in_json overflowed on a
document the decoder had just accepted and escaped inspect_package as a
RecursionError. The walk now runs on an explicit stack in the same
document order. The field label of a receiver notice is cut to 120
characters, so a long object key cannot enlarge the sidecar past the
bounds the digest and notice caps set.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two reviewer-owned resource-boundary threads remain unresolved.

The current delta fixes both nonblocking findings I opened: parsed metadata is walked iteratively in document order, and rendered field labels are bounded. I reproduced the original depth-992 input and 100,000-character key successfully, replied to the author, and resolved both of my threads. I found no new defect in this revision.

Coverage: I reviewed the five-file delta against the previously reviewed branch, repository rules and runtime context, callers/history, backward compatibility, resource limits, and tests for weakening. Verification: six focused unit files passed (706 tests), the real-process integration file passed (3 tests), and Ruff, source-language, large-file, and git diff --check passed. All threads I opened are resolved; I did not resolve or re-grade the other reviewer's two still-open blockers.

…nner

The deadline tests asserted the upload ended within 1 s of a 0.3 s total
timeout, and a busy CI shard delivered the cancellation after 1.5 s. The
error message already names the path that raised; the elapsed bound only
has to show the request was cut short of the alternatives (a 1 s io_timeout,
a 3 s drip), so it is now 2.5 s and a parameter of the helper.

Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking: the two reviewer-owned resource-boundary threads remain unresolved.

The new delta only widens the deadline tests' elapsed-time ceiling from 1.0s to 2.5s. I tried to refute whether that weakens the behavior check: it still requires the retryable UploadError and the total-deadline-specific exceeded 0.3s message, which distinguishes this path from the I/O-timeout error, while remaining below the server's 3s drip. The upload test file passes, and the three deadline cases passed in five consecutive runs.

I covered the repository rules, this revision's diff, the exercised upload caller/error semantics, backward compatibility, whether the test was weakened to green, and the relevant timeout architecture. I found no new defect in this revision. The existing chained-header and sidecar-budget threads were opened by another reviewer and remain unresolved, so I am preserving their blocking stance rather than re-grading or resolving them.

Verification:

  • uv run pytest tests/test_trajectory_upload.py -q - 88 passed
  • five runs of uv run pytest tests/test_trajectory_upload.py -k deadline -q - 3 passed each
  • git diff --check github/main...HEAD - passed

@0xKT
0xKT merged commit ef3b4bf into main Oct 13, 2026
29 checks passed
@0xKT
0xKT deleted the feat/bug_report_receiver branch October 13, 2026 09:52
@0xKT 0xKT mentioned this pull request Oct 20, 2026
4 of 12 tasks
0xKT added a commit that referenced this pull request Oct 20, 2026
## Summary

Bump the package version from 0.2.4 to 0.2.5 for the next release. Only
pyproject.toml and uv.lock change.

Since v0.2.4, main merged 115 PRs: 30 features, 74 fixes, three perf,
three
docs, three test, one refactor and one build. The larger features are
work on
registered machines (#920, #922, #934, #947, #983, #1005), the host as
an agent
manager (#1010), the knowledge reader (#489), Personas (#709), the
everos-cloud-memory plugin (#883) with everos 1.4.2 (#950), and the
trajectory
view with bug report export and upload (#877, #926, #948). None carries
a
BREAKING CHANGE footer. The maintainer chose a patch bump, the way
v0.2.1
through v0.2.4 were for their features.

The release workflow now also builds the everos-cloud-memory wheel
(#883), a
step no release has run yet, so a v0.2.5-rc1 tag goes first to dry-run
the
pipeline. The stable tag and release notes follow once this lands.

uv.lock is regenerated with uv 0.12.12. uv 0.9.x rewrites the dependency
markers of five unrelated packages, and uv 0.13.x raises the lock
revision from
3 to 5. uv 0.12.12 leaves only the raven version line changed.

## Type

- [ ] Fix
- [ ] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [x] Other

## Verification

- `uvx --from uv==0.12.12 uv lock` on a clean worktree cut from
origin/main:
  Updated raven v0.2.4 -> v0.2.5
- `git diff --numstat origin/main..HEAD`: one line each in
pyproject.toml and
  uv.lock
- `uvx --from uv==0.13.3 uv lock --check`: exit 0, so the
  `uv export --locked` step in release.yml accepts the lock
- `PYTHONPATH=. python3 scripts/check_commit_messages.py
origin/main..HEAD`:
  exit 0
- `npx commitlint --from origin/main --to HEAD --config
commitlint.config.cjs`:
  exit 0
- No test run: the change is version metadata only.

- [ ] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

No code change. Rollback is reverting this commit before any v0.2.5 tag
is
pushed.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A

Co-authored-by: Claude (claude-opus-5-5) <noreply@anthropic.com>
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.

3 participants