feat(s52): export Notifier public contract + sync extending.md B11
This commit is contained in:
parent
1267f6c3eb
commit
b84f4d46bb
3 changed files with 48 additions and 2 deletions
|
|
@ -117,8 +117,15 @@ territory for a deployer, with the seam named:
|
||||||
layers (`write_verdict`, `promote_verdict`) are last-write-wins per file. The full taxonomy
|
layers (`write_verdict`, `promote_verdict`) are last-write-wins per file. The full taxonomy
|
||||||
(rejection categories + a rule for conflicting expert verdicts) is deferred until real experts
|
(rejection categories + a rule for conflicting expert verdicts) is deferred until real experts
|
||||||
produce conflicting verdicts.
|
produce conflicting verdicts.
|
||||||
- **B11 — expert notification.** `run_project(notify=...)` is a stub seam (`run.py`): pass any
|
- **B11 — expert notification.** `run_project(notify=...)` remains a plain-callable seam
|
||||||
callable; no delivery mechanism (e-mail/Teams/webhook) ships with the core.
|
(`run.py` auto-wires no default notifier), but the core now ships the declared `Notifier`
|
||||||
|
contract (`notify.py`, exported from the package top) with three implementations:
|
||||||
|
`ConsoleNotifier`, `FileNotifier` (byte-deterministic JSONL), and `WebhookNotifier` — plus a
|
||||||
|
fail-fast `build_notifier(config, *, allow_egress=...)` factory. The webhook is the ONLY egress
|
||||||
|
point and is fail-closed behind an explicit per-run `allow_egress=True` opt-in (a code kwarg,
|
||||||
|
never a config field — mirroring the ingest layer's `allow_network`). SSRF guards, HMAC
|
||||||
|
signing, and auth headers remain deployer-owned extension points on the injectable
|
||||||
|
`WebhookPost` transport seam.
|
||||||
- **U12 — checkpointing / crash-survival of a run.** A run either completes or is re-run; the
|
- **U12 — checkpointing / crash-survival of a run.** A run either completes or is re-run; the
|
||||||
async verdict inbox (step 7) is the resumable boundary, not intra-run state.
|
async verdict inbox (step 7) is the resumable boundary, not intra-run state.
|
||||||
- **U14 — OpenTelemetry / observability.** Provenance stamping is the audit trail the core
|
- **U14 — OpenTelemetry / observability.** Provenance stamping is the audit trail the core
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,16 @@ from portfolio_optimiser.ledger import (
|
||||||
SavingsLedger,
|
SavingsLedger,
|
||||||
realize,
|
realize,
|
||||||
)
|
)
|
||||||
|
from portfolio_optimiser.notify import (
|
||||||
|
ConsoleNotifier,
|
||||||
|
FileNotifier,
|
||||||
|
Notifier,
|
||||||
|
NotifierConfig,
|
||||||
|
NotifyError,
|
||||||
|
NotifyRefused,
|
||||||
|
WebhookNotifier,
|
||||||
|
build_notifier,
|
||||||
|
)
|
||||||
from portfolio_optimiser.run import (
|
from portfolio_optimiser.run import (
|
||||||
GoalReached,
|
GoalReached,
|
||||||
PortfolioResult,
|
PortfolioResult,
|
||||||
|
|
@ -35,5 +45,14 @@ __all__ = [
|
||||||
"GoalConfig",
|
"GoalConfig",
|
||||||
"load_goal_config",
|
"load_goal_config",
|
||||||
"GoalReached",
|
"GoalReached",
|
||||||
|
# S5.2 varsling (B11): the declared Notifier contract + deliverable implementations
|
||||||
|
"Notifier",
|
||||||
|
"ConsoleNotifier",
|
||||||
|
"FileNotifier",
|
||||||
|
"WebhookNotifier",
|
||||||
|
"build_notifier",
|
||||||
|
"NotifierConfig",
|
||||||
|
"NotifyError",
|
||||||
|
"NotifyRefused",
|
||||||
"__version__",
|
"__version__",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -129,3 +129,23 @@ def test_config_fail_fast() -> None:
|
||||||
|
|
||||||
notifier = build_notifier(webhook_cfg, allow_egress=True)
|
notifier = build_notifier(webhook_cfg, allow_egress=True)
|
||||||
assert isinstance(notifier, WebhookNotifier)
|
assert isinstance(notifier, WebhookNotifier)
|
||||||
|
|
||||||
|
|
||||||
|
def test_public_contract_exported() -> None:
|
||||||
|
"""The declared B11 contract is public authoring API: importable from the package top and
|
||||||
|
listed in ``__all__`` (the brief-permitted export path — run.py's seam stays byte-intact)."""
|
||||||
|
import portfolio_optimiser
|
||||||
|
|
||||||
|
exported = (
|
||||||
|
"Notifier",
|
||||||
|
"ConsoleNotifier",
|
||||||
|
"FileNotifier",
|
||||||
|
"WebhookNotifier",
|
||||||
|
"build_notifier",
|
||||||
|
"NotifierConfig",
|
||||||
|
"NotifyError",
|
||||||
|
"NotifyRefused",
|
||||||
|
)
|
||||||
|
for name in exported:
|
||||||
|
assert name in portfolio_optimiser.__all__, f"{name} missing from __all__"
|
||||||
|
assert hasattr(portfolio_optimiser, name), f"{name} not importable from package top"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue