Skip to content

Lasagna-010726/seam-integration - #14

Merged
Arcoders merged 33 commits into
LASAGNA-020626/isolation-hardening-and-benchmarksfrom
LASAGNA-010726/seam-integration
Jul 1, 2026
Merged

Lasagna-010726/seam-integration#14
Arcoders merged 33 commits into
LASAGNA-020626/isolation-hardening-and-benchmarksfrom
LASAGNA-010726/seam-integration

Conversation

@Arcoders

@Arcoders Arcoders commented Jul 1, 2026

Copy link
Copy Markdown
Owner

AI

Add a new layer to the Lasagna: the AI satellite. Your SaaS can now talk to AI models without getting burned (fail-closed), without making a mess (SSRF pinned), and without serving the same dish twice (tenant-reserved budgets). Bon appétit. 🧑‍🍳

Arcoders added 15 commits June 30, 2026 21:18
… (SEAM-1)

Add a required tableLocation(tenant) to IsolationDriver returning a closed
tagged union (schema | database | rowscope | connection), so an extension
places per-tenant tables without hardcoding a tenant_<id> namespace and without
branching on the driver. Implement it on all four shipped drivers, bump
ISOLATION_CONTRACT_VERSION 1 -> 2 (the internal isolation-driver contract, not
the package semver), and pair the bump with an UNCONDITIONAL registry presence
gate that throws for any driver missing the method (drivers at contractVersion 1
and unversioned drivers included), so the requirement surfaces at boot rather
than at first call.

Also add the optional MigrateOptions.extraMigrationPaths? field (reserved for
the per-tenant satellite-migration wiring, riding the v2 bump) and export the
TableLocation types from the root entry, /services, and the isolation barrel.
Update the custom-isolation-driver cookbook, regenerate the api-extractor
golden, and record the seam in the curated [1.0.0] CHANGELOG. Red-first:
isolation_table_location.spec.ts covers each driver's union, rowscope carrying
no schema/rlsGuc key, fail-closed identifiers, an exhaustive never-default
consumer, and the presence gate in four forms.

Contract note (pre-1.0, no release yet): a custom IsolationDriver implements
tableLocation() and sets contractVersion: 2. The four shipped drivers are
updated; hosts using only the shipped drivers need no action.
…AM-3)

Add reserve()/settle()/release() to QuotaService so an operation whose true
cost is only known when it finishes (a streaming model response) can hold its
worst-case cost up front and reconcile the actual usage as it arrives. Backs the
AI-satellite streaming seam and is useful standalone for any metered streaming.

The model is "derive, never store": there is NO scalar reserved-counter. Each
live hold is a member of a per-(tenant,day,quota) sorted set scored by absolute
expiry, with amounts in a companion hash; the outstanding reserved sum is
recomputed from the live members on every reserve, and each reserve first evicts
score<now members. That eviction is the reaper, so a process that crashes
between reserve and settle has its budget reclaimed by Redis TTL alone, with no
timer, no scheduler, and no counter left inflated. QuotaService stays stateless.

- reserve(tenant, quota, worstCase): atomic single-EVAL hold against BOTH the
  per-tenant daily budget AND an optional operator-global ceiling
  (plans.operatorCeiling[quota], a denial-of-wallet cap), both-or-neither.
  Over-budget is a hard stop. FAIL-CLOSED BY CONSTRUCTION (not configurable): a
  Redis outage refuses rather than let an unbudgeted call through, the deliberate
  opposite of consume()'s fail-open.
- settle(reservation, cumulativeUsed): monotonic-cumulative, clamped to
  [0, worstCase]; a repeated or smaller total is a no-op. Fail-open.
- release(reservation): returns the unused remainder, idempotent under a double
  abort. Fail-open.

New public type QuotaReservation (exported from the root, /services, and the
services barrel). New config plans.operatorCeiling + plans.reservationTtlMs
(additive, no contract bump; consume() is unchanged). A quota is metered by
consume XOR reserve.

Designed and adversarially reviewed via multi-agent passes. The review caught a
real money bug the first cut missed: the operator holds/amt are shared across
tenants, so one tenant's reserve reaps another's expired op member while the
latter's own tenant hold survives, and the old settle then charged the shared op
counter for a reaped hold, inflating the ceiling permanently. Fixed by gating
each side's write on its own live member (settle) and giving the op cleanup its
own guard (release). Also hardened: exclusive reap boundary (no self-reap at a
reservationTtlMs of 0), non-finite settle coerced to 0 (no phantom-outage cost
dodge), and malformed-record-tolerant parsing.

Unit specs cover the TS orchestration and the fail-closed policy (with a mutation
check proving the spec bites). Real-Redis integration specs cover single-EVAL
atomicity, the over-budget hard stop, the both-or-neither ceiling, clamp and
monotonic settle, idempotent release, orphan reclaim by TTL, and the two
shared-ceiling regressions; the Lua was also validated against a live Redis.
Targets standalone/Sentinel Redis; Cluster (hash-tags plus a second-EVAL
ceiling) is a deferred follow-up.
Add operator tooling to install the PostgreSQL `vector` extension outside the
app's request-serving role, so the app role stays least-privilege and never runs
CREATE EXTENSION. Backs the AI-satellite vector store and is useful standalone
for any pgvector host.

- provisionVectorExtension(): CREATE EXTENSION IF NOT EXISTS vector under a
  privileged provisioning connection (isolation.provisionConnectionName, default
  centralConnectionName), dispatched by driver: once on the shared database for
  schema-pg/rowscope-pg, per tenant database for database-pg (cloning the
  privileged connection onto each), skipped for sqlite-memory and unknown
  drivers. Idempotent; doubles as the backfill for existing databases.
- tenant:vector:provision --dry-run --tenant: the operator/backfill command.
- pgvector_extension doctor check (opt-in): asserts the app role is not a
  superuser and the extension is present where embeddings live.
- config isolation.provisionConnectionName; CI Postgres image -> pgvector; a
  docs/reference/commands.md section; the curated [1.0.0] CHANGELOG.

Dispatched by driver.name (not tableLocation), so this PR is independent of the
SEAM-1 branch. The database-pg tenant-creation hook and its "new tenant after
backfill" ordering test are deferred to the SEAM-2 PR, where an embeddings
migration finally exists to order against and to test.

Additive, no contract bump. Targets standalone Postgres.

Adversarially reviewed; the review hardened five real gaps. The backfill loop and
the doctor check now isolate per-tenant failures (one unreachable tenant database
no longer aborts the run or the whole check) and report a failed count; a warning
fires when provisioning falls back to the app's central connection; unknown or
custom drivers are skipped rather than assumed central; the database-pg driver's
databaseName()/connect() are contract-guarded; and each throwaway provisioning
connection gets a unique per-invocation name so concurrent runs cannot close each
other's pool. Unit specs cover the dispatch, dry-run, per-tenant failure
isolation, and the unknown-driver plus missing-contract paths; a real-Postgres
integration spec self-skips when the image lacks pgvector.
… contract

pinnedFetch's custom `lookup` returned a single (address, family). Since Node 20,
autoSelectFamily (default true) invokes a custom lookup with `{ all: true }` and
requires an array result `[{ address, family }]`; a single-address callback there
throws ERR_INVALID_IP_ADDRESS at connect. On Node 24 (the package's engines floor)
this broke EVERY pinned outbound request — webhook delivery and OIDC token/discovery
— and the failure was wrapped as a retryable `network_error`, so it read as flaky
networking rather than a hard bug.

It went unnoticed because the completing pinned path was never exercised in tests:
the pinning specs only cover pinned mode REFUSING a private/loopback target (which
fails closed at validation, before the socket) and reaching a listener via
allowLoopback (plain fetch, not pinnedFetch). Pinned mode blocks every private
range by design, so the httpsRequest + custom lookup only ever runs against a
public address.

Extract the pin into `pinnedLookup(address, family)` and return the pinned address
in whichever shape the caller asked for. It is the same single validated address
either way, so the DNS-rebinding pin is unchanged. Add a deterministic unit
regression on the { all } contract and a fault spec that drives the real https
connect path under default Happy-Eyeballs.

Verified end to end: safeFetch('https://example.com/') now returns 200 on Node
24.15.0 where it previously threw "Invalid IP address: undefined".
…n (SEAM-4 core)

executeExtension gains an optional `signal`. It is composed with the internal
timeout controller BEFORE the no-timeout fast-path return, so an external abort
(a caller timeout, a tenant suspension, a client disconnect) reaches `fn` even
when no timeoutMs is set. Previously the fast path returned a promise bound only
to the internal controller, silently dropping every external abort; with a
timeout set, the deadline still wins its race deterministically. Callers that
pass no signal are byte-identical (fn still receives a live, non-aborted signal).

New `utils/signals.ts`, exposed at the `/signals` subpath:
  - composeSignals(): the single AbortSignal.any composition point in the kernel,
    now reused by safeFetch's combinedSignal so the two cannot drift.
  - onRequestDisconnect(ctx): the ONLY sanctioned place that touches the raw
    inbound request (ctx.request.request). It bridges a client hangup to an
    AbortSignal, aborting only while the response has not finished (a
    writableEnded latch, so a clean end-of-response 'close' does not false-abort),
    and disposes its listener so nothing leaks. An @architecture guard
    (inbound_request_seam.spec.ts) fences the raw-request access to this helper.

This is the core lever the AI streaming gateway (SEAM-4 satellite) builds on:
the composed signal cancels the provider call, onRequestDisconnect feeds it a
disconnect trigger. Additive, no contract bump; the three existing executeExtension
callers pass no signal.
# Conflicts:
#	packages/core/CHANGELOG.md
The isolation override `{ rowScopeRls: true }` omitted `driver`, which
`IsolationConfig` requires. It was latent on the SEAM-1 branch (surfaced only
when the full test tree typechecks together on the integration branch). Match the
established pattern (isolation_scoping.spec.ts) and name the rowscope-pg driver.
…ths (SEAM-2)

A satellite can now ship migrations that run PER TENANT (embeddings, per-tenant
memory) rather than only once in the shared backoffice schema.

- The three real-migrate drivers (schema-pg, database-pg, sqlite-memory) delegate
  migrate() to a shared runTenantMigrations() that folds MigrateOptions
  .extraMigrationPaths into the run. Lucid's Migrator reads its source directories
  from the CONNECTION's migrations.paths (not from its options), so the helper
  clones the already-registered tenant connection into a throwaway registration
  whose migrations.paths is [...base, ...extra], runs the Migrator against it, and
  releases it. The shared connection is never mutated; the ledger and advisory
  lock live in the same physical database, so a fold is idempotent and the
  no-extra-paths path is byte-identical to the old inline code. rowscope-pg stays
  a no-op (row-scoped tenants use a central migration).
- SatelliteManifest gains perTenantMigrations: a directory of RUNNABLE per-tenant
  migration files inside the package, validated by the same isSafeRelativePath as
  migrations. It runs straight from node_modules, with no host copy step.
- tenant:migrate discovers installed satellites (manifests read as JSON, no
  satellite code imported) and folds each perTenantMigrations dir into every
  tenant's run. satelliteMigrationDirs() emits the dirs RELATIVE to the app root
  and forward-slashed: Lucid resolves each via new URL(dir, appRoot), which an
  absolute path breaks on Windows (the drive letter parses as a URL scheme), so a
  root-relative path is correct and ledger-stable on every OS.

The database-pg tenant-creation pgvector hook (deferred here from SEAM-5) lands
with the AI satellite's embeddings migration, where its ordering test has a real
migration to run against. Additive: MigrateOptions.extraMigrationPaths rides the
isolation contract v2 bump SEAM-1 already paid; no new break.
@Arcoders
Arcoders requested a review from AFellat July 1, 2026 14:12
ci.yml only triggers on push/PR to master|main, so the merged seam work
on this branch never gets the real-PG/Redis integration, billing, and
e2e tiers. Add this branch to the push filter so the whole suite runs
here for validation. Revert before merging into isolation-hardening.
@Arcoders Arcoders added the qa: product-testing Validation of product behavior, QA testing, and feature correctness. label Jul 1, 2026
Arcoders added 12 commits July 1, 2026 16:43
The full ci.yml suite ran green on this branch (run 28525464822: real
PG/Redis integration, billing, e2e, and the compose deploy stack). Remove
the temporary branch entry so ci.yml is back to master|main only and the
branch is clean to merge into isolation-hardening.
Re-add this branch to the push filter so the whole ci.yml suite (real
PG/Redis integration, billing, e2e, compose deploy) runs here again for
validation ahead of PR-6. Revert before merging into isolation-hardening.
…ruction

A QuotaReservation handle is server-only in-process state, but it is a plain
object a caller could forge or mutate. The safety against that is structural:
settle()/release() key every Redis op by the handle's own (tenantId, day, quota)
via the *Key builders, and address the hold by id, so a handle with a swapped
tenantId lands in a different namespace, misses the hold, and charges nothing.

Promote that implicit property into an enforced, tested invariant:

- security_quota_handle_tamper.spec.ts (real Redis): forged tenantId / holdId /
  quota / day are all harmless no-ops; a control case proves the assertions are
  not vacuous. Tagged @ai (cross-cutting slice, runs via --tags @ai).
- check-quota-key-tenant-scoped.mjs: fails if any file outside the key-builder
  module hand-builds a quota: key, so a refactor cannot drop the tenantId. Ships
  a --self-test and is wired into npm run check.
- keys.ts: add tenantKeyPattern() so reset()'s wildcard also comes from a
  builder, keeping the "every key from a builder" invariant absolute.
- Introduce the @ai Japa tag (AI_TAG constant) for the cross-cutting AI slice.
…vision

Three fault-injection specs (non-gating tier) covering the new SEAM surfaces
against real Postgres + Redis, all @ai-tagged:

- redis_outage_during_reserve: the reserve EVAL is made to reject with a real
  ECONNRESET through the requireRedis() seam; the real ResilienceService
  classifies it and reserve fails closed (DependencyUnavailableException) leaving
  zero holds, then recovers cleanly once Redis is healthy. Complements the
  resilience-tier policy unit test with real-infra classification + state proof.
- ceiling_flood_concurrent: a burst of reserves against a shared operator ceiling
  admits exactly floor(ceiling/worstCase) and never over-commits; a crashed
  winner's hold is reclaimed by TTL so a latecomer reserves again under
  contention.
- migrate_during_vector_provision: running tenant:vector:provision and migrate
  concurrently corrupts neither the extension nor the adonis_schema ledger —
  disjoint objects, no advisory lock needed. Self-skips without pgvector.

Document the ordering-not-locking rule in the commands reference.
reserve/settle/release and executeExtension routed errors through
ResilienceService but emitted no telemetry of their own, so an operator could
see "Redis degraded" but not "tenant over budget" or "ceiling 90% utilised".
Instrument the existing primitives — no prom-client, no new infra — matched to
call frequency:

- TelemetryService gains addEvent/addLink/addEventOnActive helpers over the OTel
  Span API the withSpan wrapper hid.
- reserve (once/stream) opens a quota.reserve span with hold_placed/refused
  events and an outcome of ok|over_budget|ceiling; release opens a quota.release
  span carrying the freed remainder; settle (per-fragment, hot) records a settle
  event on the ACTIVE span — never a span per fragment (no trace explosion).
- executeExtension opens an extension.execute span whose outcome classifies the
  terminal state; the fast path is preserved (behavior unchanged, spans are OTel
  no-ops without an SDK).
- /metrics gains bounded operator-ceiling gauges (committed/outstanding/
  utilization), DERIVED at scrape from the shared Redis op keys and labelled by
  quota only — never tenant_id (a cardinality bomb). O(#quotas), no tenant scan.

Names and attribute keys are single-sourced in services/observability/names.ts
(the dashboard/alert contract). Two disciplines are enforced BEHAVIORALLY rather
than by a fragile source regex: span names come from OBS_SPAN, and every emitted
attribute key is in the non-PII allowlist (ids/counts/outcomes only) — the
telemetry unit spec fails on a stray content attribute. All specs tagged @ai.
The WS3 fault spec imported SchemaPgDriver as a value but only uses it as a
type annotation and cast, tripping @typescript-eslint/consistent-type-imports
in CI's Lint job. Mark it `type` like every sibling driver spec.
…api report

WS2 added addEvent/addEventOnActive/addLink to the public TelemetryService.
The api-extractor golden report was stale, failing the API report gate, and the
two event helpers referenced a module-local `SpanAttrs` alias that api-extractor
flagged as a forgotten export. Match the existing `withSpan` convention in the
same class: inline the `Record<string, string | number | boolean>` attribute
type (no exported alias, no ABI expansion) and regenerate etc/saas-tenancy.api.md.
behavior_compliance_report's "oldest first" assertion relied on the three probe
rows keeping insertion order, but exportStream orders by (created_at, id) and the
three inserts can share a millisecond — then the tie breaks on the primary key, a
random UUID, so b/c came back swapped intermittently (green on the two prior CI
runs, red on this one). The production query is correct and deterministic; the
test was the flake. Pin each probe row to a distinct ascending created_at so
"oldest first" is decided by created_at alone, independent of insert timing.
…R-6 C1)

C1 of PR-6: a valid, RC-labeled @adonisjs-lasagna/ai package with the config
surface and provider skeleton the streaming spine builds on. No feature logic
yet; mirrors packages/reporting.

- AiConfig + defineAiConfig + assertAiConfig: eager boot validation, a per-tenant
  default-deny provider allow-list (G12), and named-constant defaults so nothing
  streaming-related is hardcoded. SatelliteConfigRegistry declaration-merge.
- AiProvider: assertSatelliteApiCompatAtBoot + assertAiConfig + the compile-time
  SatelliteProviderConstructor ABI pin.
- configure hook, the guarantee test tree, an RC .c8rc coverage gate, CHANGELOG,
  README (RC badge), the stability.md RC row, and docs/guides/satellites/ai.md.
- the eight-place new-workspace wiring: build:ai + build:all, the lockfile,
  check-satellite-config-wiring, check-abi-boot-assertion, the per-satellite
  ci.yml + publish.yml steps, and the stability matrix.

17 unit specs green; typecheck, check (21 guards), lint, knip:deps clean.
…e, exceptions (PR-6 C2)

C2 of PR-6: the provider abstraction the streaming spine calls through.

- AIProviderContract + AI_CONTRACT_VERSION=1: closed StreamFragment / AIStreamRequest
  / AICapabilities shapes (readonly), model + maxTokens as per-request input.
- AIProviderRegistry (Map-backed, container.singleton): mirrors BillingDriverRegistry
  plus the one divergence, an unconditional streaming-presence gate at registration
  (a provider not declaring capabilities.streaming fail-closes), and forTenant behind
  the per-tenant default-deny allow-list.
- resolveTenantProviderSelection: the isolated selection seam WS-AI-2 swaps for
  per-tenant BYOK storage with no signature change (it already takes the tenant).
- AIException: closed AIErrorCode union + FATAL_CODES + isRetryable() + the pinned
  402/429/503 status map; never carries an upstream body, key or prompt.
- assertNever exhaustiveness helper; MockAIProvider + checkAIProviderConformance
  shipped from ./testing.

39 unit specs green (presence gate, version compat, default-deny, exception codes,
conformance); coverage 92.6% lines; typecheck, check (21 guards), lint clean.
…ket-error handling (PR-6 C3)

C3 of PR-6: the isolated SSE frame writer the streaming integrator pumps through,
unit-testable without an HttpContext (it takes a plain sink double).

- SseWriter: well-formed id/event/data frames, multi-line data split into one
  data: line per line, monotonic ids continued from a Last-Event-ID resume cursor.
- Backpressure: write() === false awaits 'drain' before resolving, so a slow
  consumer throttles the pull instead of forcing unbounded buffering.
- Socket write-error handling (the review gap): a sink 'error' (EPIPE / hang up),
  captured out-of-band or during drain, rejects the write so the pump aborts
  rather than looping into a dead socket; dispose() detaches the listener.
- writeHeartbeat (':\n\n' comment) and writeErrorEvent (a code-only in-band
  'event: error' frame, never an upstream body). Named SSE constants, no inlines.

11 sse_writer specs green; coverage 94.8% lines; typecheck + lint clean.
C4 of PR-6: the orchestrator that pumps a provider's fragments to the client over
SSE, metering cost per chunk and resolving a discriminated StreamResult. Kept
thin over three unit-testable collaborators (SseWriter, FragmentPipeline, the
pump loop), per the review's decomposition point.

- StreamResult: completed | aborted(reason) | failed_preflight(error), closed
  unions. worstCase = the request maxTokens cap; settle is clamped to it (in the
  quota seam), so no fragment sequence can over-spend.
- Lifecycle: breaker + reserve pre-flight can resolve failed_preflight (402/429/
  503) before any byte; the first fragment (or a clean empty end) is the commit
  point after which nothing throws; the finally always settles used + releases the
  remainder, FAIL-OPEN so a transient Redis blip can never break it (the I3
  violation this guards against).
- Four-way composed abort (liveness/tenant_suspended, client disconnect, budget
  early-stop at worstCase, timeout) attributed to the right reason; a mid-stream
  provider error writes a code-only in-band event:error frame, never an upstream
  body; a broken socket write is a client_disconnect.
- executeExtension / ExtensionTimeoutError are INJECTED (not imported) so the
  module never pulls the core /services barrel (eager redis) into the unit
  runner; the provider supplies the real ones. Registered as a container
  singleton resolving QuotaService + CircuitBreakerService.

72 unit specs green (happy path, budget, pre-flight matrix, mid-stream aborts,
socket error, fail-open finally, fragment validation); coverage 96% lines.
Arcoders added 5 commits July 1, 2026 22:36
C5 of PR-6: the outbound half the AI providers need. An opt-in `streaming` on the
shared `SafeFetchOptions` hands the pinned response body to the Response as an
incremental web stream (Readable.toWeb) instead of buffering it.

- The pin is a connect-time `lookup` property, so streaming the body off the same
  socket keeps the IP-pin (no second lookup, no rebind); cancelling the body
  destroys the socket. Buffered remains the default, byte-identical for every
  current caller (the whole core suite stays green).
- Streaming is pinned-path only: combining it with trustedHost/allowLoopback (both
  buffered) throws `streaming_unsupported_mode` rather than silently no-op-ing.
- Because `SafeFetchOptions` is exported from the main entry, this changes the
  public surface: the core api-extractor golden is regenerated in this commit
  (check-api-report back in sync). Additive, no contract-version bump.

Red-first unit specs (the rejection guard + the Readable.toWeb incremental /
cancel-destroys-socket mechanism); the existing pinning fault spec still proves
the pin holds at connect. Core unit suite 1314 green; lint + api-report clean.
C6 of PR-6: the two pure functions where a vendor wire shape is known, split from
the providers so they unit-test exhaustively without a transport.

- parseSseFrames: the shared framing, correctly reassembling a frame split across
  chunk boundaries, dropping heartbeat comments, handling CRLF and LF.
- anthropic_sse: content_block_delta text + incremental message_delta usage; a
  malformed frame is skipped, an error event becomes a sanitized AIException.
- openai_sse: choices[].delta.content + final usage; the data: [DONE] sentinel;
  same skip-malformed / sanitized-error handling. DeepSeek and Kimi share it (both
  OpenAI-compatible), so one adapter serves both.
- Error mapping emits only a classified code, never the upstream body or a key
  fragment (a canary-in-the-body test asserts it).

13 parser specs green (text/usage extraction, split frames, sentinels, malformed
skip, sanitized errors); coverage 96% lines.
…ch (PR-6 C7)

C7 of PR-6: three real providers streaming through the kernel's SSRF-pinned fetch
with no vendor SDKs (an SDK's own transport would bypass the pin).

- HttpAiProvider base with an INJECTABLE transport (deps.fetch defaults to the
  pinned safeFetch({streaming:true}); tests inject a fake fetch), so the full
  adapter (request build to parse to error map) runs in CI without a live key.
  Mirrors SsoService's injectable-deps pattern.
- ClaudeProvider (Anthropic Messages, x-api-key + anthropic-version, /v1/messages,
  anthropic_sse) and OpenAICompatibleProvider (Bearer, /chat/completions,
  openai_sse) with DeepSeek + Kimi as thin subclasses. One adapter serves both
  OpenAI-compatible vendors.
- Nothing hardcoded: base URL, model, path suffixes and the anthropic-version are
  named-constant defaults + config overrides; the model is per-request input
  checked against the per-provider allow-list (G12). defaultModel is now optional
  (falls back to the provider's built-in).
- SSRF-safe: never passes trustedHost/allowLoopback; a pin rejection of a BYOK
  endpoint surfaces as byok_endpoint_blocked; a 429/5xx head throws before the
  first byte so the gateway resolves failed_preflight. A missing key throws
  config_missing (never a shared/env fallback); a canary-key test proves no key
  reaches a fragment.
- The provider boot() registers the allow-listed, configured built-ins (claude
  active); unconfigured providers are never registered. Real-API smokes per
  provider self-skip without their key.

97 unit specs green (request shaping, dialect parse, model override, secret
negatives, SSRF boundary, HTTP head classification); coverage 96% lines; check
(21 guards) + lint clean.
…, docs (PR-6 C8)

C8 of PR-6: the observability layer, the guarantee specs that need real
composition, and the production docs.

- Observability (injected, so the module stays unit-loadable): the streamed call
  is wrapped in an ai.stream span (tenant / provider / model attributes only,
  never content) and emits integer usage metrics (ai_requests, ai_tokens_total,
  ai_errors, and ai_stream_disconnects on the disconnect path). The provider wires
  the real MetricsService.emitMetric + TelemetryService.withSpan. A negative
  assertion proves no prompt/response content and no float ever reaches a metric
  or a span attribute (G3), and the attribute set is allow-listed.
- First performance guarantee spec: the first fragment is flushed before the
  producer completes (incremental, not buffered), and backpressure throttles the
  producer pull instead of buffering unboundedly.
- Integration: the AI provider's DI wiring resolves a StreamExtensionService and
  the provider registry against the real booted container (its quota / breaker /
  metrics seams are all makeable) through the shared kit Ignitor.
- Docs: the production-checklist heartbeat-below-proxy-idle-timeout entry (OQ#12).
  minMergedCoverage stays at the graduation floor (60), ratcheted off the first
  CI merged-coverage run.

103 unit specs green; coverage 96% lines; check (21 guards) + lint clean.
The AI-seam work on seam-integration is validated (full ci.yml green on
real PG/Redis + e2e + deploy), so the push trigger that ran the full suite
on this branch is no longer needed. Restore the default: ci.yml runs on
push to master|main and on PRs to master|main only.
@Arcoders

Arcoders commented Jul 1, 2026

Copy link
Copy Markdown
Owner Author

Fresh out of the oven. See you on the base branch there's a special ingredient waiting for you. 🍝✨

@Arcoders
Arcoders merged commit 4b7eefa into LASAGNA-020626/isolation-hardening-and-benchmarks Jul 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

qa: product-testing Validation of product behavior, QA testing, and feature correctness.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant