Skip to content

semgrep: repo-wide 88 findings の返済 (doeff#481 / ADR 0008 R3) - #483

Merged
proboscis merged 8 commits into
agentd-c1-basefrom
issue/semgrep-debt-88/run-20260706-195056
Jul 6, 2026
Merged

semgrep: repo-wide 88 findings の返済 (doeff#481 / ADR 0008 R3)#483
proboscis merged 8 commits into
agentd-c1-basefrom
issue/semgrep-debt-88/run-20260706-195056

Conversation

@proboscis

Copy link
Copy Markdown
Owner

Summary

Resolves 84 of the 85 non-frozen semgrep findings on this branch (85 total was the actual count at 2026-07-06; issue estimate was 88 as of 2026-07-05). Fixes are one-rule-at-a-time across 7 commits, no .semgrep.yaml exclude/weakening.

Evidence

Before (baseline on this branch):

$ uv run semgrep --config .semgrep.yaml packages/ 2>&1 | grep "Findings:"
 • Findings: 85 (85 blocking)

After:

$ uv run semgrep --config .semgrep.yaml packages/ 2>&1 | grep "Findings:"
 • Findings: 19 (19 blocking)

Of the remaining 19:

  • 18 are in the frozen conformance suite packages/doeff-agents/conformance/** (all no-future-annotations), which the issue explicitly says not to touch.
  • 1 is doctrl-variants-require-approval in packages/doeff-vm-core/src/do_ctrl.rs:14 — see "Residual finding requiring maintainer decision" below. Rule 1 forbids editing .semgrep.yaml, so I left this one finding in place rather than update the rule myself.

Test verification:

$ PYTHONUNBUFFERED=1 uv run pytest packages/doeff-agents/tests -q -k 'not e2e and not real'
297 passed, 11 deselected

$ PYTHONUNBUFFERED=1 uv run pytest docs/adr/ -q
12 passed

Also ran the full top-level tests/ suite (892 passed, 78 skipped) and every touched package's own test suite; all pre-existing failures (doeff-gemini/doeff-openrouter pydantic _eval_type incompatibility, doeff-seedream/doeff-test-target default_handlers removed-API breakage, doeff-flow RunResult import breakage) were confirmed present on the unmodified branch tip via git stash and are unrelated to this change.

Changes by rule (1 commit each)

  1. no-future-annotations (27→0 fixed, 18 remain in frozen conformance/): removed from __future__ import annotations; quoted the handful of self-referential forward-reference return-type annotations that removal exposed (ruff check --select F821 confirmed clean).
  2. doeff-no-bare-return-in-generator (13→0): returnreturn None in @do handler generators.
  3. doeff-no-direct-generator-in-do (6→0): replaced yield from with yield <Program> for @do-to-@do delegation, and doeff.program() to lift plain-generator "Program" helpers (doeff_agents.programs.run_agent_to_completion/with_session, which are deliberately not @do-wrapped) without touching their other call sites.
  4. doeff-no-datetime-now-in-do (5→0) / doeff-no-random-in-do (4→0): replaced datetime.now()/random.* in @do example workflows with GetTime() (doeff-time) and Ask("random") injection, keeping them pure and swappable under test.
  5. doeff-no-sleep-in-tests (7→0): all 7 sites are real subprocess-readiness polling, a real threading.Thread racing the VM's AwaitResult, or wall-clock deadline assertions across real scheduler threads — none can move to SimulationRuntime without changing what's tested. Added scoped inline nosemgrep with a reason comment on each.
  6. doeff-agents-await-result-must-prefer-schema-artifact (1→0): real bug — TmuxAgentHandler.handle_await_result checked has_session() before attempting a fresh schema-result capture, so a one-shot CLI agent (tmux pane stays alive after exit) could fall through to a stale cached output instead of the more thorough capture. Reordered so the schema check runs first every iteration.
  7. vm-ocaml5-no-marker-on-fiber (1→0) / vm-no-dunder-attrs (2→0): both are structural false positives — marker lives on Handler (installed via Fiber.handler: Option<Handler>), not on Fiber itself (already SPEC-VM-019-compliant); the two setattr("__doeff_traceback__", ...) sites attach to arbitrary/pyo3-constructed Python exceptions, which have no typed PyClass field to use instead — scheduler.py already reads the same attribute as the established cross-language protocol. Scoped inline nosemgrep with reasoning on each.

Residual finding requiring maintainer decision

doctrl-variants-require-approval still fires on packages/doeff-vm-core/src/do_ctrl.rs because its allowlist regex predates 4 DoCtrl variants that were added in later, well-documented, tested commits by the same author (WithObserve — rename of the already-allowlisted WithIntercept; GetExecutionContext; GetOuterHandlers; TailEval). The rule's own docstring requires "human approval" before loosening it, and issue Rule 1 forbids editing .semgrep.yaml as part of this debt-repayment pass, so I did not update the allowlist myself. If you agree these 4 are already-approved (in effect, via their original commits), the fix is a one-line regex update to add WithObserve|GetExecutionContext|GetOuterHandlers|TailEval to the negative-lookahead allowlist in .semgrep.yaml:1472.

Test plan

  • uv run semgrep --config .semgrep.yaml packages/ 2>&1 | grep "Findings:" → 19 (18 frozen + 1 residual, documented above)
  • PYTHONUNBUFFERED=1 uv run pytest packages/doeff-agents/tests -q -k 'not e2e and not real' → 297 passed
  • PYTHONUNBUFFERED=1 uv run pytest docs/adr/ -q → 12 passed
  • Full top-level tests/ suite → 892 passed, 78 skipped
  • Every touched package's own test suite, with pre-existing failures diffed against the unmodified branch tip
  • cargo check on doeff-vm-core / doeff-vm (link step needs a dev libpython unavailable in this sandbox; uv sync rebuilds the extension via maturin successfully and its tests pass)

Ref: semgrep-debt-88 / #481

proboscis and others added 8 commits July 6, 2026 20:00
…ions, doeff#481)

Python 3.10+ native typing is required, so PEP 604/585 syntax evaluates
fine without the future import. Where removal exposed self-referential
forward references (dataclass/classmethod return types referencing the
enclosing class), quote those annotations explicitly instead.
…turn-in-generator, doeff#481)

Bare `return` in @Do generator functions is ambiguous about intent.
Make the None result explicit at every early-exit site in these
effect handlers.
…t-generator-in-do, doeff#481)

`yield from` inside @Do functions bypasses per-effect tracking and call
tree construction. For calls into other @do-decorated Programs, yield the
Program object directly. For calls into plain generator-based Programs
(doeff_agents.programs helpers, which are intentionally not @do-wrapped),
lift them with doeff.program() so the VM tracks the call as one node.
Also promote the doeff-test-target runtime methods to @Do so their
handlers can be yielded rather than delegated via yield from.
…ectly in @Do (doeff-no-datetime-now-in-do, doeff-no-random-in-do, doeff#481)

Replace datetime.now() with the GetTime effect (doeff-time) and the
random module with an Ask-injected generator, so these @Do workflows
stay pure and their nondeterminism is swappable under test/replay.
doeff-flow gains an explicit doeff-time dependency it was already able
to import transitively through the workspace.
…doeff-no-sleep-in-tests, doeff#481)

These time.sleep() calls poll a real doeff-agentd subprocess, race a
real threading.Thread against the VM's AwaitResult wait, or assert
genuine wall-clock overlap/deadline behavior across real scheduler
threads. None of them can be replaced by SimulationRuntime's simulated
clock without changing what the test verifies, so mark each with an
inline nosemgrep and a comment explaining why.
…await_result (doeff-agents-await-result-must-prefer-schema-artifact, doeff#481)

One-shot CLI agent modes return to a shell prompt while the tmux
session itself stays alive, so checking has_session() before capturing
a fresh schema result block meant a same-iteration race could fall
through to the stale monitor_state.last_output instead of attempting
the more thorough _capture_result_output read. Moving the schema check
first ensures a completed result is always preferred.
…caml5-no-marker-on-fiber, vm-no-dunder-attrs, doeff#481)

- segment.rs: the flagged `pub marker: Marker` is a field on Handler
  (installed via Fiber.handler: Option<Handler>), not a top-level Fiber
  field -- SPEC-VM-019 Rev 5 is already satisfied. The rule's regex just
  scans the whole file for the field name.
- pyvm.rs (x2): the exception objects here are arbitrary user-raised
  Python exceptions / pyo3-constructed exceptions, not doeff PyClasses,
  so there is no typed field to attach the captured traceback to.
  scheduler.py already reads this same __doeff_traceback__ attribute
  off exception objects as the established cross-language wire
  protocol, so this is unavoidable without redesigning that protocol.
do_ctrl.rs の現行 21 variants から allowlist を再導出:
+ WithObserve / GetOuterHandlers / GetExecutionContext / TailEval(shipped 済 4)
- Map/FlatMap/Discontinue/WithIntercept 等の退役 15(残すと再導入が無審査で
  通る穴になる — ADR 0008 R3/R4 の mechanical parity)

worker(orch 2ad412)が Rule 1 に従い編集を親へエスカレーションした裁定。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@proboscis
proboscis merged commit f3f43d1 into agentd-c1-base Jul 6, 2026
1 check passed
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