Files
LocalAI/docs/content/development/addon-testing.md
T
mudler-agentandEttore Di Giacinto 7904b3d0ab feat(addons): declare typed host capabilities (#13034)
* feat(addons): model execution status observations

Keep observation lifetimes explicit before runtime integration. The pure
reducer rejects retries and stale replies using exact binding identity.

Cover all eleven trace families, the complete transition matrix and
bounded histories with safety and liveness checks.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): visualize execution status traces

Generate a self-contained viewer from the real reducer and shared traces.
Check independent expectations, coverage and bounded invariants before
exporting data. Detect stale artifacts and nondeterministic generation
without maintaining a second reducer in JavaScript.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): honor the viewer artifact root

Keep default viewer output under SDD_LOCAL_ROOT when configured.\nLet go generate use the same default without forcing a source artifact.\nRetain the portable fallback and explicit output and check semantics.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): add typed extension contracts

Validate optional home commands, execution bindings and requested host
operations without changing manifest versions or granting authority.
Decode bounded execution status payloads conservatively and document
strict staging compatibility for older hosts.

Assisted-by: nib:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): reject noncanonical extension keys

Reject case aliases before typed decoding can override canonical fields
or bypass presence checks. Keep locale maps outside exact-key checks.

Add regression coverage for status, declaration objects, and nested
entries. All status errors return unknown readiness and isolation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): reject root enums for status tools

Status tools must accept the empty object. The supported schema enums
contain only strings, so a root enum excludes every object.

Reject root enums only for execution status bindings. Cover incompatible
root constraints and optional properties without changing shared validation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): project typed home commands

Resolve declared home destinations and display metadata in the installed
Apps view. Pin packaged command icons to immutable asset URLs.

Cover strict declaration transport, immutable receipts, exact health echo,
and process restart. Keep legacy packages and disabled installed intent.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): preserve typed lifecycle declarations

Copy execution and host-operation declarations from installed receipts.
The existing capability completeness test exposes the missing fields.

Exercise real staged Manager upgrades, activation rollback and recovery.
Check installed Apps metadata through pinned mTLS frontends for each actor.
Verify mutation isolation and name receipt replay coverage accurately.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): stabilize fixture archive digests

Map iteration changes tar entry order between identical publishes.
Sort fixture filenames so unchanged packages retain their digest.

Check repeated publish bytes, digests and lexical archive entry order
with and without a manifest transform. Register the regression case.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): resolve explicit host policy grants

Load strict administrator policy files once before application startup.
Honor explicit empty overrides without reading ignored policy files.
Copy programmatic and manager grants to isolate startup authority.

Keep the legacy CLI fallback until the official deployment policy ships.
The generic resolver already defaults to empty grants.

Verify config, CLI, hostops and execution-status checks with GOMAXPROCS=2,
-p=2 and -ldflags="-s -w" to fit the available build disk.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): select explicit deployment grants

Move official host grants out of the generic executable into reviewed
host deployment policy. Stock Docker explicitly selects the shipped file;
unconfigured binaries and nil or empty programmatic grants grant nothing.

Document the standalone upgrade break and deliberate operator selection.
Do not infer consent from installed addons or merge explicit overrides.
Guard policy packaging and generic configuration boundaries with tests.

Verify executionstatus, viewer -check, config, CLI, packaging and root
architecture tests with auth, GOMAXPROCS=2 and bounded build parallelism.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): gate activation on required host ops

Refuse unmet required requests before package publication or runtime
withdrawal. Keep installed intent unavailable when startup grants change.
Optional requests permit degraded operation and never create authority.

Carry bounded admin diagnostics through Apps and the owner carrier.
Keep startup policy immutable and broker authorization authoritative.

Verify executionstatus, viewer -check, lifecycle, packages, updates,
hostops, Apps and gateway offline with auth and bounded parallelism.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): guard host operation registry parity

Compare startup admission names against real builtin registrations so
registry drift cannot silently change request satisfaction.

Cover dynamic progress and stop operations and connector inbound.
Reject missing, extra, duplicate and case-changed names with negative
controls. Record both regression cases in the suite inventory.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): fence status observation effects

Keep observation ownership in admitted runtime operations. Deny all
correlated callbacks before dispatch, including inbound-class callbacks.
Reject status outputs before callers can open or adopt artifact handles.

Retain bounded denial correlations until the runtime endpoint closes so
late callbacks cannot recover ordinary authority. Preserve independent
ordinary operations and document the dedicated read API for T9.

Verify the status model, viewer, and race-enabled gateway, hostops and
runtime suites with the pinned offline environment.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): drain callbacks before retirement

Server.Close does not wait for active callback requests. Clearing retired
observation correlations first can restore inbound authority.

Stop endpoint admission before closing connections. Drain admitted requests
before releasing correlations, and refuse dispatch after Stop. Keep locks
out of handlers and cleanup waits so cancellation cannot deadlock teardown.

Cover pending inbound callbacks, decoded authorization paused across Stop,
and handlers that ignore cancellation. Preserve ordinary authorized work
on other endpoints. Document delayed reclamation under the existing bound.

Verify the status model and viewer, then race-test gateway, hostops,
runtime, the model and root architecture checks with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): bind owner status to exact clients

Add authenticated, versioned status reads over the owner carrier.
Revalidate actor permissions and provider selection before dispatch and
before returning an observation. Keep status separate from authority.

Admit the captured client against its immutable identity under the
withdrawal lock. Never dispatch an old binding through a replacement.
Generate runtime operation IDs on the owner and retain the T8 effect fence.

Cover forged identities, strict DTOs, stale replies, restart ordering,
wire discriminators, and unchanged model exclusion of uiOnly tools.
Document the T10 callback and request-shell integration requirements.

Verify the model and viewer, then race-test gateway, runtime, tools,
and the model with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): compose owner status with runtime

Exercise installed addon processes through the authenticated mTLS carrier.
Keep runtime clients real while channel barriers control lookup ordering.

Reject captured clients after same-generation replacement before dispatch.
Reject completed status reads when replacement precedes owner consumption.
Prove fresh bindings succeed and record named tools and host operation IDs.

Verify the model and viewer, then race-test gateway, runtime, tools,
the model, and the protocol fixture package with auth enabled.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): scope execution status observations

Run the execution status reducer within one explicit request lifetime.
Coalesce consumers without retries and withdraw stale or canceled reads.

Keep owner admission and production lifecycle integration separate for
review before wiring the shell into requests and turns.

Assisted-by: OpenAI:ChatGPT
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): isolate consumer cancellation

Consumer cancellation during validation must not withdraw a valid
observation from other consumers. Return unknown locally before shared
invalidation, while preserving parent closure and freshness failures.

Cover cancellation and deterministic deadline expiry with one-read
reducer traces and live-consumer checks.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): track selection mutation epochs

Detect selection ABA at config and owner mutation boundaries. Clone
provider pins for Studio readers and share synchronized selection state
across config value copies.

Track lifecycle publication, candidate rollback and recovery transitions.
Keep reads and unchanged publications stable. These lifetime epochs do
not replace current-incarnation checks or grant execution authority.

Verify race tests for config and the add-on Manager, plus application
compilation with auth tags. Request binding remains separate work.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): expose owner binding transport

Add a host-only binding API with an optional owner resolver. Forward
conversation claims over authenticated mTLS with strict versioned DTOs.
Compare full bindings during validation and deny missing resolvers.

Keep ordinary tool execution unchanged through an optional backend.
Test trusted resolver fixtures, stale bindings, denial and compatibility.
Application authority and store revisions remain a separate milestone.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): fence selection snapshots with epochs

Read actor-scoped records and opaque mutation tokens under store locks.
Invalidate tokens after successful eligibility changes, including ABA.
Keep reads, rejected writes and turn history from invalidating tokens.
Clone mutable records so callers cannot bypass mutation tracking.

Support authoritative revisions only for memory-only JSON stores.
File stores lack exclusive ownership; GORM lacks persisted revisions.
Fail closed for both rather than trust frontend-local state.
Ordinary chats remain unchanged; application integration stays separate.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): enforce persistent JSON ownership

Acquire lifetime sidecar locks before loading or repairing JSON data.
Enable selection revisions only under cooperating exclusive ownership.

Close drains operations before releasing ownership and rejects later use.
Clean up unpublished stores when application initialization fails.
Retain published locks through Shutdown because HTTP users may remain live.

Document coordinated upgrades and the advisory local-filesystem boundary.
Keep GORM revisions unsupported pending a separate approved design.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Codex:gpt-5

* test(application): isolate warning log capture

Run warning assertions in isolated test subprocesses. The suite leaves
background log producers running, which race with global logger changes.

Keep the parent logger unchanged and check child exits and completion
markers. Preserve pin persistence and ownership warning assertions.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): resolve local owner execution bindings

Resolve execution providers from owner-held chat and agent snapshots.
Fence bindings with selection epochs, mutation tokens and live runtime
identity. Recompute every component during status authorization.

Keep named-user and SQL observations unavailable until authoritative
access and shared selection revisions can detect ABA changes.

Drain restart test producers before releasing JSON store ownership.
Register six product resolver cases in the gateway suite inventory.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): bound product scopes to chat turns

Keep request-local product state alive through tool calls, checkpoints
and persistence. Give chat priming its own scope instead of seeding the
next turn with a closed observation.

Exercise one reducer read per scope across prompt and tool consumers.
Check closure of pending reads on normal completion and interruption.
Provider wiring and safety projection remain separate work.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(studio): consume typed execution providers

Replace the Sandbox-specific status heuristic with owner-bound typed reads.
Share one reducer observation across turn consumers and close it at the
outer scope boundary. Keep priming and standalone requests independent.

Sandbox exposes a separate callback-free typed read while preserving its
private Settings payload. Unknown backends never imply isolation.
Provider isolation does not prove host network-policy enforcement.

Local-owner JSON bindings are supported. Named-user and SQL bindings
remain explicitly unavailable and fail closed.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(application): isolate auth startup lifetime

Shutdown does not join every background producer. Auth startup tests
can therefore delete their temporary directory while a writer remains.

Run each case in a bounded child process. The parent waits for child
exit before deleting the fixture, preserving every startup assertion.
Production shutdown and JSON ownership retention remain unchanged.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(ui): resolve installed Home app commands

Project Home navigation from installed addon declarations, not product IDs.
Reserve native and skill names and suppress every colliding addon alias.

Re-read the authenticated registry before navigation and reject stale
actors, generations, immutable entries and destinations. Keep mounted
consumers polling through disabled and failed states. Abort pending reads
on actor changes and ignore replies from obsolete request epochs.

Studio declares its own alias. Preserve legacy redirects and pin migration.
Add unit coverage, localized Home copy and command lifecycle documentation.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): accept resolved Home command metadata

Apps resolves inherited titles beyond the explicit manifest label cap.
Bound resolved labels to 147 Unicode characters, including secondary pages.
Keep the backend's 60-character limit for explicit manifest labels.

Match packaged icon paths to the contract's ASCII alphabet and image types.
Accept dotted names while rejecting traversal, encodings and foreign assets.

Verify 68 focused cases, 657 total cases, lint, i18n checks and the UI build.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): bind safety notices to actual providers

Warn for addon host execution and unknown isolation without changing tool
permissions or built-in host consent. Reset dismissal when the admitted
binding, observation, actor or chat context changes.

Expose presentation-only status identity from the admitted request.
Keep configured network policy visible when enforcement is unverified.
Render bounded provider explanations as escaped React text.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): isolate command status across sessions

Invalidate command observations when actors or authentication sessions
change, before effects can expose a previous actor’s reply.

Keep actual host execution warnings generic instead of recommending
Sandbox installation or repair. Cover both regressions with source-hook
lifecycle tests and provider notice assertions.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): declare audited host requests

Declare actual official hostclient operations without inferring requests
from deployment grants. Keep optional settings and egress checks separate
from required workspace and job operations.

Verify all twelve manifests through strict package fixtures and assert
that requests never self-grant. Guard generic provider and Home dispatch
without banning historical routes, native Studio, or deployment policy.

Document the audited operations and unresolved named-user and SQL gaps.
Refresh Sandbox catalog metadata for its existing typed status tool.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): wire exact extension inventories and real provider selection

Run contract, official packaging and root architecture gates through actual report inventories. Exercise two installed execution providers through the real owner resolver and distinguish callback pass-through coverage. Share strict report validation with focused-scope handling and missing/skipped artifact self-tests; document direct evidence and remaining harness limits.

Assisted-by: Codex:gpt-5

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): expire registry reads on auth refresh

Bind registry authority to the actual authentication session epoch.
Same-user refresh now rejects old polls and pending Home navigation
before effect cleanup, even when permissions and metadata stay unchanged.

