Skip to content

v0.24.4

Latest

Choose a tag to compare

@github-actions github-actions released this 29 Sep 09:23
· 67 commits to main since this release
796e3c0

Added

  • A review of content this deployment already reviewed is replayed from a durable store instead of paying for the reviewer again. src/automation/reviewVerdictStore.ts keys a verdict on (kind, base, sha256(content digest)), the digest being every changed path paired with its content hash, and replays the stored outcome on a hit — from both the plain openswarm review path (reviewCommand.ts) and the publication hook (publicationReviewHook.ts). Measured over the recorded history (530 records, 512 carrying a verdict): 68 byte-identical pairs, 15 of them same-mode (8 direct→direct, 7 pr→pr) and 10 of those repeating the same verdict — on the order of 10 of 512 reviews, at a measured ~$0.26 and 93s p50 per reviewer call. Proven across two separate processes: a cold 27.0s / 6-model-call review became a 1.58s replay with zero model calls, same verdict. The key carries the base ref and the review mode deliberately, because 15 of the 53 cross-mode repeats in that history flipped their verdict — a CLI review of a tree and a publication review of the same tree are different reviews. What is skipped is the reviewer call; the advisor pass still runs over the reused verdict, so a replay can tighten (approve→revise/reject) exactly as a live run would and the cache cannot fail open past an escalation.
  • The reuse survives a daemon restart. The verdict lives in its own publication_reviews table in the automation database rather than in the in-process reviewedPublications Set it replaces, which was empty after every restart — the failure census records 141 owner_process_exited and 157 shutdown_cancelled, each of which threw away a verdict already paid for. A store that cannot be opened does not suppress a review; the hook reviews instead.
  • openswarm review --max --harness-only runs a deterministic quality harness with no LLM cost. The new harness (src/verify/qualityHarness.ts) enumerates every tracked source file via the git index and scans each one, then runs the discovered typecheck/lint/test/build commands inside the existing isolated verify sandbox. It is fail-closed by construction: a file over the 512 KiB ceiling, one containing NUL bytes, an unreadable path, a symlinked source, or a path that escapes the repository root each become an explicit error finding rather than a silent skip, and a listing that scanned nothing is itself an error — so the gate cannot report "passed" over a subset it never read. Findings are folded into the audit run as a synthetic .openswarm/quality-harness area, which means the markdown report and the exit-code contract carry the evidence on every --max run, not only when an LLM area happened to notice something.

Fixed

  • openswarm review --max no longer changes its verdict with --concurrency. The audit re-partitioned the source into reviewer areas until the fan-out saturated the pool (INT-2249), so the same files and the same reviewer produced 2 areas at concurrency 1 and 10 at concurrency 8 — and because aggregateAuditResults is worst-wins, the finer split could only turn an approve into a revise/reject. The audit's units of judgement now come from planAuditAreas, which is derived from --max-files-per-area alone (--concurrency is purely how many reviewers run at once), so a given file set gets one verdict. The finer, more parallel fan-out is still available, deterministically, by lowering --max-files-per-area; the --fix path keeps the pool-filling balanceAreasToConcurrency, where more areas only mean more parallel fix workers. The cost gate now prints the real agent-run count (the area count) and points at the granularity flag when the pool is under-filled.
  • A workflow execution can no longer be persisted in a state its DAG forbids. saveExecution now rejects a step marked completed without a completedAt, failed without an error, or advanced past a dependency that is still pending, running or failed — states that previously reached disk and were then read back as if the pipeline had progressed. It also gains a definitionStamp fence: a definition replaced underneath a live execution makes the next save refuse rather than record a snapshot that never ran against it.
  • The local issue store no longer serves a stale snapshot after a same-size replacement. The cache stamp was mtimeMs:size, which is unchanged when another process replaces the file by atomic rename within the same mtime tick and writes the same number of bytes — the process then kept serving the old contents indefinitely. The stamp now includes the inode.
  • A status transition's event log records the status the write actually saw. changeStatus and updateIssue read the current status inside the write transaction instead of from a pre-transaction read, so a concurrent transition can no longer stamp a stale oldValue into the audit trail.
  • The daily reporter retries a failed project once, and only advances its watermark when every project succeeded (after that retry). A transient Linear error previously left a project unreported until the next day, while a permanently failing project could hold the window open; the watermark now reflects post-retry reality.
  • A Linear project description keeps its automation summary. The compact [Done:n InProgress:n Todo:n] line was appended before truncation, so a long base description pushed it past Linear's 255-character limit and it was silently cut. The builder now reserves room for the summary first.
  • Repository-registry corruption that could not be quarantined says so. loadRepos reported a failed renameSync as if the corrupt file had been preserved, naming a recovery path that does not exist; the quarantine failure is now surfaced as its own error.
  • A failed local Linear mapping can no longer orphan-recreate the remote issue. If updateIssue/addEvent throws after Linear's createIssue succeeded, the new linearId is remembered in-process and re-persisted on the next call (with an idempotencyKey on the linked event), instead of the retry creating a second Linear issue.
  • Backlog grooming refuses to act on an issue id it was not given. validIssueIds is now mandatory at both parse time and apply time, so a hallucinated or out-of-scope id from the planner is dropped before any mutation path sees it, and an omitted scope means "nothing is in scope" rather than "anything goes".
  • Shell/SQL source under data directories still requires validation evidence. isValidationRelevantFile exempts locale/fixtures/snapshots trees as non-code, but its idea of "code" excluded sh, bash, sql, swift and others that a worker can meaningfully break; those now count.
  • Linear project-overview pagination surfaces a broken page walk. A response with no issues connection, or a missing/repeated endCursor, now throws instead of silently returning a truncated issue list that the overview reports as complete.
  • An idempotent createIssue rejects a materially different artifact under the same id. A caller-supplied id that already exists is treated as a retry only when title, description, parent and project agree; otherwise the store throws an explicit collision error instead of returning the stored row (which masked a re-planned decomposition) or overwriting it (which would destroy the first plan's artifact).
  • The dev.ts close handler always releases its task. Reporting ran before onComplete and activeTasks.delete, so a throw while formatting cost/output left the task registered forever; the reporting is now contained and the cleanup runs in finally.
  • memoryBridge emits one memory_linked event per link, not two (linkMemory already emits one).
  • A failed Linear SDK load is retryable. The rejected init promise stayed cached, so every later initLinearBridge call awaited the same rejection and the bridge never recovered within the process.
  • advisor role: a second, independent review of the same diff. Ported from the harness agent patterns. A separate, independently-prompted model is asked one narrow question — what concrete defects did the reviewer miss? — and its only permitted effect is to make the gate more cautious: it may append concrete findings the reviewer did not report and raise severity, but the merged decision is the max rank of the two (approve < revise < reject), so an advisor approve can never soften a reviewer revise/reject, and a raised severity with no concrete finding is discarded. Any error, timeout, empty, or unparseable output fails open — ran: false, the reviewer's result untouched, no exit code changed. review, review --max (per area), and the --fix re-review loop all run it before dedupe, so its findings are deduped against history like any other. Disabled by default (a second paid call per review), configured under autonomous.defaultRoles.advisor; its model must come from a different family than the reviewer's or it is a second identical opinion.
  • Declarative per-role subagent settings: tools and effort. Each role (worker/reviewer/advisor/…) may declare tools.allow / tools.deny and effort. An allow-list can only narrow the role's default tool set — it can never grant a tool the role would not otherwise have, so a misconfiguration cannot hand a read-only reviewer bash. deny is applied after allow (deny wins) and supports a trailing * (scratch_*). effort selects the native-loop reasoning level for the stage.