Ghidra 12 rejects a project path with an element starting with '.', so td decompile/disasm failed on the project under .td/ghidra. The project now lives in ~/clank-ghidra/<workspace hash>; an existing .td/ghidra project is moved there.
The server's Ghidra project is now built with Ghidra 12.2 (JDK 25); a project saved by a newer Ghidra cannot be opened by an older one. /api/ghidra-project announces the release (url, sha256, dir, jdk); td ghidra download upgrades a td-installed Ghidra to it and refuses with instructions for a user-configured older one; td decompile/disasm fall back to the server's export while the local Ghidra is older than the pin. The unpacked project is handed to the local user (Ghidra opens a project only for its recorded owner). The rodecomp server image ships Ghidra 12.2 and Temurin JDK 25.
Once a function's no-decompile claim is accepted (task state skipped), removing its old annotation or changing its code no longer counts as breaking a function that matched before. This lets a maintenance batch clean up an annotation that was stacked on the wrong definition.
When a // FUNCTION: annotation binds a ??1 destructor to an original function that is a scalar deleting destructor (tests flags&1 of its argument, calls operator delete, returns this with retn 4), the submission goes back with guidance to claim the function no-decompile (compiler_generated). Only the submission target is checked, so existing code on main is unaffected. td check prints the same hint.
POST /api/admin/batches/:id/accept-regressions is admin-only; like batch review and task updates, an admin API token can now call it without a browser session.
Commits the changes already running in production (image rodecomp/server:adminreview-20261028), which were never pushed:
- Repository sync before verification: with pushRemote set, the worker fast-forwards its checkout at idle cohort boundaries, blocks merge/check lanes while a sync is pending or failed, and reloads data/decomp.json, the compile commands and data/toolchain.lock.json from one committed snapshot; the web process adopts only the published snapshot (git.js, verify.js, merge.js, checks.js, maintenance.js, project.js, app.js).
- Symbol corrections: POST /api/admin/symbols/:id accepts decorated and swapDecoratedWith with strict validation, records old/new values, and invalidates cached symbol snapshots; td maintenance symbol --decorated/--swap-decorated-with and MCP td_correct_symbol (cli.js, mcp.js, ops.js, app.js).
- Downloads: /download/ responses are no-store and answer HEAD; self-update revalidates the manifest and fetches the binary with ?sha256= to bypass stale CDN entries (update.js, app.js).
- deploy/rodecomp/compose.yaml: Forgejo SSH enabled on port 22 (SSH_DOMAIN 192.168.1.77), as running.
Tests: repo-sync, symbol-corrections and update suites added; npm test passes (164).
Batch review (including skipPeerReview) and POST /api/admin/tasks/:id required a browser session. Admin API tokens may now call them; other roles still need a password login.
GET /api/batches lists batches newest first with state, reason, notes and per-state function counts; filters by state and author, pages with before/limit. The batch detail adds notes, times and file paths. The website gets a Batches view with tabs, a detail panel linking each function to its review page, and an admin button to accept a batch sent back only for lower scores.
A definition moving to its owner unit keeps its // STATIC: line. The free-text gate pools the base versions of all submitted files; each base comment or string covers one occurrence anywhere in the submission.
unit_deps holds only the units some merge compiled, so a change to a header no merge touched compiled none of its includers. Includers now unite unit_deps with a transitive #include scan of the sources as the submission has them.
effectiveHash masks only relocated fields (an operand equal to its source line no longer counts), and normalizes MSVC anonymous-namespace hashes. A check binds every annotated global's decorated name, from its definition or an annotated extern, across all its units before comparing, so references from other units resolve to the global's ID as they will once main learns the name.
effectiveHash fingerprints a function by its bytes and where each reference lands (symbol address + addend). A maintenance batch compiles the touched units as main has them and refuses only functions whose effective code changes, so moving a global, renaming it, or reaching a member through its owning object merges.
A maintenance batch carries structural source changes that belong to no function task (moving a global to its owner, fixing data annotations, adding a header or unit). It runs the same gates as function work and compiles every unit the change can affect; it merges only when no function's compiled code changes, recording the result like any merge. POST /api/admin/symbols/:id corrects a symbol's size or name, or retires a global main no longer annotates. td maintenance submit|symbol drive both.
A region whose functions are all on main can be claimed, and its done tasks submitted as revisions: competing candidates verified and peer reviewed like new work, while the task stays done whatever the outcome. Rewriting a merged function without listing it sends the batch back instead of merging it unreviewed. Admins can force a done task back to open.
Check named files over a detached HEAD worktree using the existing overrides pipeline. Restrict automatic submission task discovery to selected files, and leave dirty working trees in place during sync.
The listing assigned each compiled function to the file of its first
source-line marker. When the optimizer schedules code inlined from a
header first (std::map::find, std::string::c_str, ...), that marker is
the header's, so a correctly compiled function was reported as "no
compiled function found after this annotation".
Checks now compile with /Z7, which puts CodeView debug info in the
object without changing codegen, and CoffObject.functionLines() reads
each function's definition file and line from its line table. The
binder uses that location and keeps the listing as the fallback for
objects without debug info; the listing still provides instruction
text and codeHash.
The "review first" rule counted only task claims, but new work is claimed by
region, so after three reviews an hour an agent could claim any number of
regions while the review queue kept growing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The queue judged "decided" by weight alone, while a decision also takes two
reviewers on the winning side. A function led by one heavy verdict dropped
out of the queue but was never decided, and waited forever.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"same" now needs 70% of the weight as well as a lead of three (7:3, not 10:7),
so a contested function is not worn down until "same" happens to lead. A
function seven reviewers could not decide goes back to its author with why;
the queue stops handing it out at the cap. Both are policy settings
(peerAcceptShare, peerMaxReviewers); trust's author record counts the same way.
One body annotated for several functions: when the original of the submitted
one is the same code as another's (same bytes, relative references to the same
targets) and main already accepts that one from this compiled body, the
submission is approved without peer review.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A resubmission whose peer verdicts carry over now goes on as approved; in_review
waited for a human review that no longer comes, and the peer queue skipped it.
A batch that only merges as a whole (noSplit) goes back to its author once a
function of it drops out later (sent back, withdrawn, superseded), instead of
holding its approved functions forever.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Changes to upstream:
- git.js: coordinator commit identity from TD_GIT_AUTHOR_NAME/EMAIL.
- build.js: docker runner can share job dirs through a named volume
(jobVolume/jobVolumeRoot) for containerized workers.
- install.js: staged installer output with logs, start prompt for regions.
- web: feed layout, cache-busting asset versions, multi-agent help section.
- build-td: fetch checksummed Windows runtime when building on non-Windows.
Additions:
- deploy/rodecomp: compose stack, Dockerfiles, Caddyfile, server.json,
bulk import/verify scripts, and README.md with the deployment notes
(the root README stays identical to upstream).
- toolchain/msvc-v142: MSVC v142 under Wine (Docker), SDK 10.0.19041.0.
- tools/rodecomp: project preparation, Forgejo provisioning, history and
identity tools.
users.review_weight (1 to 3, or auto) overrides the earned weight
(trust.js). Setting it re-weights the account's verdicts on functions still
waiting and settles the queue: what the new weight decides is accepted or
sent back at once. One reviewer still never decides alone.
td-server user review-weight <name> <1|2|3|auto>, or the admin actions
review-weight-1/2/3/auto.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Four (five after an objection) "same" could not be reached with the few
accounts online: an author never reviews their own work, and with four
active accounts that were also the authors, each function could get at most
three verdicts. 948 of 1027 verdicts in six hours went to functions that
stayed undecided; merges fell to about 5 an hour.
- Accept when weighted "same" leads "different" by peerMargin (3: 3:0, 4:1,
5:2), from at least two reviewers.
- Send back when at least peerReviews (3) "different" lead by
peerRejectMargin (2: 3:1, 4:2), from at least two reviewers.
- Close votes wait for more verdicts; reviewers are offered functions that a
verdict can still decide.
- The worker settles the waiting queue under the current rule when it starts.
- Trust and quality count accepted and sent-back functions the same way.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>