Skip to content

fix: 2026-10-08 bug-hunt findings, one commit per finding - #309

Merged
jkyberneees merged 148 commits into
mainfrom
fix/bughunt-2026-10-08
Oct 9, 2026
Merged

jkyberneees merged 148 commits into
mainfrom
fix/bughunt-2026-10-08

Conversation

@jkyberneees

@jkyberneees jkyberneees commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

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

  • Already-approved project MCP servers re-prompt once (approval key is now length-prefixed).
  • Fixed-time cron jobs follow cron DST rules: once on fall-back, right after a spring-forward gap.
  • Compaction digests are capped at an eighth of the context budget and counted against it.
  • /api/subagents is session-scoped; an operator view needs the instance token.
  • Telegram /resume binds the chat to its canonical tg-<chat> session.
  • odek schedule add and the numeric budget flags parse strictly, like run.
  • Redaction runs an unquoted credential value to the next whitespace.

Coverage (statements, packages that moved)

Package Before After
cmd/odek 71.2% 71.9%
internal/agent 76.4% 77.4%
internal/artifact 89.6% 90.0%
internal/bgproc 88.3% 88.6%
internal/config 85.2% 85.9%
internal/guard 73.5% 74.7%
internal/loop 92.9% 93.1%
internal/mcpclient 93.8% 94.0%
internal/memory 82.5% 82.9%
internal/pathutil 87.1% 88.2%
internal/resource 88.6% 89.2%
internal/runtimelog 87.8% 87.5%
internal/sandbox 74.2% 85.0%
internal/schedule 97.4% 97.0%
internal/session 87.1% 87.5%
internal/skills 90.1% 90.4%
internal/telegram 89.2% 89.9%

15 packages up, 2 down.

Follow-ups

  • Export the provider key env-name table from internal/config (duplicated in internal/agent and cmd/odek).
  • Tool-level approval denials carry no typed error, so a denied plan check from a tool records as failed, not blocked.
  • Telegram end-of-turn saves are not generation-guarded (per-step saves are).

🤖 Generated with Claude Code

jkyberneees and others added 30 commits October 9, 2026 07:15
… 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>
jkyberneees and others added 20 commits October 9, 2026 07:53
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>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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

jkyberneees and others added 7 commits October 9, 2026 08:57
…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>
@jkyberneees
jkyberneees merged commit 984427a into main Oct 9, 2026
10 checks passed
@jkyberneees
jkyberneees deleted the fix/bughunt-2026-10-08 branch October 9, 2026 07:47
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.

1 participant