Exercise both races through the production registry provider harness.
Verify 59 focused tests, 669 total tests, lint, i18n, and the UI build.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(studio): resolve current execution selection

Use existing principal-scoped chat and agent reads for advisory status.
Revalidate current named credentials instead of requiring historical SQL
revisions. Local-owner mode also works with the existing GORM store.

Remove resolver nonces and historical revision prerequisites. Keep exact
runtime binding and existing status admission hooks. Document current-read
consistency without promising revoke-and-restore detection.

Verify the SQLite regression fails before the fix. Two separate full race
runs pass: application 262 specs (3 skips), auth 257, gateway 115, tools 91.
Cover named chat, API key and session credentials and current-state denial.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): drop selection revision tracking

Use current selection comparisons for advisory status instead of mutation
history. Keep actor, credential and conversation identity explicit in the
model. Hash current inputs at the gateway without exposing raw store data.

Remove unused epochs, revision interfaces and JSON ownership lifetimes.
Preserve current authorization, runtime identity, mutexes and atomic writes.
Document current-read semantics without historical ABA guarantees.

Verify all affected packages with the race detector and auth tag. Check
the trace viewer, prerequisite self-tests and 18 chat-command unit tests.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): scope Apps reads and provider status

Bind Apps replies and registry ingestion to their originating auth session.
Reject late replies before effect cleanup on refresh or account switch.

Use selected-provider command DTOs in the composer instead of private
Sandbox discovery. Keep unknown isolation unknown and remove specific
add-on installation recommendations from the affected native surfaces.

Test production Apps, registry, switches and composer paths with controlled
cleanup and two arbitrary providers. Update the matching feature docs.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): guard composer and session publication

Remove the deleted Sandbox variable from the full composer render.
Report unknown command isolation explicitly in the about card.

Bind Apps records and admin action results to their originating actor
and actual authentication epoch before cleanup can run. Reject stale
request admission and verification without changing server authorization.

Add full-component render and pending session-transition regressions.
Enable no-undef for the production composer in normal UI lint.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): expire cancelled upgrade reconciliation

Bind cancellation reconciliation to the initiating session authority.
Discard status replies after expiry without publishing old outcomes.
Match the exact operation identity before clearing a held action.

Cover pending cancellation reads, expiry before reads, and preservation
of a newer attempt with the same action key.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): bind background upgrades to auth lifetime

Keep background upgrade authority in AuthProvider, independent of views.
Expire pending observations before auth consumers render or logout waits.
Preserve same-session navigation and exact-operation cleanup.

Cover cancellation, completion, and verification after the origin unmounts
with actual provider transitions and the real upgrade store.
Thread captured authority through Activity retries and document expiry.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): remove implicit sidebar pins

Start every account without implicit Studio or Talk pins. Preserve saved
user selections and durable explicit pins across reopen and frontends.

Remove startup seeding, seed metadata APIs, and first-install exceptions.
Apply equal retention to all absent IDs without dropping database tables.
Keep store initialization, cached errors, and the leader sweep unchanged.

Verify genuine RED regressions before removal. Race checks pass for all
42 pin specs, 10 focused application specs, and 10 pin endpoint specs.
Update pin documentation; renamed specs have no exact inventory entries.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* feat(addons): declare typed model destinations

Let installed addons own model launch destinations without teaching core
about addon identities or private routes. Keep native chat and completion
outside the declaration vocabulary.

Validate strict keys, unique capabilities, declared pages and SDK route
bounds. Carry cloned typed metadata through Apps using the shared
contract type, and add owning Studio, Talk, Face and Voice declarations.

Verify strict contract RED before implementation, then 1276 passing
specs across contract, Apps, packages, runtime, protocol, config and manager.
Verify 178 packaging and architecture specs and exact carrier inventories.
Frontend dispatch and route cleanup remain a separate milestone.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(ui): resolve declared model launch actions

Resolve installed model actions through the shared addon registry. Keep
native chat and completion launches, and never promote absent providers.

Refresh actor-filtered Apps before navigation and reject stale authority,
provider metadata, generation, immutable entry and destination claims.
Use named menu choices for collisions without loading addon modules.

Remove unreleased addon-specific redirects and migrate browser fixtures
to canonical declared pages. Preserve native chat handoff and addon-local
navigation adapters. Update user documentation and historical plans.

Verify three production UI consumers fail against their previous code,
then pass all 13 mocked-HTTP browser regressions. Verify 696 host unit
cases, full quiet lint, targeted no-undef, locales, inline styles and build.
Full backend/browser integration remains outside these mocked-HTTP checks.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): validate Ginkgo hooks before focus

Ginkgo emits null container hierarchies for suite hooks. Require hooks\nto pass before applying focus only to It cases. Normalize null\nhierarchies without weakening exact required-case comparisons.\n\nRun reporter regressions in prerequisites. Register the declared uiOnly\ntool case and remove an update fixture case excluded by the existing\nmanager focus. Both inventories match the complete CI case output.\n\nVerify 12 reporter regressions, 39 prerequisite cases, three existing\nreports, and fresh runtime, tools and focused update manager suites.

Assisted-by: Codex:gpt-5 [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): restore Go CI contract checks

Align route fixtures with explicit pins and declared network controls.
Keep network enforcement unknown instead of inferring it from isolation.

Propagate viewer output errors and select output paths with CLI arguments.
Check test writes and use Ginkgo assertions without copying atomic state.
Register the three migrated binding cases in the exact suite inventory.

Document two narrow security false positives for administrator file access
and symbolic credentials in reducer traces. Focused race tests pass.
The available golangci-lint reports zero issues in affected packages.

Assisted-by: OpenAI ChatGPT [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(ui): align browser coverage with addon contracts

The generic provider refactor removes implicit install advice and private
Sandbox discovery. Update browser fixtures to use the installed command
metadata and typed execution observations without restoring those paths.
Keep command warnings, consent, disabled switches and privacy assertions.

Home now holds a live command consumer. Exercise polling shutdown on a
route without that consumer, after its lazy transition finishes. Preserve
the bounded cadence, error backoff and disable-stop assertions.

Restore the remembered-voices link through the addon's compatibility
router. Cover native migrations and NotFound for removed generator aliases.

Validation: 293 affected browser cases and three native service cases pass.
The gallery command passes all 74 inventoried cases. Full UI units, quiet
lint, locale parity, production build and diff checks pass.

Assisted-by: OpenAI ChatGPT [functions]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* fix(addons): bind status to owner provider policy

Resolve typed status with the same current owner pins as execution.
Fail closed on owner policy outages and fingerprint authoritative pins.

Exercise CAS updates, conflicts, stale bindings, and policy outages.
Retain host settings when the named-account fixture replaces its gateway.
Refresh exact gateway and Apps inventories from passing reports.

Validation: the new regression fails before the fix. All 16 focused
binding and policy cases pass. Gateway passes 361 exact cases and Apps
passes 265 exact cases across their configured package reports.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): share qualified tool identities

