Skip to content

Commit 2bf044f

Browse files
committed
release(0.5.8): finalize enforcement release
1 parent 5b86675 commit 2bf044f

18 files changed

Lines changed: 188 additions & 34 deletions

CHANGELOG.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10-
### Internal 0.5.7 release train (not published)
10+
## [0.5.8] — 2026-08-02
1111

12-
This odd-numbered release train is build- and CI-validated but intentionally has
13-
no package version bump, tag, or PyPI publication.
12+
Published release containing the build- and CI-validated internal 0.5.7 train,
13+
the GPT-5.6 enforcement work, and configuration-applicability clarity.
1414

1515
### Added
1616

@@ -21,6 +21,9 @@ no package version bump, tag, or PyPI publication.
2121
- `X-Airlock-Model-Alias` makes configured generic aliases transparent: clients see
2222
the actual served LiteLLM body and a directly callable newer/current-generation
2323
alias without any routing change.
24+
- The TUI Config screen now marks controls that require a proxy restart and identifies
25+
controls that its Apply action does not change. Apply reports whether each actual
26+
change is effective immediately or pending a restart, and reports no-op Applies.
2427

2528
### Fixed
2629

airlock/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Airlock — Enterprise LLM proxy built on LiteLLM."""
22

3-
__version__ = "0.5.6"
3+
__version__ = "0.5.8"

airlock/callbacks/tracing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _get_tracer() -> Any:
4545
provider = TracerProvider(resource=resource)
4646
trace.set_tracer_provider(provider)
4747

48-
return trace.get_tracer("airlock", "0.5.6")
48+
return trace.get_tracer("airlock", "0.5.8")
4949

5050

5151
_tracer = _get_tracer()

airlock/fast/guardian.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@
3939
AirlockProviderBlocked,
4040
sanitize_reason,
4141
)
42-
from airlock.reasoning_effort import normalize_reasoning_effort, validate_reasoning_effort
42+
from airlock.reasoning_effort import (
43+
normalize_reasoning_effort,
44+
validate_reasoning_effort,
45+
)
4346
from airlock.transparency import detect_dropped_params, record_mutation
4447
from airlock.client_identity import (
4548
client_id_from_api_key,
@@ -312,7 +315,9 @@ async def async_pre_call_hook(
312315
)
313316
suggestions = alias_table.suggest(model_name)
314317
suggestions.sort(
315-
key=lambda item: 0 if item.get("model") == cross_tier.suggested else 1
318+
key=lambda item: (
319+
0 if item.get("model") == cross_tier.suggested else 1
320+
)
316321
)
317322
if not suggestions:
318323
suggestions = [

airlock/proxy_errors.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,7 @@ def __init__(
8989
class AirlockInvalidReasoningEffort(Exception):
9090
"""A known-invalid OpenAI reasoning effort rejected before provider dispatch."""
9191

92-
def __init__(
93-
self, requested: str, model: str, supported: frozenset[str]
94-
) -> None:
92+
def __init__(self, requested: str, model: str, supported: frozenset[str]) -> None:
9593
self.requested = requested
9694
self.model = model
9795
self.supported = tuple(sorted(supported))
@@ -262,7 +260,9 @@ def install_airlock_error_handlers_on_proxy_app() -> bool:
262260
if not getattr(app.state, "airlock_model_not_found_handler_installed", False):
263261
app.add_exception_handler(AirlockModelNotFound, airlock_model_not_found_handler)
264262
app.state.airlock_model_not_found_handler_installed = True
265-
if not getattr(app.state, "airlock_invalid_reasoning_effort_handler_installed", False):
263+
if not getattr(
264+
app.state, "airlock_invalid_reasoning_effort_handler_installed", False
265+
):
266266
app.add_exception_handler(
267267
AirlockInvalidReasoningEffort, airlock_invalid_reasoning_effort_handler
268268
)

airlock/reasoning_effort.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ def _supported_efforts(model: str | None) -> frozenset[str] | None:
201201
return frozenset(levels)
202202

203203

204-
def _validation_status(model: str | None, requested: str) -> tuple[bool, frozenset[str]] | None:
204+
def _validation_status(
205+
model: str | None, requested: str
206+
) -> tuple[bool, frozenset[str]] | None:
205207
"""Return ``(known_invalid, supported)`` or ``None`` when validation is unsafe.
206208
207209
GPT-5.6's ``max`` support is documented by OpenAI even though the pinned LiteLLM

dev/plans/0.5.8-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,6 @@ The open GitHub backlog is intentionally **not** an implicit 0.5.8 queue:
754754
- [x] P-2 and P-2b enforcement reviewed and implemented; post-release reporting stays
755755
enabled.
756756
- [x] P-6a and P-6b resolved (implemented or explicitly recorded as no-change).
757-
- [ ] P-8 implemented and reviewed, including its focused TUI tests.
757+
- [x] P-8 implemented and reviewed, including its focused TUI tests.
758758
- [ ] P-9 complete: final validation recorded, #22 audited/closed-or-deferred, version
759759
and release notes prepared, and the normal 0.5.8 release tag created.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# 0.5.8 release evidence — 2026-08-02
2+
3+
## Candidate
4+
5+
- Package version: `0.5.8` in `pyproject.toml`, `airlock.__version__`, and the tracing
6+
version. `scripts/check-version-consistency.py --tag v0.5.8` passed.
7+
- Dependency authority: `uv.lock` refreshed after the manifest version change;
8+
`uv lock --check` passed. Docker resolves the locked LiteLLM `1.94.1` and validates
9+
it against Airlock's declared `>=1.94.1,<2` floor at image build time.
10+
11+
## P-8 / #34 validation
12+
13+
`uv run pytest tests/test_tui.py -q -k config` passed: **12 passed**.
14+
15+
The ConfigPane has one field-applicability contract. It marks restart-required and
16+
unapplied controls, reports no-op/live/restart/mixed Applies without values, and leaves
17+
`config.local.yaml` loading semantics unchanged.
18+
19+
## GitHub issue #22 audit — PASS
20+
21+
| Required decision | Evidence |
22+
|---|---|
23+
| Keep `/v1/chat/completions` OpenAI-compatible | `airlock/docs.py` conceptual/API documentation and `tests/test_docs.py` |
24+
| Accept Gemini request controls without a new body response contract | `airlock/gemini_interface.py` and `tests/test_gemini_interface.py` |
25+
| Surface Gemini mode/state through response headers | `airlock/callbacks/model_override_headers.py` and `tests/test_model_override_headers.py` |
26+
| Preserve structured provider detail for operators | `airlock/callbacks/request_event.py`, enterprise JSONL projection, `tests/test_enterprise_logger.py`, and the TUI Guards projection |
27+
28+
Focused audit command:
29+
30+
```text
31+
uv run pytest tests/test_gemini_interface.py tests/test_model_override_headers.py \
32+
tests/test_enterprise_logger.py tests/test_tui.py -q -k 'gemini or config'
33+
# 24 passed, 143 deselected
34+
```
35+
36+
The issue is documentation/decision scope, not a request for a new provider-native
37+
response body. It is eligible to close with this evidence.
38+
39+
## Local release gate
40+
41+
| Gate | Result |
42+
|---|---|
43+
| Full non-live suite | `2768 passed, 107 deselected, 1 xpassed` (98 deprecation warnings) |
44+
| Ruff check / format | clean: `airlock/` and `tests/` |
45+
| Mypy | `airlock/fast/` clean (`--ignore-missing-imports`) |
46+
| Dependency audit | no known vulnerabilities; local project and spaCy model are intentionally not PyPI-auditable |
47+
| Docs | `mkdocs build --strict` passed |
48+
| Build | `uv build` produced `airlock_llm-0.5.8.tar.gz` and wheel |
49+
| Clean wheel install | fresh temporary venv imported `airlock 0.5.8`; `airlock --help` passed |
50+
| Docker | image built with locked LiteLLM `1.94.1`; isolated container returned `"I'm alive!"` from `/health/liveliness` with `AIRLOCK_HOST=0.0.0.0` and optional services disabled; container stopped |
51+
52+
Live-provider tests remain opt-in and were intentionally deselected. The direct funded
53+
OpenAI `reasoning_effort=max` probe remains unavailable because no funded key is
54+
present; documented GPT-5.6 support is covered by the model-specific contract test.
55+
56+
## Remaining release actions
57+
58+
1. Commit this release candidate.
59+
2. Close GitHub issue #22 with this audit evidence.
60+
3. Push the candidate and require the GitHub Actions CI workflow to pass.
61+
4. Create and push `v0.5.8`; the release workflow then performs PyPI trusted
62+
publishing and creates the GitHub release.

dev/plans/runs/STATUS-0.5.8.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# STATUS — 0.5.8 (live state board)
2+
3+
> The current state is derived from release evidence and commits; this board is a
4+
> compaction-safe pointer, not a substitute for them.
5+
6+
_Last updated: 2026-08-02 · release candidate: `v0.5.8` (local candidate)_
7+
8+
## Current state
9+
10+
- **In flight:** P-9 release hygiene.
11+
- **Next action:** commit the release candidate, close the audited documentation issue,
12+
push for GitHub Actions, then create the `v0.5.8` publishing tag only after CI is
13+
green.
14+
15+
## Scope scoreboard
16+
17+
| Item | State | Witness |
18+
|---|---|---|
19+
| P-2 reasoning-effort enforcement | CLOSED | `5a7c714`, `5b20c4e`; tests in `tests/test_reasoning_effort.py` |
20+
| P-2b cross-tier fuzzy refusal | CLOSED | `5a7c714`; tests in `tests/test_model_suggestion.py` |
21+
| P-6 alias disclosure / P-6a / P-6b | CLOSED | `1062343`; header and interface tests |
22+
| P-7 documentation | CLOSED | routing/configuration docs and 0.5.8 plan |
23+
| P-8 config applicability (#34) | CLOSED | `5b86675`; 12 ConfigPane tests green |
24+
| P-9 release hygiene / #22 audit | IN PROGRESS | `0.5.8-release-evidence-2026-08-02.md` |
25+
26+
## Constraints
27+
28+
- `config.local.yaml` is a deliberate, default-empty extension mechanism and any
29+
machine-local override remains unstaged.
30+
- Liveness validation uses `/health/liveliness` only—never `/health`.
31+
- The release tag triggers PyPI trusted publishing; it follows, rather than precedes,
32+
a green GitHub Actions run.

dev/user-needs.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -968,9 +968,9 @@ multiples on every request until someone notices a bill.
968968
token. *(0.5.6)*
969969
4. The refusal response tells the caller what to send instead: a 404 whose
970970
`error.message` is self-sufficient prose, plus ranked suggestions with cost tier.
971-
*(0.5.7 F-3 — not yet shipped)*
971+
*(0.5.8)*
972972
5. Suggestions never include a model outside the caller's permitted catalog.
973-
*(0.5.7 F-3)*
973+
*(0.5.8)*
974974

975975
---
976976

@@ -1016,3 +1016,24 @@ only one of them is honest.
10161016
these; Airlock inherits them, confirmed by inspection and local calculation)*
10171017
5. Per-request-priced providers either record their cost or are explicitly documented
10181018
as unrecorded with a reason. *(0.5.7 F-4 step 3)*
1019+
1020+
---
1021+
1022+
## UN-31: Truthful Runtime Configuration Applicability
1023+
1024+
**As an** Airlock operator,
1025+
**I need** the TUI to tell me which configuration edits take effect immediately and
1026+
which need a proxy restart,
1027+
**so that** I do not either interrupt traffic unnecessarily or assume an inactive
1028+
setting is protecting a running proxy.
1029+
1030+
### Acceptance Criteria
1031+
1032+
1. Every ConfigPane control has an explicit applicability classification; controls not
1033+
changed by Apply are identified as such rather than presented as live.
1034+
2. Restart-required controls are visibly marked, including provider credentials,
1035+
listener address, failover map, and logging destinations.
1036+
3. Apply reports no change, live-only changes, restart-required changes, and mixed
1037+
changes accurately without exposing a secret or configuration value in its status.
1038+
4. Existing live controls—enforcement mode, PII/keyword controls, and MCP tool
1039+
allow/block lists—continue to update the runtime environment immediately.

0 commit comments

Comments
 (0)