Skip to content

refactor(tracker): adopt asyncssh tracker_factory API - #4

Merged
AlexMKX merged 1 commit into
mainfrom
feature/forward-tracker-factory
Jun 24, 2026
Merged

refactor(tracker): adopt asyncssh tracker_factory API#4
AlexMKX merged 1 commit into
mainfrom
feature/forward-tracker-factory

Conversation

@AlexMKX

@AlexMKX AlexMKX commented Jun 24, 2026

Copy link
Copy Markdown
Owner

What

Reworks the idle-auto-stop integration onto the redesigned asyncssh forward-tracker API. Externally observable behavior is unchanged — this is an integration-layer refactor to match the new upstream-agreed API shape.

Pairs with asyncssh fork v2.23.0+forward-tracker.3 (branch feat/forward-tracker), the API agreed with the upstream maintainer in ronf/asyncssh#807.

Why

The original ForwardTracker(Protocol) (single object per listener, connection_made(orig_host, orig_port) / connection_lost(orig_host, orig_port, exc), tracker= kwarg) was redesigned during upstream review into a class hierarchy with a per-connection factory:

  • asyncssh.SSHForwardTracker (parent, no-op connection_lost + forward_local_bytes + forward_remote_bytes)
  • asyncssh.SSHPortForwardTrackerconnection_made(forwarder, orig_host, orig_port) (TCP)
  • asyncssh.SSHPathForwardTrackerconnection_made(forwarder) (UNIX)
  • new tracker_factory= kwarg, invoked once per accepted connection

Changes

  • activity.pyActivityTracker is now the per-daemon aggregate and the factory via make_tracker(). The asyncssh-facing hooks move to a per-connection _IdleConnectionTracker(asyncssh.SSHPortForwardTracker) with _opened/_closed idempotency guards (a connection counts exactly once even if connection_lost fires twice). Public contract (make_tracker, note_connection_made, note_connection_lost, is_idle, seconds_since_activity) preserved.
  • ssh.pyopen_local_forwards(..., tracker_factory=...) (was tracker=), forwarded as forward_local_port(tracker_factory=...).
  • manager.py — passes tracker_factory=self.activity_tracker.make_tracker.
  • pyproject.toml — asyncssh fork pin bumped v2.23.0+forward-tracker.1.3.
  • teststest_activity / test_ssh_transport / test_idle_watchdog / test_manager_* fakes rewritten for the factory + per-connection pair.
  • docs/specs/2026-05-21-auto-stop-idle-design.md — revision-history note recording the API shift.

Verification

  • pytest tests/unit213 passed
  • ruff / black / mypy → clean; pylint activity.py 10.00/10
  • Independent passes: multi-delegate code review (critical double-connection_lost fixed) + RP/OP code-health-check → PASS (RP 32.3 / OP 29.0, max CC 4, MI grade A)

Note: the fork pin .3 only resolves once the fork branch/tag is published (already pushed to AlexMKX/asyncssh). CI here will install it from the VCS pin.

Rework the idle auto-stop integration onto the redesigned asyncssh
forward-tracker API (fork v2.23.0+forward-tracker.3, agreed in upstream
PR ronf/asyncssh#807). Externally observable behavior is unchanged.

- activity.py: ActivityTracker becomes the per-daemon aggregate and a
  tracker factory via make_tracker(); the asyncssh-facing hooks move to a
  per-connection _IdleConnectionTracker(asyncssh.SSHPortForwardTracker)
  with _opened/_closed idempotency guards so a connection counts exactly
  once even if connection_lost fires twice.
- ssh.py: open_local_forwards takes tracker_factory (was tracker) and
  forwards it as forward_local_port(tracker_factory=...).
- manager.py: pass tracker_factory=activity_tracker.make_tracker.
- pyproject.toml: bump asyncssh fork pin to v2.23.0+forward-tracker.3.
- tests: rewrite activity/ssh-transport/idle-watchdog/manager fakes for
  the factory + per-connection pair.
- docs/specs: add revision-history note recording the API shift.
@AlexMKX
AlexMKX force-pushed the feature/forward-tracker-factory branch from b870cb0 to de380f3 Compare June 24, 2026 07:41
@AlexMKX
AlexMKX merged commit 86c3569 into main Jun 24, 2026
21 checks passed
AlexMKX added a commit that referenced this pull request Aug 10, 2026
…27)

Three committed documents carried the literal path
`/home/<user>/Projects/.../worktrees/tunstrap-issue15-spike`, publishing a
username and an unrelated sibling project from a public repository. The sweep
went wider than the three files named in the report -- other absolute home
paths were sitting in a recipe example and in four test fixtures -- and all of
them are now placeholders.

The second half mattered more. Three live citations pointed into
`docs/artifacts/`, which `.gitignore` excludes, so no reader could resolve
them. One of the three justifies a production decision: `envrender.py` cites
the measured provider precedence to explain why `KUBE_CONFIG_PATH` and
`KUBE_CONFIG_PATHS` are never exported together, the rule that otherwise
silently hides every cluster but the first. The Terragrunt recipe cited the
same file at consumers.

That measurement is a result, not scratch work, so it is now a committed
document under `docs/specs/` and both citations point at it. Its
source-corroboration section is scoped as inference rather than presented as
verified source reading, because that is what it is.

The e2e baseline citation was a different case and is not promoted. Its claim
-- plan, mutate only the kubeconfig, apply the saved plan, zero mismatch -- has
no test behind it, so removing the citation as "self-evident from the
assertions" would have erased the only provenance a live manual measurement
had. The recipe now says plainly that finding #4 comes from the unpublished
spike and is not automated.

Edits to frozen plan and spec documents are redactions and repoints only. One
of them initially had its `docs/artifacts/` prefix stripped rather than
resolved, which silenced the guard while leaving the citation dangling and
added a false "now committed" claim about files that are not in the repo; it
names the real spec now.

The guard test enforces the property rather than a spelling. Matching the
literal string `docs/artifacts/` is exactly what prefix-stripping evades, so it
resolves path-like references and fails on any that `git check-ignore`
excludes, plus bare `*-findings.md` names that resolve to nothing. It carries a
documented allowlist marker so the next author has a route other than rewording
around a regex, and it builds its own message sentinels by concatenation so it
does not flag itself once tracked.

Verification note: the e2e tier could not be run green for this commit --
`tofu init` fails with `registry.opentofu.org requires authentication
credentials` while resolving the kubernetes and helm providers. That
reproduces on an unmodified HEAD, so it is an upstream registry problem rather
than a regression here; this change is documentation plus one test, and the
only production edit is two docstring citations.
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