Use one formatter and contract-component parser for add-on identities.
Keep callable names bounded to 64 characters while recognizing long
unavailable stand-ins without granting dispatch authority.

Validate selected execution names and their provider ID at the owner
binding boundary. The real adapter already emits canonical identities.
Update exact importer and case inventories from passing reports.

Migrate the binding backend test to Ginkgo and check shutdown errors
reported by lint in the affected packages.

Validation: behavioral RED proves malformed stand-in acceptance and
plain selected-name acceptance. Affected full race suites pass: tools
94 and Studio 638, with zero skips. Application passes 275 of 278;
three existing PostgreSQL cases skip without their configured URL. All 12 owner-binding cases
pass and match the exact inventory. Scoped golangci-lint reports no issues.

Assisted-by: Codex:gpt-5 golangci-lint
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* refactor(addons): share execution declarations

Share pure declaration predicates across manifest validation, gateway
observations, and owner bindings. Keep authorization and exact-runtime
admission checks local. Reuse the strict manifest schema validator so
raw status definitions fail closed without changing manifest acceptance.

Share the workspace-operation prefix scan without changing grant sources.
Add declaration parity and workspace-prefix tables and update exact
inventories from passing reports.

Validation: two new gateway cases fail before the fix; all 14 pass after.
Race tests pass: contract 245, gateway 136, tools 94, and focused
application binding/workspace cases 22. Scoped golangci-lint: 0 issues.

Assisted-by: Codex:gpt-5 golangci-lint
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

* test(addons): retain document skill cases

Match the contract inventory to the passing report after rebasing.\nKeep the new document-provider case and updated reserved-name cases.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

Assisted-by: OpenAI:gpt-6.1-sol

* test(addons): follow owner-managed upgrades

Accepted upgrades belong to the owner and cannot be cancelled by UI.
Exercise observer expiry during reads, completion and verification.
Supply the real upgrade store to source-executed auth harnesses so
logout reaches its synchronous authority invalidation.

Match whole-package inventories to fresh Manager and gateway reports.

Assisted-by: Codex:gpt-5
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>

---------

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
2026-11-02 23:50:40 +00:00

36 KiB

+++ title = "Focused addon testing" toc = true description = "Prepare and verify the toolchain for focused addon tests." +++

The addon verification scripts separate explicit dependency setup from offline checks. Run all commands from the repository root. scripts/addons/suites.json lists the available suites and their exact case inventories. For typed extensions, run contracts, apps, runtime, gateway, and host-slots. The contracts suite includes all official package manifests and generic architecture guards. It runs ./pkg/addons/contract, ./pkg/addons/packaging, and the root architecture suite, each with an exact Ginkgo report inventory (including twelve official manifest audits, two policy packaging checks, and two architecture guards). apps inventories the generic UI fixtures; gateway inventories the real application owner binding fixture. The latter installs two differently named packages through the actual catalog and Manager, uses JSON chat/agent stores, selects the oldest provider, then pins the second and checks each declared execution/status tool and runtime identity. The gateway callback-only test separately checks honest binding pass-through.

The shared scripts/addons/ginkgo_report.py comparator checks actual report artifacts, not just JSON syntax. Every required case must pass exactly once. A report may declare an explicit focus matching its command's Ginkgo focus; only excluded specs may be skipped, never required cases. Harness self-tests exercise missing artifacts, missing required cases, and skipped reports. Unknown suites, empty selections, skipped required tests, and failed commands return a nonzero status.

Direct verification evidence and limits

The bounded review verification used offline caches, GOMAXPROCS=2, and GOFLAGS='-mod=readonly -p=2 "-ldflags=-s -w"'. Direct commands used go test -timeout=5m -count=1 -tags auth PACKAGE -ginkgo.json-report=REPORT for ./pkg/addons/contract, ./pkg/addons/packaging, ., ./core/services/addons/apps, ./core/services/addons/gateway, and ./core/application. Actual reports compared with the harness comparator contain 196 contract, 174 packaging, 2 architecture, 10 apps, and 115 gateway cases, all passed. The focused execution binding owner resolver run passed all 8 required cases; its excluded specs are not required cases. Full application testing exited zero with 256 passed and 3 PostgreSQL-dependent Files cases skipped because LOCALAI_TEST_POSTGRES_URL was unset; it is not an all-cases acceptance pass.

sh scripts/addons/prerequisites_test.sh passed its exact 38-case inventory, including missing/skipped report rejection. Model race testing and viewer tests passed, and go run ./pkg/addons/model/executionstatus/cmd/viewer -check verified trace coverage and deterministic data. Focused real owner artifacts with a removed or skipped required case were also rejected by the comparator. Existing contract coverage passed without an invented failing baseline.

These are direct checks, not a full harness pass. The commands and report inventories are wired, but sh scripts/addons/prepare.sh --check remains blocked by missing protoc 26.1. No downloads or integration preparation were performed. Browser acceptance and named-user SQL authority integration remain unverified; the JSON owner fixture does not establish either. Local evidence reports and logs are under .cache/addons/logs/review2-* and are not product artifacts.

Required tools

Provide these tools before running setup:

  • Go 1.26.x, with GOTOOLCHAIN=local. Other minor versions are rejected.
  • Node 22.12 or later within Node 22, with npm. The validated setup uses Node 22.22.0.
  • protoc 26.1. The general Makefile downloader uses a different version.
  • A native C compiler (cc, or the executable specified by CC) with CGO_ENABLED=1.
  • Python 3, POSIX shell tools, and the runtime libraries required by Chromium.

Setup and integration preparation require at least 20 GiB of free disk space before each major step. They do not install system packages or fetch Go, Node, or protoc automatically. Place locally provisioned executables in .cache/addons/bin, or put them on PATH.

For Linux x86-64, the validated tool archives have these SHA-256 checksums:

