Skip to content

fix(registry): run the consumer check once per module set (FR-52); pin PyJWT[crypto] - #814

Merged
Masterplanner25 merged 2 commits into
mainfrom
fix/fr52-consumer-check-once
Oct 10, 2026
Merged

Masterplanner25 merged 2 commits into
mainfrom
fix/fr52-consumer-check-once

Conversation

@Masterplanner25

@Masterplanner25 Masterplanner25 commented Oct 10, 2026 •

Copy link
Copy Markdown
Owner

Answers the app's FR-52 (P1) and hardens #810's PyJWT pin.

FR-52 — ~3 s on every request

load_plugins() ended unconditionally in DEBT-COMPAT-1's check_consumer_requirements, which calls packages_distributions() and scans every distribution for shadowing metadata. The registry's getters (emit_event, get_route_guard, get_response_adapter, get_memory_policy, … 14 sites) call load_plugins() lazily — 26 times in one warm GET /memory/nodes. The app measured p50 3,780 ms as shipped vs 820 ms with the check stubbed out, on 2.25.0 and 2.26.0.

Fix: run the check when the plugin module set differs from the last one checked (registry._consumer_check_modules, added to both registry-reset dicts). The answer cannot change while the process lives; a different manifest/profile is still checked. /api/version serves the same records.

Why our suites never saw it: runtime-only boot's manifest lists no plugins, so load_plugins() returns before the check. The app has 16 plugins.

Tests (test_debt_compat1_consumer_range.py), counting metadata scans:

  • 5 load_plugins() calls → 1 scan, and the record is still written;
  • a wider module set → checked again (liveness for the latch);
  • one warm request through the real route, plugin manifest selected via AINDY_PLUGIN_MANIFEST → 0 scans (26 before), with a liveness check that the request went through load_plugins().

Mutations: always-check fails all three; never-check fails the existing check test plus two new ones.

PyJWT[crypto]

nodus-auth (def3ef8) found PyJWT's HMAC guard against asymmetric key material opens with if not has_crypto: return False. We had cryptography through its own pin, so the guard was live, but only by coincidence. Now PyJWT[crypto]==2.15.1, plus a test asserting has_crypto.

Ledger

TECH_DEBT.md: FR-51 (#810) and FR-52 (#814) entries, both CLOSED / UNRELEASED. CLAUDE.md: next app FR is FR-53 (39,720 chars). Changelog: changelog.d/814-fr52-consumer-check-once.md.

ruff clean. Local test_dependency_pin_agreement fails only on this host's otel 1.44 drift (pre-existing, unrelated).

🤖 Generated with Claude Code

Masterplanner25 and others added 2 commits October 9, 2026 21:26
… (FR-52); pin PyJWT[crypto]

load_plugins() ended unconditionally in check_consumer_requirements, and the
registry's getters call load_plugins() lazily -- 26 times in one warm
`GET /memory/nodes`. Each check read every installed distribution's metadata
(packages_distributions + the shadow scan): ~3 s on every app request since
2.25.0 (app: p50 3,780 ms as shipped, 820 ms with the check stubbed). The
check now runs when the plugin module set differs from the last one checked;
its answer cannot change while the process lives. The latch is in both
registry-reset dicts.

Tests count metadata scans: 5 loads -> 1 scan; a different module set is
checked again; one warm request through the real route -> 0 scans (26 before).
Always-check and never-check mutations each fail them.

PyJWT is pinned with its crypto extra: its HMAC guard against asymmetric key
material returns early when cryptography is absent (found by nodus-auth,
def3ef8). We had cryptography by a separate pin; now the guard does not
depend on that, and a test asserts has_crypto.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is FR-53

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Masterplanner25
Masterplanner25 enabled auto-merge (squash) October 10, 2026 04:27
@Masterplanner25
Masterplanner25 merged commit 0c1c1a3 into main Oct 10, 2026
12 checks passed
@Masterplanner25
Masterplanner25 deleted the fix/fr52-consumer-check-once branch October 10, 2026 04:36
@Masterplanner25 Masterplanner25 mentioned this pull request Oct 10, 2026
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