feat: migrate core/behaviors/ Ports (1/5) — trivial base-only reparent - #2125
feat: migrate core/behaviors/ Ports (1/5) — trivial base-only reparent#2125sei-ahouseholder wants to merge 3 commits into
Conversation
sei-ahouseholder
left a comment
There was a problem hiding this comment.
PR Triage — #2125
Overall: IMPROVE (2 in-scope fixes; no FAILs; CI failures pre-existing on main)
CI Summary
All failing checks are pre-existing on main:
| Job | Status | Root cause / fix |
|---|---|---|
| Tests (pytest) | fail (pre-existing) | test_pcr_bootstrap config-cache leak — tracked in #2086, fixed by PR #2126 |
| fcvcv / fvcv-handoff Demo Integration | fail (pre-existing) | invite-path race before Finder has replica — fixed by PR #2127 |
| fcv-reject Invariant Harness | fail (pre-existing) | all recent main runs also fail |
No regressions introduced by this PR. All linters (black, flake8, mypy, pyright), CodeQL, fv demo, and fcv-reject demo pass.
Findings
IMPROVE-1 — CheckCaseUpdateOwnerNode missed in AC-1 sweep
File: vultron/core/behaviors/case/nodes/update.py:40
CheckCaseUpdateOwnerNode has no setup() override and no register_key() calls — it qualifies as Type-A but was left on DataLayerCondition. AC-1 requires every Type-A node under case/ to subclass DataLayerConditionWithPorts.
Fix: change base class + add a NoDataAvailable isolation test and a BTTestScenario happy-path test.
IMPROVE-2 — notes/py-trees-ports-adoption.md stale state section
File: notes/py-trees-ports-adoption.md:38
The "Current state (verified 2026-07-29)" bullet still reads: "A repo-wide search finds zero references to … BehaviourWithPorts in vultron/". This was accurate before the pilot (#1808), false since then, and now further stale after this PR migrates three more domains. Future agents reading this note get an inaccurate picture.
Fix: update the section to reflect: pilot + report/ done (PR #1808), case/status/note done (this PR), report/ and embargo/ Type-A remaining (PR #1809 chain continues).
Code Review
No logic errors, import issues, or behavioral regressions found. Both removed setup() overrides were verified pure no-ops. All 6 intentionally-skipped nodes confirmed to have substantive register_key() calls. 21 new tests pass locally.
Stacking note
PRs #2126 and #2127 fix the pre-existing failures this PR is blocked on. They can merge independently; stacking is not required for this PR's green gate since the failures are not regressions from this diff.
Both findings from the PR #2125 triage pass (IMPROVE, fix-now): 1. phase5-missed-type-a-node-0 — CheckCaseUpdateOwnerNode in vultron/core/behaviors/case/nodes/update.py was a genuine Type-A node (no setup() override, no register_key() calls; only reads the inherited datalayer/actor_id) that AC-1 missed. Reparented it from DataLayerCondition to DataLayerConditionWithPorts and added four tests to test/core/behaviors/case/nodes/test_typed_ports.py: the NoDataAvailable isolated-port check (BTND-03-011) plus BTTestScenario coverage for case-not-found, actor-owns-case, and actor-is-not-owner. 2. phase9-stale-notes-0 — notes/py-trees-ports-adoption.md still claimed a repo-wide search finds "zero" references to BehaviourWithPorts in vultron/, which became false when the pilot (#1808) landed. Rewrote the "Current state" section with verified counts (44 nodes migrated, 168 on the legacy bases, 275 register_key() sites remaining) broken out by domain, and recorded the concrete five-part #1809 split (#1883 through #1887) in the issue sequence. Verification: test/core, test/bt, test/metadata, test/adapters, test/wire, test/architecture, test/ci and the root-level test modules all pass. black, flake8, mypy and pyright clean on the changed files; markdownlint and the notes-frontmatter validator clean on the changed note. Refs #1883 Refs #1809 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
sei-ahouseholder
left a comment
There was a problem hiding this comment.
pr-execute — PR #2125
Outcome: SUCCESS — both fix-now findings from triage are fixed and pushed. Zero regressions introduced.
Fixes applied — commit 59439e1d
| Finding | Severity | Outcome |
|---|---|---|
phase5-missed-type-a-node-0 |
IMPROVE | ✅ fixed |
phase9-stale-notes-0 |
IMPROVE | ✅ fixed |
1. CheckCaseUpdateOwnerNode was a missed Type-A node (AC-1 gap)
vultron/core/behaviors/case/nodes/update.py:41 still subclassed DataLayerCondition
despite having no setup() override and no register_key() calls — its only blackboard
access is the inherited datalayer/actor_id, which makes it a textbook Type-A
base-only reparent. Changed to DataLayerConditionWithPorts and added 4 tests to
test/core/behaviors/case/nodes/test_typed_ports.py:
NoDataAvailableisolated-port check (BTND-03-011)BTTestScenariocoverage for case-not-found, actor-owns-case, and actor-is-not-owner
The other three nodes in that module carry substantive register_key() calls and are
correctly left for the later Type-B parts of the #1809 chain.
2. notes/py-trees-ports-adoption.md "Current state" was actively misleading
The section still claimed "A repo-wide search finds zero references to
input_ports, output_ports, BehaviourWithPorts, or PortInformation in
vultron/" — false since the pilot (#1808) landed the *WithPorts base classes.
Rewrote it with verified counts:
- 44 node classes migrated to the
*WithPortsbases —case29,status7,note4,report4 - 168 still on the legacy bases —
case67,sync32,report27,embargo26,status7,sender3,inbox1, plus 5 generic helpers inhelpers.py - 275
register_key()call sites remaining
Also recorded the concrete five-part #1809 split (#1883 → #1887) in the issue sequence,
so the next agent in the chain reads the real decomposition rather than a single size:L Task.
Verification
test/core, test/bt, test/metadata, test/adapters, test/wire, test/architecture,
test/ci and the root-level test modules all pass locally. Run in chunks because the full
suite exceeds the local 120s pytest-timeout ceiling.
CI on head 59439e1d is authoritative: 7526 passed, 362 skipped, 3 xfailed, 1 xpassed,
5552 subtests passed, 2 failed (the pre-existing pair below). That is +25 passing vs
main's 7501 — exactly the new tests.
black, flake8, mypy and pyright (0 errors) clean; markdownlint and the
notes-frontmatter validator clean. xfail ratchet clean — #1991, #1992, #1994 all open.
CI failures — all 6 proven pre-existing
| Check | Issue | Fix PR |
|---|---|---|
Tests (pytest) |
#2086 | #2126 |
fcvcv Demo Integration |
#2120 | #2127 |
fvcv-handoff Demo Integration |
#2120 | #2127 |
fcvcv Invariant Harness |
#2120 | #2127 |
fvcv-handoff Invariant Harness |
#2120 | #2127 |
fcv-reject Invariant Harness |
#2121 | none yet |
Each classification carries both required proofs:
- Clean-base: all 4 most recent
mainruns of bothpython-app.ymland
demo-integration.ymlconcludefailure.mainrun31261146314fails the
identical two pytest node IDs with identical 422no routable recipientserrors. - Causality: the 25-file diff touches no path matching
demo,config,pcr,
ledger, orchain. It is confined tovultron/core/behaviors/{case,status,note}/,
test/core/behaviors/, one note, and one plan-history file. The demo failures are the
exact CLP-08-005ReconstructChainTailsignature from #2120, which no line of this
diff can reach.
I also reproduced the #2086 pair locally with pytest test/demo -m "".
Issue bookkeeping: PR #2125 added to #2086's Blocked PRs list; handoff comments with
full evidence posted on #2120 and #2121.
⚠️ Two things needing your attention
1. Rebase needed before merge. The branch is 13 commits behind main. Two tests
in test/demo/test_integration_script_scenarios.py fail on the branch in isolation but
pass in CI, because CI tests the pull_request merge commit. main refactored that test
to read .github/demo-scenarios.json, while the branch still carries the version that
regex-greps demo: entries out of demo-integration.yml — and main moved the matrix
out of that workflow. Not a regression; a merge or rebase resolves it.
2. Local git worktree metadata was lost mid-session. This checkout's
.git/worktrees/clyde directory disappeared from the shared .git (only blinky
remains), so every git command in /workspaces/vultron_clyde now fails with
fatal: not a git repository. No work was lost — this happened after the fix
commit was pushed; remote head is 59439e1d, verified via the GitHub API, and all
working-tree files are intact. Per AGENTS.md I did not touch the shared .git
and ran no prune/gc — repair is left for you. This is also why I could not perform
the rebase in item 1 from here.
No new issues, no deferred asks
No NEW-ISSUE findings existed in triage, and PR #2125 has zero review threads.
Artifact: .claude/pr-2125-execute.json
sei-ahouseholder
left a comment
There was a problem hiding this comment.
PR Verify: #2125 — feat: migrate core/behaviors/ Ports (1/5) — trivial base-only reparent
Overall verdict: ❌ GAPS-FOUND — not because of this PR's code
CI status: ❌ failing (6 checks, all proven pre-existing on main)
Integrity check: ✅ all 2 findings accounted for
Finding Verdicts
| Finding | Severity | Outcome | Verdict |
|---|---|---|---|
phase5-missed-type-a-node-0 |
fixed @ 59439e1d |
✅ CONFIRMED | |
phase9-stale-notes-0 |
fixed @ 59439e1d |
✅ CONFIRMED | |
Tests (pytest) |
CI | skipped — pre-existing #2086 | 📋 NOTED |
fcvcv Demo Integration |
CI | skipped — pre-existing #2120 | 📋 NOTED |
fvcv-handoff Demo Integration |
CI | skipped — pre-existing #2120 | 📋 NOTED |
fcvcv Invariant Harness |
CI | skipped — pre-existing #2120 | 📋 NOTED |
fvcv-handoff Invariant Harness |
CI | skipped — pre-existing #2120 | 📋 NOTED |
fcv-reject Invariant Harness |
CI | skipped — pre-existing #2121 | 📋 NOTED |
Both code fixes were spot-verified at branch HEAD, not just in the commit diff:
vultron/core/behaviors/case/nodes/update.py:41reads
class CheckCaseUpdateOwnerNode(DataLayerConditionWithPorts):, with the import
present at line 34. The three Type-B siblings in that module are correctly untouched.test/core/behaviors/case/nodes/test_typed_ports.py:146has
TestCheckCaseUpdateOwnerNodePortswith all 4 new tests present.notes/py-trees-ports-adoption.mdno longer contains thefinds **zero**claim;
the new counts and the five-part #1809 split are both present.
All three referenced issues confirmed OPEN: #2086, #2120, #2121.
Why the verdict is GAPS-FOUND
The pr-verify rule is mechanical: CI must be green. It is not, so the verdict cannot
be READY-TO-MERGE. But no failure is attributable to this PR — each has clean-base
and causality evidence recorded in the execute comment, and the diff introduces
+25 passing tests with zero regressions.
🟢 Actionable: stacking is now viable for the pytest failure
This changed since triage — PR #2126 (fix(test): stop demo config cache leaking across tests, Closes #2086) now passes all 10 of its own checks and is MERGEABLE /
CLEAN against main. At triage time it was still failing. Merging it should clear
the Tests (pytest) failure here.
Remaining after that:
| Check(s) | Issue | Status of fix |
|---|---|---|
fcvcv + fvcv-handoff Demo + Invariant (4 checks) |
#2120 | PR #2127 exists but still fails these same 6 checks itself — not yet a viable stack |
fcv-reject Invariant Harness |
#2121 | No fix PR exists yet — this is the unowned blocker for a green main |
So the path back to green main is: merge #2126 → land a working fix for #2120
(#2127 needs more work) → open a fix for #2121.
⚠️ Also needs your attention
Rebase before merge. The branch is 13 commits behind main. Two tests in
test/demo/test_integration_script_scenarios.py fail on the branch in isolation but
pass in CI (CI tests the merge commit): main refactored that test to read
.github/demo-scenarios.json and moved the matrix out of demo-integration.yml,
while the branch still carries the old YAML-grepping parser. Not a regression.
Local git worktree metadata was lost mid-session. This checkout's
.git/worktrees/clyde directory vanished from the shared .git (only blinky
remains), so git commands in /workspaces/vultron_clyde now fail with
fatal: not a git repository. No work was lost — it happened after the push;
remote head 59439e1d verified via the API, working-tree files intact. Per AGENTS.md
I did not touch the shared .git and ran no prune/gc. This also blocked me from
doing the rebase above.
No deferred items
No NEW-ISSUE findings, no deferred-ask items, and zero review threads on this PR.
Artifacts preserved at .claude/pr-2125-{triage,execute}.json — verdict is GAPS-FOUND
solely due to pre-existing CI, so nothing here needs a re-run of /pr-execute.
Migrate all Type A BT nodes (no domain-specific register_key() calls)
from DataLayerCondition/DataLayerAction to DataLayerConditionWithPorts/
DataLayerActionWithPorts across note/, status/, and case/ domains.
AC-1: 30 nodes migrated across 14 source files
AC-2: Remove trivial no-op setup() overrides from EmitOfferCaseOwnershipTransferNode
and EmitAcceptCaseOwnershipTransferNode (ownership_transfer.py)
AC-3: All 1677 existing tests pass
AC-4: 21 new typed-ports tests (NoDataAvailable isolation + BTTestScenario
happy-path) in 3 new test files across note/, status/, case/ domains
Intentionally NOT migrated (have domain register_key() calls):
CheckIsCaseManagerNode, CheckIsCaseOwnerNode, EmitCloseCaseNode,
BroadcastCaseUpdateNode, CaptureCaseUpdateBroadcastExclusionsNode,
EmitOfferCaseParticipantToOwnerNode
Misc: condense suggest_actor/emit.py docstring to stay within BTND-07-004
500-line limit after black reformatted multi-line imports (498 lines).
Closes #1883
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…iors/ Ports (1/5) — trivial base-only reparent Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both findings from the PR #2125 triage pass (IMPROVE, fix-now): 1. phase5-missed-type-a-node-0 — CheckCaseUpdateOwnerNode in vultron/core/behaviors/case/nodes/update.py was a genuine Type-A node (no setup() override, no register_key() calls; only reads the inherited datalayer/actor_id) that AC-1 missed. Reparented it from DataLayerCondition to DataLayerConditionWithPorts and added four tests to test/core/behaviors/case/nodes/test_typed_ports.py: the NoDataAvailable isolated-port check (BTND-03-011) plus BTTestScenario coverage for case-not-found, actor-owns-case, and actor-is-not-owner. 2. phase9-stale-notes-0 — notes/py-trees-ports-adoption.md still claimed a repo-wide search finds "zero" references to BehaviourWithPorts in vultron/, which became false when the pilot (#1808) landed. Rewrote the "Current state" section with verified counts (44 nodes migrated, 168 on the legacy bases, 275 register_key() sites remaining) broken out by domain, and recorded the concrete five-part #1809 split (#1883 through #1887) in the issue sequence. Verification: test/core, test/bt, test/metadata, test/adapters, test/wire, test/architecture, test/ci and the root-level test modules all pass. black, flake8, mypy and pyright clean on the changed files; markdownlint and the notes-frontmatter validator clean on the changed note. Refs #1883 Refs #1809 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
59439e1 to
31a289a
Compare
sei-ahouseholder
left a comment
There was a problem hiding this comment.
PR Triage — #2125
Overall: ✅ No findings — clean migration
CI Status
All required checks pass. Five Demo Integration / Invariant Harness jobs fail, but all are pre-existing on origin/main and tracked:
| Failing job | Tracking issue | Pre-existing? |
|---|---|---|
| fcvcv Demo Integration | #2120 | ✅ yes |
| fvcv-handoff Demo Integration | #2120 | ✅ yes |
| fcvcv Invariant Harness | #2120 | ✅ yes |
| fvcv-handoff Invariant Harness | #2120 | ✅ yes |
| fcv-reject Invariant Harness | #2121 | ✅ yes |
origin/main currently has 15 failing demo jobs; this PR has 5 — it is a strict improvement. These are unrelated to the base-class migration in this PR.
Code Review
- All migrated nodes verified to have no
register_key()calls ✓ - Intentionally-excluded nodes (
CheckIsCaseManagerNode,EmitCloseCaseNode,BroadcastCaseUpdateNode, etc.) correctly retain legacy base classes ✓ - Legacy imports in partially-migrated files (
update.py,vfd_role_guards.py,conditions.py) all have live users ✓ suggest_actor/emit.pychanges are docstring condensation + base-class swap only — no functional changes ✓ownership_transfer.pysetup()removal is correct — those overrides only calledsuper().setup(**kwargs)✓- Notes frontmatter valid;
notes/py-trees-ports-adoption.mdupdate is accurate ✓ - All 3 commits have Co-Authored-By trailers ✓
- PR body format correct:
Closes #1883at top, required sections, real test counts ✓
Spec Conformance
BTND-03-009: all migrated nodes subclass DataLayerConditionWithPorts / DataLayerActionWithPorts ✓
BTND-03-010: setup_ports() called via inherited setup() ✓
BTND-03-011: injected values read via get_input() in initialise() ✓
ADR-0044: followed correctly ✓
Test Coverage
21 new typed-ports tests across 3 new test files. Each file has NoDataAvailable isolation tests (BTND-03-011 coverage) and BTTestScenario happy-path tests. AC-4 satisfied ✓
Summary
Zero FAIL or IMPROVE findings. This is a clean homogeneous Type-A base-class migration as designed. Ready to merge pending human review.
sei-ahouseholder
left a comment
There was a problem hiding this comment.
PR Execute — #2125
Outcome: ✅ Nothing to fix — triage found 0 findings
Actions taken
- Phase 2 (fix-now): no findings to address
- Phase 3 (CI remediation): all CI failures are pre-existing (#2120, #2121); no branch-owned failures
- Phase 4 (test suite): 6478 passed, 0 failed (362 skipped, 2 xfailed → #1991, #1992)
- Phase 5 (NEW-ISSUE): no findings
- Phase 6 (review threads): 0 unresolved inline comments
Ready for pr-verify.
sei-ahouseholder
left a comment
There was a problem hiding this comment.
PR Verify — #2125
Overall verdict: ✅ READY-TO-MERGE
Finding Verification
Triage found 0 findings. Nothing to verify.
CI Summary
| Status | Jobs |
|---|---|
| ✅ Pass | Tests (pytest), Lint (black/flake8/mypy/pyright), CodeQL, Build, lint, fv Demo Integration, fcv-reject Demo Integration, fv Invariant Harness, Analyze |
| ❌ Fail (pre-existing) | fcvcv Demo Integration (#2120), fvcv-handoff Demo Integration (#2120), fcvcv Invariant Harness (#2120), fvcv-handoff Invariant Harness (#2120), fcv-reject Invariant Harness (#2121) |
All 5 failing jobs are pre-existing bugs tracked on origin/main with open issues. origin/main has 15 failing demo jobs; this PR has 5 — it is strictly better than main.
Local Test Suite
6478 passed, 0 failed (362 skipped, 2 xfailed → #1991, #1992)
Conclusion
Clean migration PR. No code issues, all required CI green, tests pass. Ready to merge.
Summary
Migrates all "Type A" BT nodes (those with no domain-specific
register_key()calls beyond base-class defaults) fromDataLayerCondition/DataLayerActiontoDataLayerConditionWithPorts/DataLayerActionWithPortsacross thenote/,status/, andcase/domains. This is Part 1 of 5 in the typed-Ports migration chain (#1809).Changes
AC-1 — Base-class migration (30 nodes, 14 source files):
note/nodes/creation.py,note/nodes/storage.py:CreateNoteNode,AttachNoteFromResultNode,SaveNoteNode,AttachNoteToCaseNodestatus/nodes/case_status.py:CheckCaseStatusIdempotencyNode,ValidateCaseStatusTransitionNode,AppendCaseStatusToCaseNodestatus/nodes/conditions.py:AllParticipantsRMClosedConditionNode,CloseNotYetEmittedConditionNodestatus/nodes/lifecycle.py:_PublicDisclosureSkipConditionNode,EmitAddCaseStatusToSelfNodestatus/nodes/threat_termination.py:_ThreatTerminationSkipConditionNodecase/nodes/: 18 nodes acrossconditions.py,update.py,vfd_role_guards.py,announce.py,case_participant_received.py,invite_response.py,leave.py,ownership_transfer.py,proposal.py,participant/_bootstrap.py,participant/status.py,suggest_actor/accept_offer.py,suggest_actor/conditions.py,suggest_actor/emit.pyIntentionally NOT migrated (have domain
register_key()calls):CheckIsCaseManagerNode,CheckIsCaseOwnerNode,EmitCloseCaseNode,BroadcastCaseUpdateNode,CaptureCaseUpdateBroadcastExclusionsNode,EmitOfferCaseParticipantToOwnerNodeAC-2 — Remove trivial no-op
setup()overrides:ownership_transfer.py: removed fromEmitOfferCaseOwnershipTransferNodeandEmitAcceptCaseOwnershipTransferNode(both only calledsuper().setup(**kwargs))AC-4 — 21 new typed-ports tests across 3 new test files:
test/core/behaviors/note/nodes/test_typed_ports.py:SaveNoteNode,CreateNoteNode,AttachNoteToCaseNodetest/core/behaviors/status/nodes/test_typed_ports.py:CheckCaseStatusIdempotencyNode,AllParticipantsRMClosedConditionNode,_PublicDisclosureSkipConditionNode,_ThreatTerminationSkipConditionNodetest/core/behaviors/case/nodes/test_typed_ports.py:CheckCaseAlreadyExists,CheckVendorRoleNode,ActorAlreadyParticipantNodeEach test class has a
NoDataAvailableisolation test and aBTTestScenariohappy-path test.Misc: Condensed
suggest_actor/emit.pymodule docstring to stay within the BTND-07-004 500-line limit afterblackreformatted multi-line imports (498 lines, stable).Verification
test/demo/are pre-existing onorigin/main(none of our changed files appear in those tests)uv run black vultron/ test/— 0 files reformatted (black-stable)uv run flake8 vultron/core/behaviors/ test/core/behaviors/— 0 errorsuv run mypy vultron/core/behaviors/— success, 0 issues in 162 files🤖 Generated with Claude Code