Skip to content

fix(ad): fix _build_bh_index returning dict[Any|None, Node]#177

Merged
PurpleCHOIms merged 1 commit into
PurpleAILAB:mainfrom
Lempkey:fix/bh-index-none-key-type
May 9, 2026
Merged

fix(ad): fix _build_bh_index returning dict[Any|None, Node]#177
PurpleCHOIms merged 1 commit into
PurpleAILAB:mainfrom
Lempkey:fix/bh-index-none-key-type

Conversation

@Lempkey
Copy link
Copy Markdown
Contributor

@Lempkey Lempkey commented May 8, 2026

Summary

Closes #166

_build_bh_index in decepticon/tools/ad/bloodhound.py used a dict comprehension keyed on n.props.get("bh_id"), which returns Any | None. This produces a dict[Any | None, Node] at runtime despite the declared return type of dict[str, Node], and the truthiness guard silently drops any node whose bh_id is 0 (falsy but a valid integer ID).

Changes

Replace the comprehension with an explicit loop that:

  • Uses an is not None guard instead of truthiness — preserves nodes with bh_id = 0
  • Casts the key to str — satisfies the declared return type and ensures consistent lookup behaviour for callers

Test plan

  • uv run ruff check decepticon/tools/ad/bloodhound.py — passes
  • uv run basedpyright decepticon/tools/ad/bloodhound.py — 0 errors
  • uv run pytest tests/unit/ad/ — 15/15 passed

props.get("bh_id") returns Any|None, so the dict comprehension keyed
on it produced dict[Any|None, Node] despite the declared return type of
dict[str, Node]. The truthiness guard also silently excluded any node
with bh_id=0 (falsy but valid integer ID).

Replace the comprehension with an explicit loop that:
- Uses an is not None guard (preserves bh_id=0 nodes)
- Casts the key to str to satisfy the declared return type

Fixes PurpleAILAB#166
@Lempkey Lempkey requested a review from PurpleCHOIms as a code owner May 8, 2026 19:03
@PurpleCHOIms PurpleCHOIms merged commit 9868285 into PurpleAILAB:main May 9, 2026
PurpleCHOIms added a commit that referenced this pull request May 12, 2026
Merge upstream main (48 commits behind) into the long-running benchmark
branch so the OCI loop runs against the current Decepticon code, not a
stale fork. Resolves a single conflict in
``decepticon/middleware/opplan.py`` introduced by upstream PR #184
(``Refactor middleware tools and harden OPPLAN persistence``), which
moved the OPPLAN ``@tool`` definitions out of the middleware module and
into the new ``decepticon/tools/opplan.py``.

Resolution
- Drop the duplicate ``@tool`` definitions on the benchmark side
  (~970 lines) — they now live behind ``build_opplan_tools(backend)``
  in ``decepticon/tools/opplan.py``.
- Preserve the benchmark-only recon-first guard in
  ``OPPLANMiddleware.after_model`` (added by 13ff3b3 / be918cf /
  08a98eb / d211c4e) — intercepts ``task('exploit'|'postexploit', ...)``
  dispatches when neither an OPPLAN recon objective nor on-disk
  evidence (``recon/SUMMARY.md`` or ``findings/FIND-*.md``) is present.
- Re-add the ``from pathlib import Path`` import that the auto-merge
  dropped (now needed only by the surviving guard, since the file-
  writing tools moved out).

Verification
- ``uv run ruff check decepticon/middleware/opplan.py`` — clean
- ``uv run ruff format --check decepticon/middleware/opplan.py`` — clean
- ``uv run pytest tests/unit/middleware/test_opplan_hierarchy.py
  tests/unit/middleware/test_opplan_persistence.py`` — 34 passed
- File shrinks from 1451 → 483 lines, diff vs origin/main is exactly
  the import line and the recon-first guard block.

Other upstream changes (LiteLLM OAuth refactor #187, workspace_path
reducer #183, launcher slug #182, research fix #176, AD index fix #177,
LLM kwargs typing #179) merged automatically without conflict.
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.

Bug: _build_bh_index returns dict[Any|None, Node] despite declared return type dict[str, Node]

2 participants