Archive Source SHA-256
node-v22.22.0-linux-x64.tar.xz Node 22.22.0 distribution 9aa8e9d2298ab68c600bd6fb86a6c13bce11a4eca1ba9b39d79fa021755d7c37
protoc-26.1-linux-x86_64.zip Protocol Buffers 26.1 release a7be2928c0454f132c599e25b79b7ad1b57663f2337d7f7e468a1d59b98ec1b0

Validate archive checksums before extraction. These archives do not apply to other architectures.

Explicit setup

Setup downloads Go dependencies, installs the pinned protobuf plugins, and runs npm ci against the React lockfile. It then installs Chromium through the local Playwright CLI, unless you supply an existing browser.

sh scripts/addons/prepare.sh --prepare

The protobuf plugin pins match the Makefile:

  • google.golang.org/protobuf/cmd/protoc-gen-go@v1.34.2
  • google.golang.org/grpc/cmd/protoc-gen-go-grpc@1958fcbe2ca8bd93af633f11e97d44e567e945af

Go resolves the second pin to module version v1.4.0. The check validates each plugin's embedded module version and checksum, not its version label alone. Go downloads use module checksum authentication. npm uses the lockfile's package integrity records.

The lockfile currently selects Playwright 1.58.2 and Chromium revision 1208 (Chrome 145.0.7632.6). The installed Playwright package selects the browser revision; the scripts never invoke npx. To provision only the browser after npm setup, run:

export PATH="$PWD/.cache/addons/bin:$PATH"
export PLAYWRIGHT_BROWSERS_PATH="$PWD/.cache/addons/browsers"
core/http/react-ui/node_modules/.bin/playwright install chromium

Keep PLAYWRIGHT_BROWSERS_PATH set to this worktree-local path for manual installation. The preparation and verification scripts set it automatically. A browser installed in Playwright's default user cache does not satisfy this setup.

Alternatively, set PLAYWRIGHT_CHROMIUM_PATH to an absolute executable path before setup and verification. Setup then skips the browser download. Verification still requires a successful browser launch and rendered UI. Neither option installs OS libraries. If launch fails, inspect the reported library names and the browser executable with ldd on Linux. Provide a compatible runtime environment or report the missing libraries as a blocker; do not skip the browser probe.

Prepare generated inputs

After explicit setup, generate only the inputs needed by the focused tests:

sh scripts/addons/prepare.sh --integration

This command generates pkg/grpc/proto, builds the React distribution, and builds two auth-enabled test binaries:

  • tests/e2e/mock-backend/mock-backend
  • tests/e2e-ui/ui-test-server

It does not build inference backends or run a full repository build. It disables Go dependency downloads and requires the populated setup cache. Repeat this step when the proto, UI, or test-server sources change.

Check and verify

Inspect the toolchain, installed dependencies, and generated inputs without downloading or rebuilding them:

sh scripts/addons/prepare.sh --check

This read-only dependency check is not a browser or SQLite runtime test. Run the complete readiness gate separately:

sh scripts/addons/verify.sh prerequisites

Verification performs these checks:

  1. Run isolated shell fixtures for missing tools, incompatible versions, failed children, invalid reports, and empty or skipped selections.
  2. Inspect the real toolchain and generated inputs.
  3. Compile and execute a SQLite transaction with CGO, Ginkgo v2, and Gomega.
  4. Start the real UI test server with the mock backend on an isolated loopback port.
  5. Wait for server health, launch Chromium, and require a rendered React root.

The SQLite probe uses go test -count=1 -tags auth and a machine-readable Ginkgo report. Verification rejects missing, empty, failed, skipped, or unexpected test cases. It records child commands, output, and status in .cache/addons/logs/verify-*.jsonl. Ginkgo reports use separate unique files in the same directory. Server processes stop after the probe, including when the browser check fails.

Checks and verification never install dependencies or browsers. They set GOTOOLCHAIN=local, GOPROXY=off, GOSUMDB=off, and GOFLAGS=-mod=readonly for verification builds. The npm dependency check uses --offline. Missing cached modules or browser files are failures, not permission to download them.

Cache layout and test inventory

The scripts keep caches and transient results under .cache/addons:

Path Purpose
bin Local tools and protobuf plugins; prepended to PATH
go, go/pkg/mod GOPATH and GOMODCACHE
go-build GOCACHE
npm npm cache
browsers Playwright browser installations
tmp Setup, fixture, and verification scratch data
logs Command results and Ginkgo reports
downloads Optional archives from explicit local provisioning

Do not commit caches, generated distributions, or test binaries. Keep the command and required-case inventory in scripts/addons/suites.json synchronized with the runner. Add future suites only when their actual tests and commands exist. A successful prerequisite gate demonstrates readiness, not acceptance of addon functionality that has no tests yet.

Gateway verification

Run sh scripts/addons/verify.sh gateway for the owner carrier and real-auth HTTP suites. The inventory lists each report separately and requires their exact combined case count. Missing reports, skipped cases, empty reports, and unsuccessful child commands fail the gate. The tests use local certificate authorities, pinned mTLS peers, two receiving frontends, and a real SQLite identity database. No external identity service or addon port is exposed.

The disconnect fixture consumes the POST envelope before waiting for cancellation. Leaving that body unread prevents the HTTP/1 server from observing disconnect while its handler waits, which can strand test-server cleanup. Production owner handlers decode the bounded envelope before starting a relay.

Data recovery suite

sh scripts/addons/verify.sh recovery

The recovery suite checks backup, upgrade with a migration, restore and purge of add-on data. scripts/addons/recovery.sh runs, in this order: the node unit tests of the panel rules, the typed client and the locale keys; the Go specs of core/services/addons/recovery (every journal phase of an upgrade and of a restore, with failures injected through test-only seams, cancellation, a corrupt backup, too little storage, retention, grant resurrection and purge), the specs of the policy hold and restore filter, the specs of the Tables format migration, and tests/addons/recovery, which runs the real owner, real accounts and the real Tables executable over HTTP; and a Playwright spec against a UI test server. Set RECOVERY_SHOTS to a directory to keep the screenshots of the Playwright run. The suite is part of the foundation gate. Run sh scripts/addons/prepare.sh --integration first: the runner refuses a server older than the recovery code or a UI build older than the panel.

Foundation acceptance gate

The foundation gate is the end-to-end check of everything below the first add-on: the owner, the carrier, the lifecycle, the policy, the proposals, the file results, the Apps surface and the UI asset route. It is one integration fixture, not a set of fakes at the seams.

sh scripts/addons/verify.sh foundation-acceptance   # the end-to-end suite
sh scripts/addons/foundation_gate.sh                 # every foundation suite

Run sh scripts/addons/prepare.sh --integration first. The gate builds the server, the protocol fixture and the cluster command itself, so it never tests a stale binary.

The fixture

tests/addons/foundation/cluster starts three server processes of tests/e2e-ui: the owner and two frontends (front-a, front-b). Each has its own listener, data directory and carrier identity. They share one account database, and they speak the real pinned mTLS carrier. The frontends open no add-on storage and start no process. The add-on process is the protocol fixture (tests/addons/fixture/cmd), packaged three ways from a catalog served on loopback after the host was built:

Package Declares
found-ui a service, the tools echo and export, and a UI module
found-tool a service and the same tools, no UI
found-svc a service only: no tool and no UI

The fixture add-on reads fixture.json from its package. A mode changes how it fails: exit-start, never-ready, lose-reply (commit, then drop the reply) and orphan (ignore the closing of the startup channel). Its commits and every apply request are written to a durable log, so a spec counts side effects across restarts. Specs use real API keys and real browser sessions; the only route that is not product code is the owner's loopback control route that registers a resource, because the fixture has no add-on that creates one.

What the gate proves

Case group Proves
install, activate, alternate An install and an enable through a frontend reach the owner, start one process per add-on under the owner only, and reads alternate through the owner and both frontends
host unchanged The host binary and the shipped UI build have the same digest after the whole lifecycle
actor propagation The add-on sees the actor of the credential; forged actor and authority headers change nothing, and a forged administrator cannot reach lifecycle
JSON, progress, files, assets JSON and file relays and lifecycle progress work through a non-owner frontend, a private file downloads through another frontend, and UI assets are served by digest, immutable and only for declared files
denials A missing feature, a stranger, a stale generation and an undeclared tool give the same typed denial on every node
exactly once A confirmed change reaches the add-on once, even confirmed again through another frontend; a lost reply is outcome_unknown with the original operation ID and is settled by receipt lookup with no second apply
owner down Every frontend answers 503 with the same body, starts nothing and creates no storage; a restart brings back the last good package and the retained data
no duplicate owner A second owner on the same storage does not keep running, and each add-on keeps one process
orphans A process that outlived its owner is stopped before the next one starts
failed upgrade and enable A new generation that cannot start leaves the last good one running; killing the owner in the middle of an upgrade or an enable keeps the last good code, the retained data and a usable lifecycle
disable, remove, reinstall Data and grants outlive the removal, and a stale generation and its assets are denied after an upgrade
sessions and keys Ending a session or revoking a key on one frontend refuses it on every node
browser A member sees the add-on list only and the server refuses every management request; Open works through a non-owner frontend; a proxy path prefix works for deep links; a session ended on one frontend loses the assets on the others
service layer An upgrade that cannot start keeps the last good generation running and the data; a request canceled during the start puts the last good generation back, keeps the operation open and the same operation ID finishes it; one broken add-on does not stop the owner
regression The existing model and backend gallery specs (core/gallery), the gallery operation specs (core/services/galleryop) and the process adapter specs (pkg/model) pass

The regression packages are the narrowest ones that run offline in under two minutes each and that cover the code the add-on lifecycle sits beside. The core/gallery model install specs run with FIXTURES=tests/fixtures. The only skips the gate accepts are the specs that state they need macOS.

One pkg/model spec, "includes the exit code and final stderr line for an unexpected exit", replaces the process-wide logger to capture a warning. It is a test of the upstream package and the gate does not change it. Run together with the other specs of the package, goroutines of those specs can log while the logger is swapped and fail it at random. The gate therefore runs the package three times: a dry run that fixes the inventory, a run that skips that one spec, and a run that focuses on it alone. The case passes only when the dry run lists exactly one such spec, the second run passes every other spec (or skips it for macOS) and the third run passes that spec once. A spec that is not accounted for in exactly one of the runs fails the case. A real failure of the spec still fails the gate, with no retry.

Reading the result

foundation_gate.sh runs every suite in the gate set, in order, and prints the inventory: the passed count and the skipped count of each suite, and the total. It exits nonzero when a suite fails, reports a skipped case, or is missing from the set. A suite that is added to suites.json must be named in the script. The report of the parent review is this inventory plus the case list of foundation-acceptance in scripts/addons/suites.json.

Library sources suite

sh scripts/addons/verify.sh library-sources runs the library sources spec (core/http/react-ui/e2e/library-sources.spec.js) in a real browser, against a real UI test server that starts like a normal install: the default galleries, the default library sources of the add-on, backend and model kinds, and add-ons on with nothing installed. playwright.library.config.js starts that server itself on a free port. The spec adds, turns off, removes and restores sources, checks that each page lists only its own kind, and checks the "Use official libraries" setting.

Continuous integration

Each part of the add-on tests has a job that runs it, so a pull request cannot skip them:

What Where
Go tests under addons/ and the self-contained tests/addons/actione2e suite (real Sandbox and Office executables under the real supervisor) make test and the coverage run, job tests-linux in test.yml
Every suite of suites.json except packaging, in five groups addons-tests.yml
packaging addons-package.yml
UI unit tests of every add-on, the host UI unit tests, eslint and the locale ratchet tests-ui-unit.yml (make test-ui-unit)
Add-on pages in the browser against stubbed routes, and the shipped bundle tests-ui-e2e.yml

The suites of addons-tests.yml need the integration build, so each group runs prepare.sh --prepare and --integration once, then verify.sh for each of its suites. Run the same way locally with make test-addons-suites ADDON_SUITES='gallery-ui library-sources'. A suite added to suites.json must be named in a group of the workflow and in foundation_gate.sh or tables_gate.sh.

Packaging suite

sh scripts/addons/verify.sh packaging checks the first-party OCI package, its release channels and its workflow. It runs scripts/addons/package_test.sh, which prints one JSON line for each case. It needs no registry, no Docker and no network.

  • Go specs of pkg/addons/packaging. They build real layouts and then rewrite one part of them to prove each rule: missing entrypoint, missing declared UI, missing runtime library, manifest or platform mismatch, unexpected layers or media types, digest mismatch, invalid permissions, traversal paths, a bundled React runtime and a partial platform set. They cover the event and channel table (empty, Unicode and long refs, latest, development, semver-like names and two refs that normalize alike). They publish to an in-process registry and check immutable tags, digest-identical retries, stale promotions and a failed promotion that leaves aliases unchanged. They rewrite a branch so that the commit an alias names is gone from the checkout, and check that only a push to the channel's own ref takes it over, that development refuses, and that other git errors fail. The workflow policy has one mutated copy of the workflow for each rule, including the validate chain (publish must need validate, validate must not use continue-on-error, always() or failure()).
  • tests/addons/packaging. It builds the Tables executable, runs package.sh, publishes to a loopback distribution, signs the platform image with the fixture authority under the identity of the rendered catalog entry, installs it with the real installer and starts the executable under the real supervisor. A signature of another workflow identity, an owner policy that names the backend workflow and a wrong pinned digest are each refused.
  • Shell cases for package.sh, channel.sh and addonpkg policy, and static checks that the packaging scripts never push, tag, sign or log in and never name a backend registry.

The suite never signs, pushes or creates a tag against a real registry. The workflow itself (registry login, keyless signing, the verification command) is covered by the policy assertions and by review, not by a live run: running it needs the protected environment of the repository.

Tables acceptance

sh scripts/addons/verify.sh tables-acceptance is the end-to-end acceptance of the first-party Tables add-on. sh scripts/addons/tables_gate.sh runs every tables-* suite and then this one, in order, and prints the inventory like the foundation gate. It fails for a suite that is skipped, that leaves a process behind or that is added to suites.json and not named in the script.

What it runs, and what is real:

  • The cluster is the foundation cluster of tests/addons/foundation/cluster: one owner and two forwarding frontends, each a separate server process with its own listener, one shared account database and the real pinned mTLS carrier. The accounts are real: an administrator, the owner of a database, an editor, a viewer, a stranger and an account without the add-ons feature, each with a stored session and an API key.
  • The package is the real Tables. scripts/addons/package.sh builds the OCI layout of this platform (twice, for two versions). The suite checks every layer against its digest, joins the layers into the archive a catalog on loopback serves, and installs it through a frontend as an administrator does. Nothing is pushed, tagged or signed, and a receipt that says publishable is refused.
  • Calls use the public HTTP routes only. There is no route into the add-on that a user could not use, and no seam in the add-on: crashes are SIGKILL, a stopped process is SIGSTOP, an owner outage is a killed owner process.
  • Reads of the database by the test are read-only SQLite opens of the add-on's own file, to count the receipts it committed.

The specs, by what they prove:

Area Specs
Roles Owner, editor, viewer, administrator, stranger and an account without the feature, over the browser module route, the REST routes, the typed proposal route and the chat tool consumer, each through a different node. Blocked consumers (/v1/messages, /v1/responses, /v1/mcp/chat/completions) fail with the typed addon_tools_unsupported.
Credentials A paused key, a withdrawn feature, a lowered role (and the proposals it held), a revoked grant and a disabled account are denied on the next call on every node.
Authority Forged actor and forwarding headers, authority, role and approval fields, and every way to confirm a proposal without the proposer's own tab (a key with the right headers, the owner's tab, an administrator, a missing header, a tool-call marker, a wrong digest).
Consistency Schema and data staleness from two tabs, replay of an operation ID from every node, a conflicting request under the same ID, a burst from three nodes with exactly one receipt for each acknowledged ID, and a revocation that races a confirmation.
Crash The add-on process killed in the middle of a batch and the owner killed and restarted in the middle of a batch: every acknowledged change survives, no change is applied twice, and a worker repeats only the same ID. A stopped and killed process leaves an unresolved confirmation that blocks other writes until the owner role resolves it.
Lifecycle Disable, enable, uninstall and install: stale tabs and proposals of the older generation do nothing, and data and grants remain.
Files CSV through a frontend, downloads from every node by the requester only, a revoked viewer, a disabled add-on.
Outage With the owner down, a JSON read, the JSON relay, progress, status, file status, file download, an asset, the apps list, a proposal, the pending list, a confirmation, an apply and a chat tool call all fail closed (503 with the same typed answer from both frontends), and the frontends run nothing and create no storage.
Recovery Backup, verify and restore by the administrator's tab; a corrupt backup, a running add-on, a wrong confirmation, an unknown backup.
Upgrade An explicit check, a redacted status for a member, a key refused, a stale or changed pin refused, one pinned upgrade, the data and grants kept, a backup made first, and a replay of the same operation from another node.
Browser Approval cards from chat on one frontend and the result on the other, prefix deep links, keyboard, 400 pixels, both themes, the unavailable page while the owner is down and the reload after it is back, and nothing left after logout.

The evidence manifest

Each run writes .cache/addons/tables-acceptance-evidence.json: the commands the run executed, the case inventory with the state of every case, the native platform (operating system and architecture), the digests of both packaged versions (the archive the catalog serves and the image of the OCI layout), a statement that nothing was published, and, for a case that failed, the path of the retained trace (the logs of the three servers). A Go test (TestEvidenceManifest) checks it against the inventory of scripts/addons/suites.json: the platform is native, the digests are well formed and distinct, every case passed, and a failed case has a trace that exists.

Faults the real executable cannot show on its own

Three cases need a fault that the product executable does not have, and the product executable gets no flag or variable for them. The suite builds a second, test-only executable from addons/tables/cmd/tables-faults, packages it with the same scripts/addons/package.sh and installs it on one cluster. It serves exactly what the product serves. A spec arms a fault by creating a file in the fault directory of the add-on's data:

  • crash-after-commit ends the process after the next change committed and before the answer is sent. The spec checks that the host reports the original operation ID as unknown from the frontend, that the receipt lookup finds it committed, and that the same operation again returns the same receipt: one row and one receipt in total.
  • hold-output keeps a stream of the add-on from being served, so a transfer stays in flight. The spec disables the add-on and checks that the artifact ends without being downloadable on any node, that no byte stays on disk and that a new export works.

The expiry spec needs a short artifact lifetime. The fixture's cluster configuration has an artifactExpiryMillis field, which the test launcher passes to the owner as the programmatic AddonConfig.ArtifactExpiry option. No flag sets it and the default stays one hour. The spec checks that every node gives the same denial for the expired file and that the database and the backups are untouched.

What it does not prove

  • Other platforms. The evidence is of the platform it ran on. Advertising another platform needs a native run on that platform; the platform matrix keeps it disabled.
  • The free space seam is a storage bound that needs a seam the real add-on does not have. The recovery suite covers it with an in-process seam. The expiry and the withdrawal of an artifact are covered here (see below).
  • A needs-recovery upgrade needs a data format change that fails to migrate. The recovery suite covers it; this suite covers the successful upgrade and the restore.
  • Which interleaving a race takes. The race and the crash specs accept whatever order happens and check the invariants. The committed-and-lost-reply case is not left to chance: see the fault specs below.
  • A refused request is a typed 400. A request that the add-on refuses as invalid (an unknown table, an unknown field) is 400 invalid_request on every route: the runtime client types the add-on's HTTP 400 and 422 as a refusal, and the host answers it the same way from the apply, read, proposal, chat and artifact paths, and from the owner and every frontend. The authorization specs pin the exact status.

Settings authority in both topologies

For every add-on settings surface, run combined-mode and owner-with-two-frontends cases. Save through one frontend and verify the other frontend and the real consumer agree. Repeat a restrictive update, restart the owner, and interrupt owner access. Assert explicit errors rather than frontend-local settings or built-in fallback. Exercise CAS conflicts, per-key provider deletion, uncertain retries, and preservation of unrelated fields. Network policy tests must use new HTTP and CONNECT sockets after revocation; established streams may finish. Add rendered-form tests for the actual dispatch payload and disabled controls during failed reconciliation. Follow the existing distributed-state checklist before introducing a new settings path.

Document skill migration checks

Run the contract, package, and packaging gates separately:

sh scripts/addons/verify.sh contracts
sh scripts/addons/verify.sh packages
sh scripts/addons/verify.sh packaging

The packaging tests use the actual skill-tree validator on Sandbox assets. They check reference coverage, local links, notices, receipt hashes, and existing file and byte limits. Package lifecycle tests cover install, upgrade, materialization, and uninstall. Core tests cover stale built-in cleanup, preserved user overrides, and unchanged document extraction.

For guest execution, supply a real package whose guest image includes the document helpers:

sh scripts/addons/sandbox_smoke.sh --layout "$SANDBOX_DOCUMENT_LAYOUT" --require isolated

This smoke runs the generated PDF fixture checker through the real Sandbox command route. It requires gVisor or KVM; an unavailable runtime is a failure, not evidence of helper execution. The checker lives at /usr/local/lib/localai/document-skills/check-document-skills.py in the guest. A stand-in image reference from --local-check cannot establish guest execution.

Run the focused UI checks with Node 22 and a dedicated server built from the current tree:

cd core/http/react-ui
node tests/unit/run.mjs chatskills.test.mjs
PLAYWRIGHT_EXTERNAL_SERVER=1 PW_WORKERS=2 ./node_modules/.bin/playwright test \
  e2e/chats-skills-picker.spec.js e2e/agents-skills-real.spec.js e2e/skills.spec.js

Build the UI and test server first, as described above. Start your own server on the configured free port. The picker specs exercise rendering and switches; they do not alone establish real provider eligibility. Also test actual provider resolution, grants, pins, OFF persistence, and index/load agreement against the application. Require native linux/amd64 and linux/arm64 execution evidence before claiming release verification. Cross-compilation or a configured CI matrix alone does not establish native execution.

Upgrade submission and observation regressions

  • go test -race ./core/services/addons ./core/services/addons/gateway ./core/services/addons/recovery -count=1
  • cd core/http/react-ui && node --test tests/unit/updatereview.test.mjs
  • cd core/http/react-ui && npm run build
  • Verify cancellation during the real verified-backup path does not cancel an accepted upgrade or prevent its package commit.
  • Verify status reads and same-ID retries through two real mTLS frontends remain available while backup exceeds an injected ordinary client deadline.
  • Verify payload conflicts, actor isolation, terminal receipt replay after restart, interruption without replay, and Close cancellation/join behavior.
  • Verify browser reload observes the stored ID, account switching isolates records, logout clears records, and progress does not invent percentages.

The recovery suite above remains required for crash boundaries during backup, staging, migration and publication, including format-changing recovery-required outcomes. The focused submission tests do not replace the real executable, real-auth HTTP or rendered-browser acceptance gates.

Current-read execution bindings

ApplicationConfig.ChatSelectionSnapshot returns synchronized, detached current provider pins and host execution mode. Store reads preserve actor scope and return detached records. JSON stores use their existing mutexes and atomic writes; SQL stores use ordinary current reads. No revision ledger or directory lock is needed.

The gateway compares current selection inputs and runtime identity before dispatch and after the reply. The reducer binds explicit actor, credential, conversation, provider, tools and runtime identity. These checks are advisory, not a transaction or a guarantee to detect revoke-and-restore changes between reads.

Model action contract milestone

Run from the repository root (these packages share the root Go module):

go test -count=1 -v -tags auth ./pkg/addons/contract ./core/services/addons/apps ./core/services/addons/packages ./core/services/addons/runtime ./addons/internal/protocol ./core/config

modelactions_test.go covers strict keys, capability exclusion, traversal and SDK path/count bounds, full manifest round trips and owning manifests. Apps specs exercise actual installed projection and slice isolation. The package and protocol declaration matrices include model actions independently and together with all other extensions. Runtime specs verify exact registration equality, crash recovery, receipt replay and immutable registry snapshots. Core/config owns vocabulary parity; the contract package does not import core/config. Exact contract, Apps, package and protocol spec names are in scripts/addons/suites.json. These unit checks do not certify browser dispatch, PostgreSQL services, or full-system harness prerequisites.

Model launch and canonical route regressions

core/http/react-ui/e2e/addon-model-actions.spec.js exercises the production React InstalledModels inspector, gallery Open and ModelPage Overview renderer. Each surface covers arbitrary single providers, deterministic named collisions, encoded selected model values, absence without install promotion and stale installation refusal. The removed-route case asserts NotFound without redirect. The tests mock HTTP responses; they do not verify backend authorization or multi-node integration. Node tests in addonmodelactions.test.mjs cover bounded metadata and dispatch rejection after actor, session, generation, entry or page/path changes. legacyroutes.test.mjs retains native chat handoff checks.

The canonical-address fixture audit includes audio-transform, chats-switches-real, chrome-audit, diarization-profiles, media-history, navigation, no-edge-rails, page-render-smoke, sound-expansion, every studio spec, talk, taste-polish, threed-animation, threed-gen and addon-navigation. Existing cases remain; standalone addon addresses become declared fixture pages, and selected models travel as query values. Addon-local navigation adapter coverage stays in place.

The execution-status viewer defaults to viewer.html. Use -o /path/to/viewer.html to choose an output location explicitly; it does not read environment variables. -check alone verifies the model without writing a file.