Skip to content

Phase 9/10: plugin update compatibility gate, execution timeout, Stellar notifier example, and default price table - #502

Merged
Ayinkx merged 3 commits into
AyinkxLab:mainfrom
Hey-Yetunde:fix/phase-9-10-405-408-415-439
Oct 17, 2026
Merged

Ayinkx merged 3 commits into
AyinkxLab:mainfrom
Hey-Yetunde:fix/phase-9-10-405-408-415-439

Conversation

@Hey-Yetunde

Copy link
Copy Markdown
Contributor

Summary

This PR resolves four Phase 9/10 issues in ai-code-assistant:

  • Phase 9: Compatibility gate on update #405 — compatibility gate on update: new owner-only update endpoint plus update_plugin(). The manifest is validated and identity-bound, and its declared PEP 440 range is enforced against the running app before anything is written. An incompatible range is refused with a message naming both the range and the current app version, leaving the registered plugin untouched.
  • Phase 9: Plugin execution timeout #408 — plugin execution timeout: event handlers and lifecycle hooks now run with a configurable PLUGIN_EXECUTION_TIMEOUT budget. A slow callback is abandoned, recorded as one bounded error report (dispatch:<event> / hook:<hook>), and dispatch or the lifecycle state change continues safely.
  • Phase 9: Example plugin - Stellar notifier #415 — Stellar notifier example: examples/plugins/stellar_notifier subscribes to stellar.analysis.completed, formats a bounded, read-only summary, and stays offline. Covered by schema, load/register, event-path, bounding, read-only, and no-network tests.
  • Phase 10: Default price table for common providers #439 — default price table: app/services/pricing.py provides documented per-1k input/output estimates for every model exposed by the built-in providers, with the mock provider pinned at zero cost. Every vendor entry is explicitly marked as an estimate, lookups are case-insensitive, and unknown models return None instead of assuming zero.

Linked issue

Closes #405
Closes #408
Closes #415
Closes #439

Changes

  • app/services/plugin_compat.py — enforce_compatible() and IncompatibleCompatibilityError; the install route reuses the same helper.
  • app/plugins/routes.py, app/services/plugin_ops.py — POST .../plugins/<plugin_id>/update and update_plugin() with id/entry-point binding and clear 400/409/404 responses. Nothing is persisted when the gate refuses an update.
  • app/services/plugin_execution.py — run_with_timeout() and PluginTimeoutError (daemon worker, app context preserved, non-positive timeout disables the bound).
  • app/services/events.py, app/services/plugins.py — handler and hook calls wrapped in the timeout.
  • app/config.py, .env.example — PLUGIN_EXECUTION_TIMEOUT (default 5 seconds).
  • app/services/pricing.py, docs/providers.md — price table, lookup/estimate helpers, and the "verify these estimates" disclaimer.
  • examples/plugins/stellar_notifier/*, docs/plugins.md — example plugin, README, and documentation for the update gate, timeout, and example.
  • New tests: tests/test_pricing.py, tests/test_plugin_update.py, tests/test_plugin_timeout.py, tests/test_stellar_notifier_example.py.

Validation

  • ruff check . — passes
  • black --check . — passes
  • pytest -q — all tests pass except the two pre-existing TestBudgetAlertDeduplication tests described below (verified locally by running the suite with that class excluded).

Notes for reviewers

Upstream main is currently broken in tests/test_notifications.py (from the #500 merge):

  • the file contains a syntax error (self/ app), which stops pytest from collecting it. This PR includes a one-character repair in a separate commit (test: fix syntax error blocking notifications collection).
  • after that repair, the two TestBudgetAlertDeduplication tests still fail because they import app.services.budget_alerts, which does not exist on main (the merged change added BudgetAlertDeduplicator to app/services/token_usage.py instead). This is unrelated to these four issues, so I left it alone rather than colliding with the Phase 10 budget work.

The execution timeout abandons an over-budget callback because Python cannot interrupt threads; callbacks must therefore be idempotent. This limitation is documented in docs/plugins.md.

The Phase 10 budget-alert test merged in AyinkxLab#500 has an invalid parameter list (self/ app), which prevents pytest from collecting tests/test_notifications.py at all. Restore the comma so the suite can run.
Ship per-1k input/output estimates for every model exposed by the built-in providers, with the mock provider pinned at zero cost. Entries are explicitly marked as estimates via ModelPrice.estimated and ESTIMATE_DISCLAIMER; unknown provider/model lookups return None rather than assuming zero. Lookups are case/whitespace insensitive, token counts are validated, and price_table() exposes the grouped table for operator-facing surfaces.
…ellar example

Refs AyinkxLab#405, AyinkxLab#408, AyinkxLab#415.

- AyinkxLab#405: add an owner-only update endpoint plus update_plugin(), identity-bound to the registered id/entry point. The manifest's PEP 440 compatibility range is enforced before any write, with an error naming the range and the running app version.
- AyinkxLab#408: run plugin event handlers and lifecycle hooks with a configurable PLUGIN_EXECUTION_TIMEOUT budget in a daemon thread. Overruns are abandoned, recorded as one bounded error report, and dispatch/lifecycle state changes continue safely.
- AyinkxLab#415: add examples/plugins/stellar_notifier, a read-only, offline example that formats bounded summaries for stellar.analysis.completed, with schema/load/event/bounding tests.
@drips-wave

drips-wave Bot commented Oct 17, 2026

Copy link
Copy Markdown

@Hey-Yetunde Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@Hey-Yetunde

Copy link
Copy Markdown
Contributor Author

CI note: Lint & format, Asset & VS Code guard, Frontend tests, and Build Docker image are green. The two failing jobs are pre-existing on main:

  • Tests — 2245 passed, 2 failed. Both failures are TestBudgetAlertDeduplication (ModuleNotFoundError: app.services.budget_alerts) from the fix: deduplicate budget threshold alerts per period #500 merge. They fail identically on main, which additionally cannot collect tests/test_notifications.py at all because of the self/ app syntax error fixed here.
  • Tests (PostgreSQL) — 182 tests passed, but the chat-only coverage gate is 83.81% < 85%. This branch does not touch app/chat/* or app/services/llm_cache.py; main's latest CI run (39034485852) fails this same job.

No new failures are introduced by this branch: test_pricing, test_plugin_update, test_plugin_timeout, and test_stellar_notifier_example all pass in CI.

@Ayinkx
Ayinkx merged commit 0e37c6d into AyinkxLab:main Oct 17, 2026
4 of 6 checks 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

2 participants