Repository navigation
fix: 2026-10-08 bug-hunt findings, one commit per finding - #309
Merged
Merged
Conversation
… path WithinRoot appended a separator to the resolved root before the prefix check. For root "/" that produced "//", so no candidate matched and every path was rejected. The separator is now appended only when the root does not already end with one. Finding F-093 from the 2026-10-08 bug hunt. Coverage pathutil: 87.1% -> 88.2%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
fallback() fell back to the local guard only when FallbackToLocal was explicitly true, while the documented default and isFallbackEnabled treat nil as true. A piguard config without the field therefore returned a "sidecar unavailable" error instead of the local guard. fallback() now uses isFallbackEnabled, so only an explicit false keeps the error. Finding F-038 from the 2026-10-08 bug hunt. Coverage guard: 73.5% -> 74.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Load cut file content at byte 51200 with a plain slice, so a multi-byte rune straddling the limit was split and the model received invalid UTF-8. The cut now backs up to the nearest rune start (utf8.RuneStart) before the truncation notice is appended. Finding F-094 from the 2026-10-08 bug hunt. Coverage resource: 88.6% -> 88.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ParseRefs ended a reference only at whitespace and closing brackets, commas and semicolons. A sentence-final ".", "?", "!" or ":" was kept in the reference, so "review @main.go." looked up a file named "main.go." and the reference was left unexpanded. The trailing run of such characters is now dropped from the token, and the span is trimmed to match. The path always keeps at least one character. Finding F-095 from the 2026-10-08 bug hunt. Coverage resource: 88.7% -> 89.2%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Install saved the previous handler and its restore function wrote that value back. Removing observers out of installation order therefore reinstated an observer that had already been removed. Observers now form a chain. Removing the active observer falls back past any entries that were already removed, and removing an inactive observer only marks it, so removal order no longer matters. Finding F-082 from the 2026-10-08 bug hunt. Coverage diagnostics: 100.0% -> 100.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The generic key/password matcher limited values to [a-zA-Z0-9+/=._-], so a special character in the first 20 characters skipped the match entirely and a later one left the tail in clear text. Quoted values now run to the closing quote and unquoted values to the next whitespace, quote or separator. Finding F-009 from the 2026-10-08 bug hunt. Coverage redact: 92.2% -> 92.2% (regex-only change; table tests added). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The generic matcher required the key name to be followed directly by [:=], so "password": "..." never matched. An optional closing quote after the key is now accepted. Finding F-010 from the 2026-10-08 bug hunt. Coverage redact: 92.2% -> 92.2% (regex-only change; table tests added). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The PEM and PGP body class [^-]* stopped at the first dash, so legacy encrypted blocks with Proc-Type/DEK-Info headers escaped redaction. The body is now matched lazily up to the first END marker. Finding F-053 from the 2026-10-08 bug hunt. Coverage redact: 92.2% -> 92.2% (regex-only change; tests added). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
InjectFiles only Lstat'ed the final path component, so a file reached through an in-cwd directory symlink pointing outside cwd was copied into the container. In-cwd paths are now resolved with pathutil.ResolveDirSymlinks and skipped when they land outside the resolved working directory. Finding F-054 from the 2026-10-08 bug hunt. Coverage sandbox: 74.2% -> 81.4%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
isPathUnder rejected any relative path starting with "..", so a real directory such as "..cache" inside the workdir was refused as a volume mount. Containment now fails only when the first component is exactly "..". Finding F-097 from the 2026-10-08 bug hunt. Coverage sandbox: 81.4% -> 82.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
In sandbox mode the group signal reaches only the host-side docker-exec client, and the follow-up pidfile kill ran only after a forced stop, so a container-side child left by a job that exited normally kept running past the job and its session. The follow-up now runs after every exit. The wrapSandboxCommand wrapper in cmd/odek/shell.go no longer removes the pidfile itself (minimal edit); the follow-up removes it. Finding F-002 from the 2026-10-08 bug hunt. Coverage bgproc: 88.5% -> 88.5%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
wait() ranked the process exit code above the pending forced-stop reason, so a job whose shell trapped TERM and exited 0 after Stop or the timeout timer was reported as a successful "exited". A clean exit with a pending forced stop now reports the forced reason; non-zero exits keep their failed status. Finding F-025 from the 2026-10-08 bug hunt. Coverage bgproc: 88.5% -> 88.6%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Consolidate and ApplyConsolidation only ran the content scan on LLM output, so a download-and-run entry could reach the always-injected fact files. Both now run FactLooksUnsafe like AddFact and ReplaceFact. Finding F-048 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.6% -> 82.6%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ator An LLM-produced entry carrying the on-disk separator split into extra entries on write, defeating the never-more-than-original-count guard. Consolidate now drops such entries and ApplyConsolidation refuses them, matching Add/Replace. Finding F-090 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.6% -> 82.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
lockFactsDir opened facts.lock without creating the directory, so the first AddFact on a fresh install failed. It now creates the directory (0700). Finding F-049 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.7% -> 82.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…call Consolidate took the facts lock before the LLM call and held it until the write, stalling every fact write sharing the directory for the LLM latency. It now snapshots, calls the LLM unlocked, then locks and writes only if the file is unchanged (a concurrent write skips the merge). The RED test now waits for the background Consolidate before TempDir cleanup. Finding F-051 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.7% -> 82.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
truncateForIndex sliced the summary at byte 117, cutting multibyte runes and persisting U+FFFD in the index that is injected into prompts. Finding F-091 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.7% -> 82.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Episode summaries are LLM output over the whole transcript and were stored and replayed without any scan. A summary the injection guard rejects is now stored tainted (untrusted, never auto-approved) so only a human promote recalls it. Finding F-050 from the 2026-10-08 bug hunt. Coverage internal/memory: 82.7% -> 82.8%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
addAtoms overwrote every atom's session and project with the shared mutable fields, so an in-flight background extraction could stamp the wrong session after SetSessionContext moved on. It now only fills fields the caller left empty. Finding F-052 from the 2026-10-08 bug hunt. Coverage internal/memory/extended: 90.7% -> 90.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ReturnAfterBreak returned raw LLM output wrapped in a WHERE YOU LEFT OFF block for the system prompt. It now runs the injection guard like AnaphoraResolve and drops a rejected summary. Finding F-092 from the 2026-10-08 bug hunt. Coverage internal/memory/extended: 90.7% -> 90.7%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The stdio readLoop decoded a missing or null id as 0 and routed it to the in-flight call with id 0 (initialize). Frames without an id are now dropped and ids start at 1. Finding F-047 from the 2026-10-08 bug hunt. Coverage mcpclient: 93.8% -> 93.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…v overrides isSensitiveEnvVar missed these secret-shaped names, so cfg.Env overrides carrying them reached the MCP child. Finding F-087 from the 2026-10-08 bug hunt. Coverage mcpclient: 93.8% -> 93.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e length applyResultLimit returned the full truncation notice even when it exceeded the cap; it is now cut to the cap like the envelope path. Finding F-089 from the 2026-10-08 bug hunt. Coverage mcpclient: 93.8% -> 93.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…artifact lines Trailing non-envelope items were appended verbatim after Render, so a line starting with '- artifact ' counted as a real metadata entry. They now pass through artifact.SanitizeText. Finding F-088 from the 2026-10-08 bug hunt. Coverage mcpclient: 93.8% -> 94.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… rendered metadata oneLine only replaced CR and LF, so media_type and summary carried ESC, NUL, bidi overrides and U+2028 into the model-facing line. Control, format and Unicode line/paragraph separator characters now become spaces. Finding F-024 from the 2026-10-08 bug hunt. Coverage artifact: 89.6% -> 90.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…s the cwd filepath.Abs of an empty string is the working directory, so roots [""] bypassed the empty-roots fail-closed rule. Blank entries are now skipped and confine nothing. Finding F-077 from the 2026-10-08 bug hunt. Coverage artifact: 89.6% -> 90.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
New only read DEEPSEEK_API_KEY/OPENAI_API_KEY for the deepseek provider, so library embedders using openai, anthropic, gemini, zai or kimi got 'no API key' despite the documented provider env fallback. Finding F-076 from the 2026-10-08 bug hunt. Coverage agent: 76.4% -> 77.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The verify-model client kept the zero temperature, which sends an explicit 0 even when the config asked to omit it, so reasoning-model gateways rejected the side call and verification silently never ran. Finding F-023 from the 2026-10-08 bug hunt. Coverage agent: 76.4% -> 77.4%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…rune pruneFile used a 1 MiB bufio.Scanner token, so a single longer line aborted every sweep with 'token too long' and expired records were never removed. Oversized lines are now streamed through unchanged and the sweep continues, matching logquery. Finding F-096 from the 2026-10-08 bug hunt. Coverage runtimelog: 87.8% -> 87.5% (the replaced scanner error branch is gone; every new branch is exercised). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…a spring-forward gap Next walked real instants, so a fixed time inside a repeated hour fired twice and a wall time inside a gap was skipped for the whole day. Jobs whose minute and hour fields are not wildcards now resolve civil wall times like Vixie/cronie: first occurrence only, and the first minute after the gap. Wildcard jobs keep the minute walk. The minute-walk oracle test now covers wildcard expressions only; fixed-time expressions have explicit DST cases. Findings F-098 and F-099 from the 2026-10-08 bug hunt (one shared fix). Coverage schedule: 97.4% -> 97.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The classifier resolves path targets through symlinks, but compared the resolved path with $HOME as spelled. On macOS /home is a symlink into /System/Volumes/Data, so with HOME=/home/user a resolved /System/Volumes/Data/home/user/.bashrc no longer sat under the home, and writes to the user's rc files, credential directories and odek anchors through output options such as `git archive --output=$HOME/.bashrc` fell to local_write (TestRED_Paths_OptionEqualsDestinationSpellingsSkipped failed on macOS while passing on Linux). The same applies on any host whose home is a symlink to another volume. currentHomeDirs now returns both the spelled and the physical home, and the account-home, own-home and system-path checks use both. Finding PRE-3 from the 2026-10-08 bug hunt. Coverage internal/danger: 92.0% -> 92.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ut statting it isDirectoryDestination documents the caller's home as a directory destination but only stat-checked the path, so `cp evil/.bashrc ~` was a plain local_write whenever the home directory could not be statted as a directory under the spelling the command used (HOME=/home/user is a symlink on macOS and absent on many CI hosts), and the rc file landing at <home>/.bashrc was never seen as persistence (TestRED_Paths_RcFileCopiedIntoHomeDirectory failed on macOS while passing on Linux). A destination equal to the current home, in either spelling, is now a directory outright. Finding PRE-2 from the 2026-10-08 bug hunt. Coverage internal/danger: 92.0% -> 92.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
accountHomes knew the superuser's home only as /root. On macOS ~root expands to /var/root, so `cp x ~root/.bashrc` was system_write instead of persistence and TestOptSpecGoldenEffects, whose golden corpus encodes the Linux result, failed there. /var/root (and its /private/var/root spelling, stripped before the check) is now an account home, so its shell rc files, systemd units and credential directories get the same protected-path rules as /root. Finding PRE-1 from the 2026-10-08 bug hunt. Coverage internal/danger: 92.0% -> 92.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…fields oneLine replaced every Unicode format character with a space, which broke Persian/Indic shaping and emoji ZWJ sequences. It now flattens only control characters, bidi controls, line/paragraph separators and the BOM. Review finding on the 2026-10-08 bug-hunt branch. Coverage artifact: 90.0% -> 90.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…n notice A max_result_chars smaller than the truncation notice produced an empty or notice-only result with no operator hint. The effective cap is now raised at resolution time to the notice length plus 64 runes, with one startup warning naming the server. The cap still holds absolutely. Review finding on the 2026-10-08 bug-hunt branch. Coverage mcpclient: 94.0% -> 94.2%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ct lines SanitizeText covered only trailing extras beside an envelope, so a plain single-item, multi-item or isError text could still start a line with '- artifact ' and inflate artifact_count. All text paths now indent such lines. The untrusted wrapper behavior is unchanged. Review finding on the 2026-10-08 bug-hunt branch. Coverage mcpclient: 94.2% -> 94.0% (run-to-run variance in concurrent paths). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A skip record kept the previous LastError beside StatusSkipped, contradicting the RunState contract and showing a stale error in the Telegram job view. The skip now keeps LastRun and LastResult, clears LastError, and records the skip time in SkippedAt; the Telegram view shows when the skip happened and the real last run. Review finding on the 2026-10-08 bug-hunt branch. Coverage schedule: 97.0% -> 97.0%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ng a garbage ref A long-form spec whose first key is misspelled (knd=x,uri=...) parsed as the kind=uri shorthand and stored the remaining pairs as the URI. A shorthand whose URI contains ,kind= / ,uri= / ,created_by= / ,read_only= is now rejected with an error naming the unknown leading key. URIs with ordinary commas still pass. Review finding CR-2 on the 2026-10-08 bug-hunt branch. Coverage cmd/odek: 71.8% -> 71.8%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Sub-agent ownership was resolved at query time from live WebSocket connections and in-memory headless runs, so after a reload, reconnect or session_switch the entries belonged to no session: they vanished from /api/subagents and could not be cancelled. Each registry entry now carries the owning session id, stamped when delegate_tasks spawns the task, and listing and subagent_cancel scope by it; entries without a recorded owner fall back to the live-connection resolution. GET /api/subagents without session_id is the operator view of all runs, gated on the instance token header and a local origin. Review finding CR-1 on the 2026-10-08 bug-hunt branch. Coverage cmd/odek: 71.8% -> 71.8%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…add tasks The error for a flag-shaped token after the task now points at both ways out (quote the whole task as one argument, or put it after --), and docs/SCHEDULES.md explains the rule with examples. Review finding CR-4 on the 2026-10-08 bug-hunt branch. Coverage cmd/odek: 71.8% -> 71.8%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…tion window Trust granted on the third same-class approval within 60s is overridden by friction until the window expires. Review finding CR-3 on the 2026-10-08 bug-hunt branch. Coverage cmd/odek: 71.8% -> 71.8% (docs only). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two preceding tools commits (json_query wrap budget sized from the real wrapper cost; credential env names matched by whole component in list_tools) landed with their tests only: the source edits had been swept into a stash used to prove the tests RED and were not restored before committing. This commit carries the source changes those tests cover. Behavior is as described in those commits. Review findings on the 2026-10-08 bug-hunt branch. Coverage cmd/odek: 71.8% -> 71.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… long options targetDirectoryOption only recognised a bare -t, a leading -tDIR and the exact --target-directory spelling, so cp -at, cp -rt, install -Dt and --target=DIR overwrote a licensed script without dropping its read licence. -t is now found at any position of a short-flag cluster (stopping at options that consume the rest of the cluster) and the long option resolves through a shared unambiguous-prefix helper. Review finding DR-2 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… and \copy program psql and mysql escapes were inspected only in argument text. A static echo/printf pipe into the client now has its decoded text scanned with the same escape pattern, \copy ... program (which runs a local program without a pipe character) is a shell escape, and psql -f FILE is a program-file operand so the unread-script gate sees the script whose meta-commands run. Review finding DR-3 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The memo that skips repeated command lines and stages ignored the nesting depth, so text first reached near the substitution depth limit (where its nested payloads are cut off) was recorded as scanned and a later shallow visit of the same text, where those payloads are reachable, was skipped. A visit now counts as complete only when the depth limit refused nothing while it ran; otherwise a shallower repeat is scanned again. Repeated find -exec operands stay bounded. Review finding DR-1 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The shell-escape pattern for psql/mysql matched \! and the words system and pager anywhere in the text, so a string literal or an ordinary query such as select * from system where x=1 classified as code_execution. The escape text is now scanned after SQL string literals, quoted identifiers, dollar-quoted bodies and comments are removed (every comment reading is tried and an unterminated quote is kept, so an apostrophe cannot hide a real escape), and system/pager count only at the start of a statement. The backslash of \! is also kept inside double quotes, where normalization used to fold it away and hide psql -c "\! id". Review finding DR-4 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
cpio --rs=CMD and zip --unzip-comm=CMD were not recognised as program-running options although GNU option parsing accepts any unambiguous prefix. zip, cpio and the -t DIR detection now share one resolver that matches an exact name or a prefix naming exactly one option of the tool, so an ambiguous prefix (cpio --re) still names nothing. Review finding DR-5 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…re-strings The denylist read the text a static echo/printf feeds a shell only from the stage immediately before the shell, so echo 'git push' | tee f | sh and cat <<< 'git push' | sh hid denied text. The scan now walks back from the shell through stages that hand their input on unchanged (cat, tee, sort, uniq, tac, head, tail, flags and head/tail counts only, no file operands) to the static producer, and a here-string on such a stage is itself the producer. Stages that rewrite or replace the data (grep, tr, sed, cat FILE, ...) stop the walk. Review finding DR-6 on the 2026-10-08 bug-hunt branch. Coverage internal/danger: 92.1% -> 92.1%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
odek | 298f4cc | Commit Preview URL Branch Preview URL |
Oct 09 2026, 07:42 AM |
…pound staticcheck QF1001 flagged the negated compound condition in isFenceTag; the character classes are now a small predicate. No behavior change. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
TestMediaCredentialCheck_AllowsOrdinaryFiles listed ~/.docker/other.json as an ordinary file, but the danger classifier rejects everything under ~/.docker as a credential directory. The case only passed on macOS, where the temp home is exempt. The lookalike now lives outside any credential directory; the behavior under test is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
clampProjectBackground replaced any project value <= 0 with the global value, but 0 for max_wakes_per_hour means wakes disabled, the strictest setting. max_wakes_per_hour now accepts 0 and rejects only negative values; max_jobs, max_output_bytes and max_timeout_seconds keep the non-positive rejection. Warning text and docs/CONFIG.md updated. Review finding OR-4 on the 2026-10-08 bug-hunt branch. Coverage internal/config: 85.9% -> 85.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lid JSON
The redactor's unquoted-value branch runs to the next whitespace, so
applied to raw argument text it swallowed closing quotes and braces and
persisted invalid JSON, which made a reloaded session fail every
Anthropic/Gemini request. Valid JSON arguments are now unmarshalled,
every string value and key redacted, and re-marshalled. Non-JSON
arguments are redacted as text; if that is still not valid JSON the
arguments become {"redacted":true} with the original dropped.
Review finding OR-2 on the 2026-10-08 bug-hunt branch.
Coverage internal/session: 87.4% -> 87.5%.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…/resume The /resume generation guard covered only per-step saves. The running turn's final Save and cancel-path Save went through the chat cache, which after /resume holds the resumed session, so the old transcript overwrote the resumed conversation. Add SaveAt/SaveCheckpointAt returning ErrStaleGeneration, use SaveAt at both call sites with the turn's captured generation, and log the dropped save. handleChatMessage needs a live Telegram bot, so the call sites are not exercised in cmd/odek; the internal/telegram test follows its call order. Review finding OR-3 on the 2026-10-08 bug-hunt branch. Coverage internal/telegram: 89.8% -> 89.9%. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
ConfigFromEnv discarded a whole ODEK_TELEGRAM_ALLOWED_* value when any
entry was malformed and kept the config-file base list, which could be
wider than the valid part ('111, 222x' on [111,222,333] kept 333). The
valid entries now replace the base list, with a warning naming the bad
entry. A non-empty value with no valid entry returns an error naming the
variable and first bad entry; ConfigFromEnv now returns (cfg, error) and
`odek telegram` refuses to start on it. The config loader (no error
channel) keeps the base list and relies on that startup check.
Review finding OR-5 on the 2026-10-08 bug-hunt branch.
Coverage internal/telegram: 89.9% -> 89.9%.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
GNU parallel and sem join their command words and run the line through a
shell unless -q/--quote is given, so a quoted separator word ('; ', '&&',
'|') in them is a real separator. The literal-operator mark made those
words plain arguments, so `parallel echo ';' rm -rf ~ ::: 1` (and the same
behind a feeding pipe) dropped from destructive to safe. The words up to
the first input-source marker are now joined and analysed as a shell
payload, like watch; -q/--quote keeps them literal. sem joins the wrapper
table with an unknown floor so unwrapping it never lowers a verdict.
Review finding OR-1 on the 2026-10-08 bug-hunt branch.
Coverage: internal/danger 92.1% -> 92.1%.
Co-Authored-By: Claude Fable 5.1 <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
Fixes from the 2026-10-08 module-by-module bug hunt: 105 replay-confirmed findings plus 3 pre-existing macOS test failures in
internal/danger, one commit per finding (RED test, fix, coverage, docs). Per-area adversarial reviews on Sonnet/Haiku raised 36 follow-ups; 26 are fixed here, the rest are documented as intended behavior.Full report:
.claude/bughunt-2026-10-08/in the repo (gitignored) and the published bug-hunt page.Behavior changes
/api/subagentsis session-scoped; an operator view needs the instance token./resumebinds the chat to its canonicaltg-<chat>session.odek schedule addand the numeric budget flags parse strictly, likerun.Coverage (statements, packages that moved)
15 packages up, 2 down.
Follow-ups
internal/config(duplicated ininternal/agentandcmd/odek).🤖 Generated with Claude Code