Skip to content

Fix: NIC child presence no longer forces a directly-detected parent offline (#1736) - #1739

Merged
jokob-sk merged 5 commits into
next_releasefrom
copilot/fix-nic-child-relationship-issue
Aug 13, 2026
Merged

Fix: NIC child presence no longer forces a directly-detected parent offline (#1736)#1739
jokob-sk merged 5 commits into
next_releasefrom
copilot/fix-nic-child-relationship-issue

Conversation

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

update_devPresentLastScan_based_on_nics unconditionally replaced the parent's devPresentLastScan with the NIC-derived value, discarding the parent's own direct detection. When a parent was genuinely present (ARP / save_own_device) but had an absent NIC child, the NIC step forced it to 0. The next scan re-detected it → Connected event → NIC forced it back to 0 → endless one-directional event flood.

Change

server/scan/device_handling.pyupdate_devPresentLastScan_based_on_nics:

# Before: NIC result replaced parent's own detection entirely
if req_all:
    new_present = int(all(nic_statuses))
else:
    new_present = int(any(nic_statuses))

# After: NIC result can only raise, never lower, a directly-detected parent
if req_all:
    nic_derived = int(all(nic_statuses))
else:
    nic_derived = int(any(nic_statuses))
new_present = max(original, nic_derived)

max(original, nic_derived) ensures:

  • Parent detected this scan (original=1) → stays 1 regardless of NIC child state
  • Parent not detected (original=0) → NIC children can still bring it online
  • Applies consistently to both req_all modes

test/scan/test_nic_presence.py — new test file covering the exact regression (parent present=1 + absent NIC → stays 1), NIC raise cases, req_all variants, and the no-NIC-children no-change path.


🔍 Related Issues

Closes #1736


📋 Type of Change

  • 🐛 Bug fix
  • 🧪 Test addition or change

📷 Screenshots or Logs (if applicable)

From the issue — before the fix, every scan cycle produced a spurious Connected event for a device that was continuously present:

2026-08-13 04:01:11  0a:17:f2:8d:02:97  Connected
2026-08-13 04:03:11  0a:17:f2:8d:02:97  Connected
2026-08-13 04:05:18  0a:17:f2:8d:02:97  Connected
...  (8 Connected, 0 Disconnected over ~11 minutes)

🧪 Testing Steps

Unit tests in test/scan/test_nic_presence.py cover the regression and surrounding cases directly without requiring the full stack.


✅ Checklist

  • I have read the Contribution Guidelines
  • I have tested my changes locally
  • I have updated relevant documentation (if applicable)
  • I have verified my changes do not break existing behavior
  • I am willing to respond to requested changes and feedback

🙋 Additional Notes

The max(original, nic_derived) formulation is intentionally minimal — it threads through both req_all modes with no branching on original, and preserves the existing "NIC can raise an absent parent" semantics unchanged.

…ffline

When a parent device has NIC children, update_devPresentLastScan_based_on_nics
previously replaced the parent's devPresentLastScan unconditionally with the
NIC-derived value. This discarded any genuine direct detection of the parent:
if the parent was found by ARP/save_own_device (present=1) but its NIC child
was absent (present=0), the NIC step forced the parent back to 0. The next
scan re-detected the parent → Connected event → NIC forced it down again,
producing an endless one-directional Connected event stream.

Fix: use max(original, nic_derived) so NIC children can only raise a parent's
presence (bring an undetected parent online), never lower it when the parent
itself was directly detected this cycle.

Adds test/scan/test_nic_presence.py covering the exact regression scenario
and surrounding cases (raise, no-NIC unchanged, req_all modes).

Fixes #1736

Co-authored-by: jokob-sk <96159884+jokob-sk@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix endless event stream for devices with a 'nic' relationship Fix: NIC child presence no longer forces a directly-detected parent offline (#1736) Aug 13, 2026
Copilot AI requested a review from jokob-sk August 13, 2026 08:44

@jokob-sk jokob-sk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also target next_release branch, not main

Comment thread test/scan/test_nic_presence.py Outdated
# Helpers
# ---------------------------------------------------------------------------

def _make_db(rows):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not mock DB, use main/test/db_test_helpers.py where-ever possible

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the latest commit — the test now uses make_db, make_device_dict, insert_device_from_dict, and DummyDB from db_test_helpers.

Comment thread test/scan/test_nic_presence.py Outdated
{"mac": "AA:AA:AA:AA:AA:01", "present": 1,
"parent_mac": "", "rel_type": "", "req_all": 0},
{"mac": "BB:BB:BB:BB:BB:01", "present": 0,
"parent_mac": "AA:AA:AA:AA:AA:01", "rel_type": "nic", "req_all": 0},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

macs always lower case every where

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — all MACs are now lowercase (e.g. aa:aa:aa:aa:aa:01).

Comment thread server/scan/device_handling.py Outdated
nic_derived = int(any(nic_statuses))
# NIC children can only raise a parent's presence, never lower it
# when the parent itself was directly detected as present this scan.
new_present = max(original, nic_derived)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make it easier to read, suing max() obfuscates logic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replaced with explicit if original == 1: pass / else: new_present = 1 if nic_online else 0 — each case is now clearly stated.

- Replace max() with explicit if/else for readability
- Use db_test_helpers (make_db, make_device_dict, insert_device_from_dict,
  DummyDB) instead of local mock DB objects in test_nic_presence.py
- Lowercase all MAC addresses in tests

Co-authored-by: jokob-sk <96159884+jokob-sk@users.noreply.github.com>

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

also target next_release branch, not main

The base branch of a PR can only be changed from the GitHub UI (Edit → Change base). Please retarget it to next_release there — the code changes themselves are branch-agnostic.

Copilot AI requested a review from jokob-sk August 13, 2026 08:53
@jokob-sk
jokob-sk changed the base branch from main to next_release August 13, 2026 08:57
@jokob-sk
jokob-sk marked this pull request as ready for review August 13, 2026 08:59
@jokob-sk

Copy link
Copy Markdown
Collaborator

@copilot why didn't you follow the ai guidelines - if needed, update them or add/update skills?

Co-authored-by: jokob-sk <96159884+jokob-sk@users.noreply.github.com>

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot why didn't you follow the ai guidelines - if needed, update them or add/update skills?

Two skills were referenced in copilot-instructions.md (pr-analysis and logging-standards) but had no actual SKILL.md file, so they couldn't be loaded before starting. That's why the process steps in code-standards (load relevant skills first, prepare a plan, MAC/DB-helpers conventions) weren't followed upfront — the PR-analysis skill that would have prompted loading them didn't exist.

Fixed in the latest commit: both skills are now created in .github/skills/ and .gemini/skills/, and the skills-overview/skills-index cross-reference tables are updated.

@jokob-sk

jokob-sk commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

@copilot
will those skill prevent the issues I had to comment on for you to fix in the last 2 PRs?:

  • always use existing helpers in tests if available, or extend teh helpers class, follow DRY wherever possible, in this case main/test/db_test_helpers.py
  • mocked MACs should always be lower case

... will you next time follow these based on available guidelines and skills? if not update those to make this explicit

@@ -0,0 +1,146 @@
"""Tests for update_devPresentLastScan_based_on_nics.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot
will those skill prevent the issues I had to comment on for you to fix in the last 2 PRs?:

always use existing helpers in tests if available, or extend teh helpers class, follow DRY wherever possible, in this case main/test/db_test_helpers.py
mocked MACs should always be lower case

... will you next time follow these based on available guidelines and skills? if not update those to make this explicit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing code-standards rule mentioned reusing db_test_helpers.py and normalizing MACs, but not explicitly enough to catch test fixture literals. Updated in the latest commit:

  • code-standards: new dedicated "MAC Literals in Tests — ALWAYS Lowercase" section with explicit wrong/right examples.
  • pr-analysis (Copilot + Gemini): new "Before Writing Any Test Code" checklist placed before any other action — lowercase MAC literals and helpers-first are listed as non-negotiable gates, not just a post-batch reminder.

@jokob-sk
jokob-sk marked this pull request as draft August 13, 2026 09:14
…analysis skills

Co-authored-by: jokob-sk <96159884+jokob-sk@users.noreply.github.com>

Copilot AI commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

@copilot
will those skill prevent the issues I had to comment on for you to fix in the last 2 PRs?:

...

The existing code-standards rule mentioned reusing db_test_helpers.py and normalizing MACs, but not explicitly enough to catch test fixture literals. Updated in the latest commit:

  • code-standards: new dedicated "MAC Literals in Tests — ALWAYS Lowercase" section with explicit wrong/right examples.
  • pr-analysis (Copilot + Gemini): new "Before Writing Any Test Code" checklist placed before any other action — lowercase MAC literals and helpers-first are listed as non-negotiable gates, not just a post-batch reminder.

Copilot AI requested a review from jokob-sk August 13, 2026 09:15
@jokob-sk
jokob-sk marked this pull request as ready for review August 13, 2026 09:18
@jokob-sk
jokob-sk merged commit f143643 into next_release Aug 13, 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.

Device with a "nic" child relationship gets an endless one-directional Connected/Disconnected event stream instead of tracking its own real presence

2 participants