master
${ noResults }
182 Commits (master)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
45011ea4a7 |
Bloque B2 — auth handlers route DEVICES + DEVICE_REVOKE
Audit P1: `ActiveAuth.listDevices()` and `revokeDevice()` POSTed
to `/api/auth/devices` and `/api/auth/devices/revoke`, but the
generic handler (the same one used by the SvelteKit integration)
only routed current/csrf/password/recovery/sign-out — devices and
OAuth fell through to 404.
Closes the device gap end-to-end:
- `AuthHandlerEngine` (handler-runtime contract) now declares
`listDevices` and `revokeDevice`. The engine already implemented
them; the gap was purely in the handler surface.
- `createAuthRouteHandlers` adds two new handlers and registers
them in `handle()`:
- `GET /api/auth/devices` → `engine.listDevices({ actorRef })`
- `POST /api/auth/devices/revoke` → CSRF-verified, body
`{ deviceId, meta? }` → `engine.revokeDevice({ actorRef, ... })`
- Both derive `actorRef` via a new internal `requireAuthCurrent`
helper that calls `engine.current()` and returns 401 when the
session is anonymous, mirroring how the rest of the auth API
treats unauthenticated requests.
OAuth / MFA / WebAuthn endpoints (which the audit also flagged in
the same finding) stay deferred — those are bigger surface
additions that need server-side flow work, not just routing. The
client cooperates: those methods are not yet declared on
`ActiveAuth`. Devices / device revoke are the only pair the client
already exposed and the handler ignored.
Test: `src/svrs/auth/test/handlers-devices.test.ts` exercises
routing + auth gating with stub engines (4 cases). Engine-level
device semantics (revocation invalidates bound sessions etc.)
remain covered by `engine-password.test.ts`.
Suite: 1496 / 1496.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
2a6706370e |
Bloque B1 — perm SQL compiler resolves nested paths via getPath
Audit P1: the SQL compiler used a literal property lookup (`(input.actor as Record<string, unknown>)[expr.path]`) for actor and context references, while the in-memory runtime evaluator goes through `getPath()` which respects the `.` separator. A policy condition like `actor.risk.mfa === true` therefore resolved correctly in memory but produced `undefined` in the SQL parameter — silently misaligning DB-side filters with allow/deny decisions. Fix: route both `PERM_ROOT_ACTOR` and `PERM_ROOT_CONTEXT` through `getPath()` in `src/libs/perm/compilers/sql.ts` so both code paths agree on segmentation. Resource references stay on `columnName` (they map to a real DB column, not to a JS object). Adds `src/libs/perm/test/sql-nested-paths.test.ts` with three regression cases: 1. Nested actor path (`actor.risk.mfa`) emits the resolved value. 2. Nested context path (`context.request.region`) likewise. 3. Missing nested path emits `undefined`, matching the runtime evaluator (so the SQL/runtime alignment doesn't accidentally diverge in the "missing" case either). Caveat documented in the new comment: the fix assumes DB column names don't contain `.`. Apps that need columns with dotted identifiers must override `columnName` and the actor/context paths must avoid `.` for those references. Suite: 1492 / 1492. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
5efec94367 |
Bloque D — uniform lifecycle for declarable services
Audit P2: every service the app declares in `createActiveApp({
services })` should respect the same dispose contract: idempotent,
post-dispose mutators are inert, no late side effects on torn-down
subscriptions.
storage:
- New `STORAGE_ERR_DISPOSED` + `StorageDisposedError` (with
`isStorageDisposedError` guard).
- `entry()`, `clear()` and `entries()` throw `StorageDisposedError`
after `dispose()` instead of silently mutating refcounted
registries with the bus already torn down.
- Re-exports added to the index barrel.
frontend:
- `ActiveFrontend.disposed` getter on the public type.
- Every mutating setter (`setLocale`, `setDir`, `clearDir`,
`setTheme`, `setMode`, `clearMode`, `setReducedMotion`,
`clearReducedMotion`, `setReducedSound`, `setDensity`) now
short-circuits when disposed, so a late media-query event or a
locale-source emit during teardown can't rewrite the DOM through
a torn-down `applyDom()`. Read-only getters keep returning the
last applied value.
- `onPreferenceChange` returns a no-op detacher post-dispose.
- `dispose()` is idempotent (was already, now also guarded against
resurrected mutations).
format:
- `ActiveFormat.disposed` getter on the public type.
- `dispose()` is now idempotent at the root and walks each
sub-engine in stable order.
- `setLocale()` is a no-op post-dispose.
Tests: +3 regression tests (one per art) covering the new dispose
semantics.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
f1055842dd |
Bloque A — sweep stale aliases and `App.<Capitalized>` references
Audit P1: documentation must stop teaching APIs the runtime no longer exposes. The svelte.config.js aliases are full words now (`$cache`, `$session`, `$connection`, `$timer`, `$logger`, `$format`, `$storage`, `$active-app`, `$bus`); the legacy 4-letter forms (`$cach`, `$sess`, `$conn`, `$timr`, `$logr`, `$fmts`, `$stor`, `$aapp`, `$buss`) were retired earlier but still lived in READMEs, demo pages, comments and a few code docstrings. Likewise, the `App.<service>` surface is lowercase for declarable services. The capitalized form is reserved for the four-piece core (`Logger`, `Bus`, `Timers`, `Orca`). References like `App.Cache`, `App.Sess`, `App.Storage`, `App.Format`, `App.Frontend`, `App.Lang`, `App.Auth`, `App.Perms`, `App.Http`, `App.Dom`, `App.Sium` were either ported to the new lowercase or migrated where it made sense. Mechanical sweep across `src/`, then a guard script: - `scripts/check-aliases.mjs` walks `src/`, fails the run if any forbidden alias or `App.<forbidden capitalized>` appears in any `.ts` / `.svelte` / `.md` / `.txt` / `.js` / `.mjs` file. `arts/active-app/types.ts` is allowlisted because its block comment explicitly documents the legacy uppercase surface as "removed". - `npm run test:aliases` exposes the script. - `npm run test:all` now includes the alias check. No runtime change; tests still 1486 / 1486. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
e6b6074c94 |
Address Codex audit P2.11 — split orca pure helpers out of `engine-orca.ts`
The engine file shrank from ~1900 LOC to ~1430 by lifting the state-free helpers into three focused modules: - `ids.ts` — `createDefaultIdFactory(timers)`. Already conceptually factored after P1.4; this commit moves the implementation out so the engine no longer references `TimerScheduler` from a one-off factory. - `validation.ts` — the entire `Orca.validate()` analysis: `validateConfiguration` orchestrator, `sortActionsCanonical`, `validateGatesForEvent`, `validateCyclesForEvent`, `validateTransactionsForEvent`, `canonicalCycleFingerprint`, and the public `RegisteredActionEntry` shape they share. Pure functions over the registry map; safe to unit-test in isolation. - `runner-helpers.ts` — `buildWaves`, `evaluateGates`, `interruptedActionRun`, `mapResultToActionStatus`, `computeRunStatus`. `computeRunStatus` now takes plain primitives (`traceAborted`, `traceAbortedReason`) instead of the engine's internal `TraceState` map, so the helper module has zero knowledge of engine state. `engine-orca.ts` keeps only the genuinely stateful orchestration core: registry, queue, drain / spawn / executeRun, runAction / runActionWithTimeout / runCompensation, ALS bus interception, and the public API surface. Constants and types that became private to the extracted modules left the engine's import block too — the file is now scannable in a single pass without having to swap mental contexts between "validation rules" and "run loop". No behavioural change. Suite: 1486 / 1486. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
c1d1ae9534 |
Address Codex audit P3.15 — ecosystem orca integration test
New `src/arts/active-app/test/ecosystem-orca.test.ts` validates the
canonical motivating scenario for `arts/orca`:
- user A → user B switch fires cache.clear, perm.invalidate and
connections.reauthenticateAll in a single orca trace, with one
runId and the union of every preset's `provides` tokens.
- session revoke fires cache.clear-on-revoke and
connections.closeAll('session-revoked'); identity-change actions
do not run on revoke.
- when one art's reaction throws, the others still run because
every preset declares `onError: continue`; the failing action is
recorded in the run trace with status `error`, run status
`partial`.
- the detacher returned by `applyStandardOrca` unregisters every
preset — subsequent events are inert.
Closes the audit's "missing compound test of the user A → user B
scenario" finding and replaces the README's pending-note with a
pointer to the live test file.
Suite: 1486 / 1486 (+4 from this commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
2473ade4ad |
Address Codex audit P1.6 — connection presets + dead `autoReauthOn` removal
Two new orca presets in `arts/active-app/presets/`:
- `applyConnectionsReauthOnIdentityChange` — listens to
`SESSION_EVENT_IDENTITY_CHANGED` and calls
`App.connections.reauthenticateAll()`. Closes the canonical motivating
scenario for orca: "chat connected with the previous user's
credentials" can no longer happen with this preset wired.
- `applyConnectionsCloseOnRevoke` — listens to `SESSION_EVENT_REVOKED`
and calls `App.connections.closeAll('session-revoked')`, leaving no
socket alive carrying revoked credentials.
`applyStandardOrca` now picks both up automatically when `App.connections`
is declared, and the index barrel re-exports the new shapes.
Removes the dead `autoReauthOn` config — declared on
`EngineConnectionsOptions` but never read by any runtime code:
- field removed from `connection/types.ts`
- `CONNECTION_AUTO_REAUTH_*` constants removed from `connection/consts.ts`
- `ConnectionAutoReauthOn` / `ConnectionAutoReauthTarget` types removed
- unused test import removed from `connection.test.ts`
- connection README rewritten: orca preset is now the canonical bridge,
per-connection `session: { ... }` documented as the manual / standalone
alternative
- demo route artifact-docs.ts and aapp page updated to use
`applyStandardOrca(App)` instead of `autoReauthOn: 'standard'`
The per-connection `session-wiring.ts` mechanism stays as-is — it's
useful for connections that live outside an App composition or that
need a custom `ConnectionSessionSource`. README now spells out the
two paths: orca preset for App-composed apps, per-connection `session`
for manual control.
Suite: 1482 / 1482 (+4 from this commit: 3 preset behaviour tests
+ 1 `applyStandardOrca` connection wiring test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
c0ed619372 |
Address Codex audit P2.7 / P2.8 / P2.9 / P2.10
P2.7 — `ctx.throwIfAborted()` helper. The action context now exposes `throwIfAborted()`. It throws an `AbortError`-style exception when `signal.aborted` is true, idiomatic for breaking out between awaits and avoiding side effects after a timeout / run-abort / dispose. Compensation contexts get the same helper. Documents cooperative cancellation as a hard contract: actions that touch external state must check `signal.aborted` (or call this helper) before mutating, especially after long awaits. Three regression tests (no-op when live, throws inside FINALLY when upstream aborted, prevents post-timeout side effects). P2.8 — global serial lane decision documented. Reformulates `canStartRun` with a doc block making the design explicit: non-parallel events share a single global lane (at most one fifo / replace-queued / drop-latest run in flight at any time); parallel-policy events bypass the lane. README's queue-policies section now leads with the lane invariant and the v2 roadmap lists "per-event concurrency lane for non-parallel policies" as a deferred upgrade. P2.9 — `commit()` documented as production seal, not a mandatory step. Adds a "When to commit" paragraph to the JSDoc: apps with an eager bootstrap should commit; apps that register actions from lazy-loaded routes must not. Removes the implicit assumption that every app should call commit during init. P2.10 — bus interception documented as bonus diagnostic, not a contract. README's bus-interception bullet now warns that the ALS attribution is an opt-in-by-environment feature: it improves the trace where AsyncLocalStorage exists (Node/Bun, modern browsers with AsyncContext) and silently degrades to root-event semantics elsewhere. Hard rule: actions must use `ctx.emit()` for attribution-critical fan-out; reserve `bus.publish` for genuinely root events. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
7e2907a75b |
Address Codex audit P1.1 / P1.2 / P1.3 / P1.4 + leftover cleanup
P1.1 — trace cleanup safe under parallel runs. `TraceState` gains `inFlightRuns`. `spawnRun` increments it synchronously before the IIFE awaits `executeRun`, decrements it in its `finally` and only then attempts `maybeReleaseTrace`. The release helper waits for both the queue snapshot and `inFlightRuns` to be empty before dropping the entry. Previously a parallel sibling finishing first could erase counters another run still relied on (reentry guards, dedupeKeys, abort flags). P1.2 — `provides` becomes the actual contract. When an action declares a non-empty `provides`, `runAction` checks each emitted token against it and emits `orca.configuration.invalid` for every undeclared token. Soft enforcement: the token is *not* dropped, keeping runtime back-compat; the diagnostic flags drift between the declaration and the runtime so authors notice. A v2 strict-drop mode can opt in later. P1.3 — `replace` queue policy renamed. The constant is now `ORCA_QUEUE_REPLACE_QUEUED` (literal `'replace-queued'`). The old name implied `takeLatest`-style "abort in-flight + queue new", which the engine never did. The hard variant lives in Roadmap v2 as `'replace-current'`. Tests updated; v1 has no external consumers yet so no back-compat alias. P1.4 — `idFactory` becomes injectable. New `OrcaIdFactory` type + `EngineOrcaOptions.idFactory`. Default factory uses the injected `timers.clock.now()` (no more direct `Date.now()` violating the "all time via timr" rule); replay/snapshot tests pass a deterministic counter. Eliminated `generateRunId` / `generateEventId` / `generateTraceId` standalone helpers. Cleanup leftovers from the audit: - `engine-orca.ts` header rewritten — was still claiming `after`/`unless`/`abortOn`/`actionTimeoutMs`/`compensate` are "accepted, ignored". Now describes the real surface. - `README.md` "Estado Del Documento" already updated; this commit also drops the legacy `## Roadmap` block, removes the `setupOrca` recommendation (moved to roadmap), rewrites "Tokens Flag" to cover the with-payload form, refreshes the Diagnostics list to match `consts.ts`, and replaces the "Tests Requeridos" wishlist with a snapshot of actual coverage + the pending ecosystem test. Tests: +5 (149 in engine-orca.test.ts, 16 in active-orca, 0 in result.test.ts → 165 in orca; 1475 / 1475 across the repo). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
38462f3282 |
Cleanup: remove stale orca v0/v0.1+ markers across docs and types
After v1 closed, the public surface still carried `@v0.0 Accepted, ignored`, `@v0.1+ never produced`, `@v1+ never invoked` notes that no longer match the engine. They lied to readers about what the runtime does. Code: - `types.ts` — rewrite docstrings for `OrcaTimeout`, `OrcaFatal`, `ctx.tokens`, `after`, `unless`, `abortOn`, `provides`, `actionTimeoutMs`, `onError`, `compensate` to describe current behaviour. - `consts.ts` — `ORCA_RESULT_TIMEOUT` / `_FATAL` and the error policy block lose their "never produced" / "v0.0" hedges. - `result.ts` — `orcaTimeout` / `orcaFatal` get real docstrings instead of `@v0.1+` markers. Docs: - `orca/README.md` "Estado Del Documento" — summarise v1 surface (engine + active wrapper, queue policies, transactions, fan-in, bus interception, tokens with payload). Drop the legacy `## Roadmap` section that listed v0/v0.1/v1 line items already delivered or already covered by the lower "Roadmap v1" / "v2" sections. - `active-app/README.md` — drop the dangling "orca v0.0" link. - `docs/orca_minds.txt` — prepend an ARCHIVED banner. - `docs/active-app-refactorizacion.md` — replace the "Working document, abierto a evaluación" header with an ARCHIVED notice (refactor completed 2026-05-04). No behavioural change. Tests still 1471/1471. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
6217c86b0d |
createActiveOrca — Svelte 5 reactive wrapper, closes orca v1
`createActiveOrca(options)` returns the engine's full interface plus five `$state`-backed snapshot properties: `runningSnapshot`, `recentRunsSnapshot`, `latestRun`, `committedSnapshot`, `disposedSnapshot`. The cells are refreshed inside an `engine.onChange` listener — no `$effect` chains, so no risk of `effect_update_depth_exceeded`. To wire that, `EngineOrca` gains `onChange(listener): () => void` and notifies on register / detach / run-start / run-complete / commit / dispose. Run controllers now live on `spawnRun` (created synchronously and tracked in `inFlightControllers`) instead of inside `executeRun`, so the `running` getter flips before the first await — reactive consumers see the start tick. Also stabilises an existing parallel-waves test that flaked when the suite ran under heavier concurrent load by raising the await margin. With this change, every line of the v1 roadmap is honoured by the engine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
23351e99c2 |
Bus interception — attribute `bus.publish` from inside an action
When a module calls `bus.publish('e', payload)` from the body of a
running orca action, the engine now treats the resulting bus event
as a child of that action: same `traceId`, `parentEventId` pointing
at the active run's envelope, depth+1, `emittedByAction` set to the
action's id. Previously the event entered as a fresh root and broke
causal traceability.
Implementation: a new `als.ts` module loads `AsyncLocalStorage`
cross-env — sync detect on `globalThis` first, fallback to dynamic
`node:async_hooks` import for Node/Bun. The first
`runActionInWave` awaits the loader and caches the resolved value
synchronously; the bus listener reads `getStore()` sync. In
browsers without AsyncContext the cached value stays `null` and
the semantics fall back to root-event (authors there should use
`ctx.emit()` for attribution).
Reentry guards count intercepted events identically to
`ctx.emit()`-derived ones, so depth/event/dedupe limits still
apply. Parallel siblings receive independent ALS contexts via
the standard ALS isolation, so attribution doesn't cross between
in-flight peers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
ef73979899 |
README: document v2 roadmap, move bus-interception to v2
Splits pending work into v1 (only `createActiveOrca()` left), v2 deferred-from-v1 items (bus interception via AsyncLocalStorage, `replace` with abort-in-flight, cross-event and nested transactions), the original v0/v1 roadmap not yet attacked (retry policies, concurrency limits, typed token payloads, graph visualisation, inspector, app presets, integration tests with `sess` / `perm` / `cach` / etc.), and the further-out `active-server` direction. Documenting the deferred set keeps audit context honest about what v1 leaves unfinished without renegotiating each item. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
c9ff825d13 |
Per-event queue policies — fifo / replace / drop-latest / parallel
`orca.configureEvent(event, { queuePolicy })` selects how concurrent
or queued runs of an event are handled:
- `fifo` (default): runs serialise globally with all other
non-parallel runs, preserving the v0 single-queue invariant.
- `replace`: a new event of the same name displaces any queued
envelope (in-flight is not aborted) and emits
`orca.queue.dropped` with `reason: 'replaced'`.
- `drop-latest`: an incoming event is dropped when one of the same
name is already in flight or queued (`reason: 'drop-latest'`).
- `parallel`: runs of this event launch concurrently via spawned
IIFEs and bypass the global non-parallel lock — they can race
with each other and with non-parallel events.
Engine refactor: drainQueue is now sync, walks the queue and
delegates to `spawnRun` (fire-and-forget IIFE). Each run tracks
its `AbortController` in `inFlightControllers` so `dispose()`
aborts them all in one pass. `running` getter reads
`inFlightControllers.size > 0`.
`configureEvent` is idempotent with the same policy, throws on a
conflicting reconfiguration, and throws `OrcaFrozenError` after
`commit()`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
6eb97abe2b |
Honor `fanIn` — quorum gate over multiple tokens
A `fanIn: { tokens, min }` declaration runs the action only when at
least `min` of the listed tokens are present in the wave snapshot.
Default `min = tokens.length` (AND); `min: 1` yields
"first-to-finish wins"; intermediate values give k-of-n quorum
patterns useful for voting / multi-source aggregation.
Evaluation order: `unless` → `abortOn` → `fanIn` → `after`.
`fanIn` and `after` may co-exist; both must pass. Skipped runs
carry `reason: fan-in-not-met:<count>/<min>:<tokens-present>`.
`validate()` reports `unsatisfiable-fan-in` (error) when fewer than
`min` upstream actions on the same event provide any of the listed
tokens — the gate could never fire at runtime. Stabilises an
existing parallel-wave timing test that flaked on slow CI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
7341b9c62a |
Tokens with payload — `emits` accepts `{ token, payload }`
Token emissions can now carry arbitrary payload data. The `emits`
array accepts either a bare token name (existing form) or
`{ token, payload }`; both forms mix freely. Downstream actions
read payloads via `ctx.tokenPayloads.get(name)`, with
`ctx.tokens.has()` still answering name-presence. The two views
can diverge: a string-form emission has presence but no payload.
Gates (`after` / `unless` / `abortOn` / `provides`) keep comparing
names only — payload semantics are opt-in metadata. Wave snapshots
extend to payloads, so parallel siblings never read each other's
payloads mid-flight. Last-write-wins on duplicate names.
Surface: `OrcaTokenWithPayload`, `OrcaTokenEmit`,
`OrcaActionContext.tokenPayloads`, `OrcaActionRun.emittedPayloads?`,
`OrcaRunResult.tokenPayloads`. Compensation contexts also expose
`tokenPayloads` so rollback paths can read the run-level state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
6f9b558238 |
Honor `transaction` — atomic groups with immediate LIFO rollback
Actions on the same event sharing a `transaction` tag form an atomic group. When any member completes with `ERROR` or `FATAL`, the engine immediately compensates that group's already-succeeded members in LIFO order of completion, marks the run aborted, and emits `RUN_ABORTED`. Transaction semantics override the failing member's own `onError` — `abort-run` is implicit. Compensators run at most once per action: tx-driven rollback marks its entries as compensated, and the standard pre-`FINALLY` rollback skips them. Non-tx compensable actions still compensate at the global phase. `OrcaActionRun.transactionId` mirrors the tag for trace navigation. `validate()` warns `transaction-without-compensate` when no member of a transaction declares a compensator (rollback would be a no-op). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
a518dcac6b |
Honor `parallel: true` — wave-based execution within a stage
Consecutive `parallel: true` actions in the same stage form a wave that runs concurrently via `Promise.all`. Sequential actions break the wave (each is a wave of one). Tokens emitted inside a wave merge into the run-level set only **after** the wave settles, so parallel siblings never see each other's tokens — gate evaluation runs against a wave-start snapshot. Errors and `OrcaFatal` decide aborts after the surrounding wave settles; in-flight siblings are not cancelled. Compensable parallel successes enter the LIFO stack in registration order. `validate()` retains `provides` until wave end and now flags `unsatisfiable-after` when two parallel siblings cross-depend. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
f62020e687 |
Add Orca.commit() — freeze the action graph
`commit()` flips a one-way `committed` flag; subsequent `onEvent()` calls throw `OrcaFrozenError` (code `ORCA_ERR_FROZEN`). It is idempotent, does not run `validate()` implicitly, and does not disturb already-registered actions, in-flight runs, or detach functions returned before the freeze. `dispose()` keeps precedence over the frozen guard. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
c996c90dc8 |
Honor compensate: invoke compensators LIFO when a run aborts
Fourth batch of v1 features. With this, OrcaAction.compensate moves
from accepted-and-ignored to a real Saga-style rollback hook: when a
run aborts (OrcaFatal, ORCA_ON_ERROR_ABORT_RUN, or trace-aborted), the
engine walks back through every action that previously completed in
success and invokes its compensator before the FINALLY stage runs.
Engine semantics:
- During the action loop, when an action returns success and has
declared `compensate`, push { action, payload } onto a LIFO stack
(compensable[]). Stage FINALLY actions are not compensable —
FINALLY is the cleanup pass itself.
- On entry to the FINALLY stage with aborted=true and
compensable.length > 0, walk the stack in reverse. Each
compensator gets a fresh AbortController (the run controller is
already aborted) and an OrcaActionContext whose emit() returns
null — compensations do not fan out new events, they roll back.
- Compensations are best-effort: a thrown compensator is recorded as
ORCA_ACTION_STATUS_ERROR but the next compensation in the chain
still runs. v1 chooses best-effort over fail-fast because rolling
back N-1 entries when one of them failed is more useful than
rolling back zero.
- FINALLY actions run AFTER compensations, in normal stage order.
Conceptual order on abort:
action loop → compensation phase → FINALLY → run trace recorded.
- Compensations appear in OrcaRunResult.compensations[], a separate
field from actions[]. The original action's record stays in
actions[] with its original success status; the compensator's
record lives only in compensations[]. This keeps the run trace
accurate (the action did succeed; it was just compensated later).
- Diagnostics: orca.compensation.started (DEBUG),
orca.compensation.completed (DEBUG), orca.compensation.failed
(ERROR). All carry runId, actionId, eventId, traceId, depth.
- Compensation does NOT trigger for PARTIAL runs (CONTINUE-onError
actions that errored without aborting) or TIMEOUT-only runs that
didn't abort. The semantic is "all-or-nothing rollback for
aborted runs", not "partial cleanup".
OrcaRunResult gains a `compensations: readonly OrcaActionRun[]` field
(empty array when no compensation ran).
Tests (+10 in a new "v1 — compensate" describe block):
- does not invoke compensate when the run completes successfully
- invokes compensate of a previously successful action when the run
aborts (the simplest happy path)
- does not invoke compensate of an action that errored itself
- runs compensations in LIFO order (with three compensators)
- runs compensations even when OrcaFatal aborts the run
- continues with remaining compensations even if one throws
(best-effort, the failing compensator's status is ERROR but
earlier and later ones still ran)
- runs FINALLY actions after compensations (order: ['compensate',
'finally'])
- does not invoke compensate when a CONTINUE-onError action errors
(PARTIAL run, no abort)
- emits orca.compensation.{started,completed,failed} diagnostics
- passes the original event payload to compensate
- emits inside ctx during compensation are dropped (return null)
The legacy "accepts compensate without invoking it" test from the v0
forward-compat block was deleted; v0 promise is now v1 reality. The
case it asserted (compensate of a failing action is not invoked) is
now covered explicitly by the new "does not invoke compensate of an
action that errored itself" test.
Verification: 1402/1402 vitest tests pass (92 in orca, +10 from this
commit on top of 82 from previous v1 commits).
README updated: compensate moved from "Roadmap v1" to "Ya en el motor
(de v1)". Remaining v1 items: commit() configuration freeze, queue
policies (commit/replace), transaction groups, parallel,
payload-bearing tokens, fan-in, bus interception (Option B), and
createActiveOrca() reactive wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
7aebe7c673 |
Add Orca.validate() — static graph analysis of registered actions
Third batch of v1 features. With the gates honored, configuration
mistakes (orphan tokens, dependency cycles, ordering bugs) became
silently fatal: the action skips/blocks at runtime with a misleading
"reason" like "after-not-met:foo" without telling the developer that
"foo" is never produced by anything. validate() catches those before
the first event arrives.
API: EngineOrca.validate(): OrcaValidateResult
- ok: false iff any issue has severity 'error'
- issues: array of OrcaValidateIssue { kind, severity, event,
actionId?, token?, cycle?, message }
- Never throws. Read-only over the registered set; safe to call
multiple times during config; engine does not gate runs on result.
Issue kinds:
- unsatisfiable-after (ERROR) — an action's `after: [T]` is not
produced by any earlier action in canonical order. Action would
always skip at runtime.
- orphan-unless / orphan-abort-on (WARN) — gate token has no
upstream producer. May be deliberate (forward-compat / typo
guard); demoted to warning so `ok` stays true.
- dependency-cycle (ERROR) — actions block on each other through
`after` / `provides`. DFS over an action-level adjacency map
(A → set of action ids it depends on); cycles deduped via a
canonical fingerprint (rotated to lexicographically smallest id
first).
Canonical order matters: actions are sorted by (stage, registeredAt)
so the validator's "did any earlier action provide T" matches what
the engine does at runtime. Concrete consequences:
- A producer registered AFTER the consumer in the same stage is
NOT considered upstream — runtime would skip the consumer, and
validate() reports it.
- A producer in a LATER stage (e.g. POST when consumer is MAIN)
is NOT considered upstream either.
Tests (+11):
- empty engine returns { ok: true, issues: [] }
- happy path: provider in earlier stage satisfies consumer
- reports unsatisfiable-after for missing token
- reports unsatisfiable-after when producer registered AFTER consumer
in the same stage (subtle ordering bug)
- reports unsatisfiable-after when producer is in a later stage
- reports orphan-unless as warning (ok stays true)
- reports orphan-abort-on as warning
- detects direct 2-action cycle
- detects indirect 3-action cycle (cycle.length === 4 with closure)
- warnings do not flip ok to false
- issues are isolated per event (cross-event tokens don't satisfy
each other)
Constants exported from $orca:
ORCA_VALIDATE_UNSATISFIABLE_AFTER
ORCA_VALIDATE_ORPHAN_UNLESS
ORCA_VALIDATE_ORPHAN_ABORT_ON
ORCA_VALIDATE_DEPENDENCY_CYCLE
ORCA_VALIDATE_SEVERITY_ERROR
ORCA_VALIDATE_SEVERITY_WARN
Types exported: OrcaValidateIssue, OrcaValidateIssueKind,
OrcaValidateResult, OrcaValidateSeverity.
README updated: validate() moved from "Roadmap v1" into "Ya en el motor
(de v1)". Remaining v1 items: compensate, commit() configuration freeze,
queue policies (commit/replace), transaction groups, parallel,
payload-bearing tokens, fan-in, bus interception (Option B), and
createActiveOrca() reactive wrapper.
Verification: 1392/1392 tests pass (82 in orca, +11 from this commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
79d6d1f776 |
Honor after/unless/abortOn gates in the orca engine
Second batch of v1 features. With this, the `provides`/`emits` token
infrastructure that has lived in OrcaAction since v0 finally has
control-flow consequences: tokens emitted by one action gate the
acceptance of subsequent actions in the same run.
Gate semantics:
- `unless: T[]` — declared on actions that should not run again if
a sibling action already produced their precondition (idempotency
guard). When any token in `unless` is present, the action is
SKIPPED with reason `unless-triggered:<token>`.
- `abortOn: T[]` — declared on actions that must halt when an upstream
flagged danger. When any token in `abortOn` is present, the action
is BLOCKED (distinct from skipped) with reason
`abort-on-triggered:<token>`.
- `after: T[]` — declared on actions whose work depends on tokens
emitted by upstream actions. When any required token is missing,
the action is SKIPPED with reason
`after-not-met:<missing1>,<missing2>,…`.
Evaluation order is deliberate: unless first (idempotency), abortOn
second (halt signal), after third (weakest reason to skip). The first
gate that fires short-circuits the action; later gates are not
evaluated. The FINALLY stage bypasses all gates so cleanup work runs
unconditionally.
Engine changes:
- New evaluateGates(action, tokens, now) helper produces a
synthesized OrcaActionRun when a gate fires, or null when the
action should proceed.
- executeRun calls evaluateGates() right after the trace-aborted
short-circuit and before the per-action AbortController is set up.
Gated actions emit either ACTION_SKIPPED or ACTION_BLOCKED
diagnostics and never reach runAction().
- The "run aborted between stages" path now also emits an
ACTION_BLOCKED diagnostic with reason 'run-aborted', so blocked
actions are observable in logs regardless of cause.
- OrcaActionRun.interruptedReason renamed to OrcaActionRun.reason —
the field carries gate, reentry, or authored reasons uniformly
across SKIPPED, BLOCKED, INTERRUPTED. The status determines what
kind of reason it is.
New constants exported from $orca:
- ORCA_GATE_REASON_AFTER_NOT_MET
- ORCA_GATE_REASON_UNLESS_TRIGGERED
- ORCA_GATE_REASON_ABORT_ON_TRIGGERED
- ORCA_GATE_REASON_RUN_ABORTED
- ORCA_DIAGNOSTIC_EVENTS.ACTION_BLOCKED
Tests (+11):
v1 — after gate (3):
- skips an action whose `after` token is missing
- runs the action when every `after` token has been emitted
- reports every missing token in the reason when partially met
v1 — unless gate (2):
- skips an action when any `unless` token is present
- runs the action when no `unless` token is present
v1 — abortOn gate (3):
- blocks an action when an abortOn token is present
- runs the action when no abortOn token is present
- emits orca.action.blocked diagnostic with the abortOn reason
v1 — gate precedence and FINALLY bypass (3):
- unless wins over after when both would short-circuit
- abortOn wins over after when both would short-circuit
- FINALLY stage bypasses gates so cleanup always runs
The legacy "accepts after/unless/abortOn without enforcing" forward-
compat tests from the v0 ignored-fields block are removed; v0 promise
is now v1 reality. The orcaInterrupted-reason test was updated to read
the renamed `reason` field.
Verification: 1381/1381 tests pass (71 in orca, +11 from this commit
on top of 63 from the previous v1 commits).
README updated: gates moved from "Roadmap v1" to "Ya en el motor (de
v1)". Remaining v1 items: compensate, commit/replace queue policies,
transaction groups, parallel, payload-bearing tokens, fan-in, bus
interception (Option B), validate()/commit() static graph validation,
and createActiveOrca() reactive wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
793767fe0d |
Honor actionTimeoutMs and OrcaFatal in the orca engine
First batch of v1 features. These were accepted in the public surface
since v0 but the engine ignored them — the documents called out
ORCA_RESULT_TIMEOUT as never produced, OrcaFatal as treated like
error. Now both are real.
actionTimeoutMs:
- In runAction, if action.actionTimeoutMs > 0, race the action's
promise against a timer scheduled on the injected TimerScheduler.
When the timer wins, it aborts the action's signal and resolves
with orcaTimeout(timeoutMs). The action's promise keeps running
in the background; the run trace records the timeout regardless.
- Each action gets its own AbortController, chained to the run-level
controller via an addEventListener('abort') hop. A timeout aborts
just that action; sibling actions in the same stage proceed.
- Diagnostic orca.action.timeout fires with timeoutMs and the usual
envelope identity.
- Run status: any TIMEOUT action puts the run in ORCA_RUN_TIMEOUT.
OrcaFatal:
- orcaFatal(error) result now maps to ORCA_ACTION_STATUS_FATAL (was
silently mapped to ERROR).
- In executeRun, FATAL status aborts the run unconditionally — it
overrides the action's onError policy. CONTINUE-flagged actions
that return fatal still abort.
- The FINALLY stage continues to run after a fatal abort.
- Diagnostic orca.action.fatal fires at LogLevel.FATAL with a
fatal: true marker and the error payload.
- Run status: any FATAL action puts the run in ORCA_RUN_FATAL.
Run-status precedence is now explicit:
FATAL > TIMEOUT > ABORTED > INTERRUPTED > PARTIAL > SUCCESS
Tests (+9):
v1 — actionTimeoutMs (5):
- produces ORCA_RESULT_TIMEOUT when action exceeds its timeout
- runs to completion when action finishes before timeout
- aborts the action signal when the timeout fires (cooperative
cancellation observable inside the action)
- emits orca.action.timeout diagnostic with timeoutMs
- lets later actions in the same stage proceed after a timeout
v1 — OrcaFatal (5):
- maps OrcaFatal to ORCA_ACTION_STATUS_FATAL
- aborts the run regardless of onError policy when fatal fires
- still runs FINALLY stage after a fatal abort
- emits orca.action.fatal diagnostic with fatal: true
- precedence: fatal beats every other status
The legacy "accepts actionTimeoutMs without enforcing timeout" test
from the v0 forward-compat block was removed; v0 promise is now v1
reality. Also dropped the equivalent OrcaFatal-as-error compatibility
behavior — fatal is now a distinct status across the engine.
Tests use the real createEngineTimers() in the timeout block so the
fake timer's not-implemented schedule() doesn't interfere; the existing
fake timer continues to serve every other test that doesn't rely on
actual scheduling.
Verification: 1373/1373 tests pass (63 in orca, +9 from this commit on
top of the 54 from the v0-kernel + verification commits).
README updated: actionTimeoutMs and OrcaFatal moved out of "Roadmap v1"
into a "Ya en el motor (de v1)" section. Remaining v1 items: compensate,
after/unless/abortOn gates, commit/replace queue policies, transaction
groups, parallel, payload-bearing tokens, fan-in, bus interception
(Option B), validate()/commit() static graph validation, and
createActiveOrca() reactive wrapper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
0f52fdfce6 |
Close orca v0 verification gaps left by the previous commit
The previous commit added reentry guards and ctx.emit() but the tests
only covered the most visible behaviors. Honest audit surfaced six
untested paths:
- orcaInterrupted() helper round-trip into ORCA_RUN_INTERRUPTED
- maxEventsPerTrace guard (only maxDepth and repeatedEventLimit had
dedicated tests)
- the emit/blocked/aborted/interrupted diagnostic events with their
enriched meta (eventId/traceId/parentEventId/depth/emittedByAction)
- dispose() while a trace has live work
- the ORCA_RUN_INTERRUPTED status appearing on actual runs (the
previous "abort-trace" test only checked run count)
- run-scoped diagnostics carrying envelope identity consistently
Adds 7 tests across the existing v0 envelope/reentry block plus a new
diagnostics block:
envelope/reentry block (+3):
- action that returns orcaInterrupted maps to interrupted status
and run (validates orcaInterrupted helper end-to-end)
- blocks the (N+1)th event in a trace when N = maxEventsPerTrace
- the existing abort-trace test gained an assertion that the
child run completed before the abort took effect, plus a
follow-up bus publish to verify the trace is sealed.
diagnostics block (+5):
- emits orca.event.emitted carrying traceId, parentEventId, depth
and emittedByAction
- emits orca.reentry.blocked when a guard rejects an emit
- emits orca.trace.aborted when policy is abort-trace
- emits orca.action.interrupted when an action returns
orcaInterrupted
- every run-scoped diagnostic carries eventId/traceId/depth
consistently across run.started / action.started / run.completed
Test infrastructure: introduces createCapturingLogger() that intercepts
the Logger interface and pulls structured DiagnosticEntry rows from
the catalogued log routing in libs/logger/diagnostics.ts. The
input.context.diagnostic shape is documented enough to be a stable
public test interface without reaching into internals.
Total: 1364/1364 vitest tests pass (54 in orca, +7 from this commit on
top of the 10 from the kernel commit).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
feacd46c62 |
Promote orca v0 from callback runner to orchestration kernel
Implements the v0-kernel design captured in docs/orca_minds.txt: the
engine now carries every event through an internal envelope, exposes
trace identity to actions, and bounds derived-event chains through
configurable reentry guards. The previous engine was effectively a
callback runner with stages; this commit turns it into a runtime that
can answer "where does this event come from, how deep is it, and when
should I stop?" without polluting user-defined payloads with runtime
metadata.
New public surface:
- OrcaEnvelope<TPayload> + OrcaEventMeta (eventId, traceId,
parentEventId, parentRunId, emittedByAction, depth, stack,
publishedAt, dedupeKey)
- OrcaActionContext gains eventId / traceId / parentEventId / depth
plus emit(event, payload, options?) -> OrcaEventId | null
- OrcaResult adds OrcaInterrupted (with orcaInterrupted() helper)
- OrcaActionRun.status and OrcaRunResult.status add 'interrupted'
- OrcaRunResult exposes eventId / traceId / parentEventId / depth
- OrcaReentryOptions on EngineOrcaOptions: maxDepth (16),
maxEventsPerTrace (128), repeatedEventLimit (2),
repeatedEventPolicy (skip / abort-trace / error)
- Constants for reentry policies and reasons; OrcaReentryError class
Engine semantics:
- Bus publishes are roots: fresh traceId, depth=0, no parent. They
never enter the reentry counters.
- ctx.emit() builds a child envelope inheriting the parent's traceId
and incrementing depth. The child is enqueued, never executed
inline.
- Reentry guards apply only to derived envelopes. Crossing maxDepth,
maxEventsPerTrace, repeatedEventLimit, or matching a previous
dedupeKey triggers the configured policy. Skip blocks just that
envelope; abort-trace marks the trace and skips every queued event
that belongs to it; error throws OrcaReentryError synchronously.
- dispose() marks every live trace aborted with reason 'disposed' so
late ctx.emit() calls (e.g. from compensating cleanup) get a clean
rejection instead of an exception.
Diagnostics gain four new event types
(action.interrupted, event.emitted, reentry.blocked, trace.aborted)
and every existing one carries the envelope identifiers
(runId, eventId, traceId, parentEventId, depth) where applicable, so a
log sink can correlate runs without parsing variant tags.
Tests: 10 new tests covering envelope identity (root depth=0, child
depth+1), ctx.emit() trace inheritance, run-trace correlation, orphan
emit, all four reentry guards (maxDepth with disjoint event names so
the same-name limit doesn't interfere, repeatedEventLimit, error
policy throwing OrcaReentryError, abort-trace, dedupeKey), and the
invariant that bus publishes start fresh traces.
Active-app presets keep working unchanged (they don't call ctx.emit
yet); the API extension is additive on the OrcaActionContext side
(presets still type-check against the wider context shape).
Total: 1357/1357 vitest tests pass (47 in orca, +10 from this commit).
Roadmap v1 documented at the foot of the orca README and parked under
@v1+ in the source: actionTimeoutMs runtime, compensate invocation,
after/unless/abortOn gating, OrcaFatal distinction, queue policies
(commit/replace/parallel), transaction/atomic groups, payload-bearing
tokens, fan-in, validate()/commit() static graph validation,
createActiveOrca() reactive wrapper, and the bus.publish interception
(Option B) that would let modules' direct publishes attach
emittedByAction perfectly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
1515df1be3 |
Update docs site to reflect post-big-bang App architecture
The /active/* documentation site still taught the pre-2026-05 API
surface — App.createActiveSession(), autoInvalidateOn / autoReauthOn,
APP_EVENT_USER_IDENTITY_CHANGED, App.createSiumEngine(). Refreshed every
page so users see the current model:
- Lowercase services (App.lang, App.cache, App.session, App.perm, …)
instead of the deleted uppercase aliases.
- createActiveApp({ services: { x: defineActive*(...) } }) instead of
createActiveApp({ x: ... }) + App.createActive*().
- applyStandardOrca(App) (or cherry-picked apply* presets) instead of
consumer-side autoInvalidateOn / autoReauthOn flags.
- SESSION_EVENT_IDENTITY_CHANGED on App.Bus instead of the (gone)
APP_EVENT_USER_IDENTITY_CHANGED translator output.
- Single-instance services enforced by the schema's object-literal
semantics, not runtime AlreadyCreatedError throws.
Touched: artifact-docs.ts (the central data source), the long-form
docs/aapp + docs/perm + docs/lang pages, the four get-started pages,
the security page, the root /active page, plus three nav components.
Also fixes a pre-existing bug from an earlier rename: a few imports of
SvelteKit's $app/state, $app/paths, $app/environment had been
incorrectly rewritten to $active-app/* (which doesn't exist as a
SvelteKit alias), leaving the docs site responding 500 to every route.
Restored the correct $app/* imports in:
- active/_components/{Sidebar,PageNav,Toc}.svelte
- active/+page.svelte, test pages (conn, ecosystem, perm, stor, logr,
+page.svelte for /test and /)
- JSDoc example in arts/session/ssr.ts
active/docs/cach/+page.svelte now reads artifactDocs.cache to match
the renamed key in artifact-docs.ts.
Verification: 1347/1347 vitest tests pass, all 11 sampled docs routes
return 200 with zero console/page errors (sampled /active and the
get-started + docs pages for aapp, perm, cach, sess, sium, conn).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
48404bb037 |
Move setBus/getBus to $bus; the bus owns the propagation pattern
The Svelte-context bridge `setBus(bus)` / `getBus()` previously lived in
arts/active-app/bus-context.svelte.ts. That placement was wrong: the
helper does not depend on App, doesn't know about services, and serves
any consumer holding an EngineBus — including isolated test buses or
secondary buses for embedded sub-trees. Its semantic owner is the bus.
Moves:
arts/active-app/bus-context.svelte.ts -> arts/bus/svelte/context.svelte.ts
AappBusNoContextError -> BusNoContextError (libs/bus)
APP_ERR_BUS_NO_CONTEXT (code) -> BUS_ERR_NO_CONTEXT
APP_BUS_CONTEXT_KEY (string constant) -> Symbol inside the helper
`setBus` / `getBus` are re-exported from the `$bus` barrel; no consumer
needs to know the file path. Imports change from `$active-app` to `$bus`:
-import { setBus } from '$active-app';
+import { setBus } from '$bus';
The dropped surface area in arts/active-app:
- bus-context.svelte.ts (file)
- APP_BUS_CONTEXT_KEY (consts)
- APP_ERR_BUS, APP_ERR_BUS_NO_CONTEXT, APP_ERROR_MSG_BUS_NO_CONTEXT (errors)
- AappBusNoContextError class + isAappBusNoContextError guard
- setBus/getBus/AappBusNoContextError/isAappBusNoContextError barrel exports
Verification: 1347/1347 vitest tests still pass; the demo's +layout
imports from $bus and the page renders all 11 cards with sign-in working
and zero console/page errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
eb6001fae4 |
Reduce App core to Logger/Bus/Timers/Orca; everything else is opt-in services
Big-bang replacement of the active-app composition: legacy uppercase surface
(App.Lang, App.Cache, App.Format, App.Frontend, App.Dom, App.Storage, App.Http)
removed entirely. All non-core artifacts are now opt-in via services schema:
services: { cache: defineActiveCache(), lang: defineActiveLang(...), ... }
Schema services are exposed as lowercase properties (App.cache, App.lang, …)
with end-to-end type safety; accessing a service the schema didn't declare
is a compile error.
Three import paths split for honest tree-shaking:
$active-app createActiveApp + core types/errors/bus-context
$active-app/services defineActive* / defineEngine* factories
$active-app/presets applyCache* / applyPerm* / applyStandardOrca
Service factories declare core deps (logger/bus/timers/orca) and sibling
service deps (e.g. format wires localeSource from lang automatically when
both are declared). The builder validates names, computes topological order,
detects cycles, builds immediates eagerly, and exposes lazy proxies that
materialise on first access. factory.create() runs inside untrack so
subscriptions wired during construction (e.g. lang.onLocaleChange) cannot
crash the outer reactive scope when triggered from a $derived.
Reactions to lifecycle events (cache.clear on revoke / identity change,
perm.invalidate on identity change) move from internal bus subscriptions
inside arts to opt-in orca presets registered by the application:
applyStandardOrca(App) // or cherry-pick individual apply* functions
Also lands a working showcase at /demo wiring 10 of 12 services
(everything except auth/connections, which need a real server) plus a
mocked perm fetcher and a real http client against jsonplaceholder.
Misc cleanup along the way:
- libs/cache/{key,policy,scope}.ts: missing CACHE_VALIDATION_MESSAGES
imports (the throw paths were never covered by tests, so the bug
only surfaced via the demo)
- All arts READMEs scrubbed of autoInvalidateOn / APP_EVENT_USER_*
references; bus README rewritten around the orca-preset model
- refactorizacion.md moved out of src/ into docs/
Verification: 1347/1347 vitest tests passing, demo loads and exercises
all wired services in a real browser with zero console errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
c6de4a11f1 |
Mark refactorizacion.md as completed — big-bang shipped
Closes the working document. Replaces the 'Lo que falta' checklist with the actual list of removals applied in commits |
5 months ago |
|
|
64ab1f0b63 |
Big-bang: remove legacy translators, App.createActiveX(), APP_EVENT_*, autoInvalidate*
Completes step 3 of the active-app refactor. The orca-based service
schema model is now the single supported path; the legacy
auto-reactive ecosystem is gone.
Removed:
- arts/active-app/integrations/session-translator.ts and auth-cache.ts
(their purpose is replaced by orca presets in arts/active-app/presets/).
- arts/connection/bus-session-source.ts (canonical session events
flow through SESSION_EVENT_* directly).
- libs/active-app/ entirely. Its contents (consts, errors, events
reduced to APP_EVENT_DISPOSE_STARTING) consolidated into
arts/active-app/{consts,errors,events}.ts. The libs layer no longer
has anything app-specific.
- From arts/cache:
- bus / autoInvalidateOn options on ActiveCacheOptions.
- wireAutoInvalidation internal subscription.
- CACHE_AUTO_INVALIDATE_* constants and types.
- From arts/perm:
- bus / autoInvalidateOn options on ActivePermsOptions.
- wireAutoInvalidation internal subscription.
- PERM_AUTO_INVALIDATE_* constants and types.
- From arts/connection:
- bus option in EngineConnectionsOptions.
- shouldWireBusSessionSource helper.
- The "reacts to canonical app identity bus events" test that
depended on the deleted bus-session-source.
- From arts/active-app/active-app.svelte.ts:
- createSiumEngine() / createActiveSession() / createActiveConnections() /
createActivePerms() / createActiveAuth() factory methods.
- App.Sess / App.Perms / App.Auth getters and the singleton
guards (Sess !== undefined etc.).
- APP_ORCHESTRATION_* preset system, resolveActiveAppOrchestration,
STANDARD_ORCHESTRATION_TRANSLATORS, all five translator handles.
- APP_ERROR_ALREADY_CREATED_* / APP_ERROR_CREATE_PERM_ENDPOINT_REQUIRED
constants (orphan after factory removal).
- From arts/active-app/test:
- ecosystem.integration.test.ts (9 monolithic tests, ~1900 lines).
- session-translator.test.ts.
- create-sium-engine.test.ts.
- From libs/active-app/test:
- events.test.ts (covered the deleted publishers and the legacy
APP_USER_IDENTITY_* causes).
- APP_EVENT_USER_IDENTITY_CHANGED, APP_EVENT_TENANT_SWITCHED,
APP_EVENT_PERMISSIONS_REFRESH_REQUESTED,
APP_EVENT_CACHE_INVALIDATE_REQUESTED, APP_EVENT_CONNECTIVITY_CHANGED
(only DISPOSE_STARTING survives).
- Their associated payload interfaces and the
APP_USER_IDENTITY_CAUSE_* constants.
- publishAppUserIdentityChanged / publishAppPermsRefreshRequested /
publishAppCacheInvalidateRequested / publishAppTenantSwitched /
publishAppConnectivityChanged. publishAppDisposeStarting and the
new onAppDisposeStarting helper remain.
Final shape of arts/active-app/:
- active-app.svelte.ts: builds Logger, Lang, Format, Frontend, Dom,
Storage, Http, Timers, Bus, Orca, Cache, then the schema services
via buildServiceBuilders. dispose() publishes DISPOSE_STARTING and
tears everything down in reverse construction order.
- services.ts, service-builder.ts, service-factories/, presets/,
bus-context.svelte.ts, consts.ts, errors.ts, events.ts, types.ts,
index.ts (public barrel exposing every define*, applyStandardOrca,
types and errors).
Suite: 1374 tests pass. The reduction from the prior 1408 reflects the
deleted legacy tests; coverage of the new model is comprehensive
(schema-declarative.test.ts, service-builder.test.ts,
service-factories.test.ts, presets.test.ts, active-app.test.ts core,
plus the existing per-art suites).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
01a85ad299 |
Replace legacy ecosystem tests with focused composition tests
Removes the two large legacy integration suites:
- ecosystem.integration.test.ts (9 monolithic tests, ~1900 lines,
14 occurrences of autoInvalidateOn, exercising the
APP_ORCHESTRATION_STANDARD path that is being removed).
- session-translator.test.ts (validates wireSessionTranslator,
which is also being removed).
Replaces active-app.test.ts (1072 lines, 26 tests with heavy
dependence on App.createActiveX() and publishApp* publishers) with
~200 lines of focused composition tests:
- core surface (Logger, Lang, Format, Frontend, Dom, Storage,
Http, Timers, Bus, Orca, Cache, dispose).
- locale flow (setLocale, onLocaleChange).
- mono-lang behavior (path passthrough, warn-once, |fallback).
- dispose idempotency and Orca teardown.
The new model's coverage already lives in:
- schema-declarative.test.ts — declarative App.cache / App.session
end-to-end.
- service-builder.test.ts — topology, lazy proxies, dispose.
- service-factories.test.ts — each defineActiveX wired against a
real core.
- presets.test.ts — orca actions registered via applyStandardOrca
react to SESSION_EVENT_*.
Total suite: 1383 pass (down from 1408 — the deleted legacy tests
were exercising paths that disappear entirely in the big-bang).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
4299c3071d |
Update refactorizacion.md with implementation status and remaining big-bang plan
Captures the state after the long implementation session: steps 1+2 done, step 3 partially done (model active, legacy deprecated), 13 items left for the dedicated big-bang session. The 'Lo que falta' list points to concrete file:line locations and test counts so the next session has a precise checklist. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
60e130b656 |
Mark legacy active-app APIs as @deprecated with migration guidance
Adds @deprecated jsdoc to every API that the orca-based service-schema
model replaces. No runtime change; the legacy paths continue to work
end-to-end, but IDEs and consumers see the strikethrough and the
recommended replacement.
Marked deprecated:
- ActiveCacheOptions.bus / autoInvalidateOn — use the orca preset
applyCacheClearOnIdentityChange (or applyStandardOrca) instead.
- ActivePermsOptions.bus / autoInvalidateOn — use the orca preset
applyPermInvalidateOnIdentityChange.
- App.createSiumEngine / createActiveSession / createActivePerms /
createActiveAuth / createActiveConnections — declare the matching
service in `services: { … }` and access via the lowercase
property (App.session, App.perm, …).
- App.Sess / App.Perms / App.Auth — replaced by App.session /
App.perm / App.auth from the schema.
- ActiveAppOptions.connections / permissions / auth / orchestration
— same migration as above.
- publishAppUserIdentityChanged — subscribe to
SESSION_EVENT_IDENTITY_CHANGED directly (or use the orca preset).
- publishAppPermsRefreshRequested — call App.perm.refresh().
- publishAppCacheInvalidateRequested — call App.cache.clear().
- publishAppConnectivityChanged — slated for arts/connection to
own its CONNECTION_EVENT_*.
- publishAppTenantSwitched — no module owner today; apps emit
their own event.
The big-bang removal of these APIs requires migrating the 9-test
ecosystem suite, the 26 active-app tests and any consumer pages.
That stays scheduled for a session with dedicated time. Until then
this commit communicates the direction without breaking anything.
Tests: 1408 pass (no behavior change).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
7148a5d2ec |
Wire AppServiceSchema into createActiveApp() alongside legacy core
createActiveApp() now accepts an `options.services: TSchema` object
and exposes each declared service as a lowercase property on the
returned App. Schema services coexist with the legacy uppercase
core (App.Cache, App.Bus, App.createActiveSession() etc.) — neither
collides with the other and apps can adopt the schema gradually.
Type changes:
- ActiveApp<S, TSchema = AppServiceSchema> intersects
ActiveAppLegacy<S> (the uppercase core + factory methods),
ResolveServiceInstances<TSchema> (schema instances), and
ActiveAppServicesIntrospection ({ services: { … } status map }).
- ActiveAppOptions<S, TSchema> adds the optional `services?` slot.
Runtime changes:
- createActiveApp builds a CoreServices snapshot (logger, bus,
timers, orca) after Bus + Timers + Orca are constructed.
- When `services` is provided, buildServiceBuilders() is invoked
and every key becomes a getter on the App; reading triggers
lazy construction. Immediate services build during
createActiveApp().
- dispose() runs schema services first (in reverse construction
order) and then the legacy core in its existing order.
- When `services` is absent, App.services returns an empty frozen
object so introspection still works.
Tests:
- active-app.test.ts: API surface includes the new `services` key.
- schema-declarative.test.ts (new, 6 cases): lowercase access,
lazy construction, coexistence with legacy uppercase, dispose,
introspection map, empty schema fallback.
Total suite: 1408 pass (1402 + 6 new). The legacy translators,
APP_EVENT_* publishers and App.createActiveX() factories are still
present; their removal lands in subsequent commits of step 3.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
fb2e3ac507 |
Add App.Orca to the createActiveApp() core
App.Orca is the orchestration engine instance built alongside Logger, Bus and Timers. It is inert until the application registers actions via App.Orca.onEvent() or applies a preset from arts/active-app/presets/. Construction order: Logger → Lang → Storage → ... → Timers → Bus → Orca → Cache → (lazy services). Orca disposes before Bus and Timers to ensure its bus subscriptions and any future timer-based features are torn down cleanly. This is the first surgical change of step 3 (the big-bang merger of the new service-schema model with the legacy createActiveApp). The legacy App.createActiveX() factories remain in place; subsequent commits will migrate the schema-driven services and remove the legacy translators / orchestration translators / APP_EVENT_* patches. Tests: API surface test in active-app.test.ts updated to include the new Orca key. Total suite: 1402 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
a04fa67152 |
Add cache/perm/session/connections service factories and orca presets
Closes the second half of step 2 of the active-app refactor: every
service in the schema now has a defineActiveX/defineEngineX factory,
and the orchestration that lived inside arts/cache and arts/perm as
internal bus subscriptions moves to opt-in presets registered via
App.Orca.
Service factories added (4):
- defineActiveCache — passes only logger from core. The legacy
autoInvalidateOn / bus options are still accepted for back-compat
but no longer recommended.
- defineActivePerm — same shape as cache; perm endpoint required
via options.
- defineActiveSession — wires core.bus into the session so
SESSION_EVENT_LIFECYCLE_* events flow without per-app config.
- defineActiveConnections — requires logger + timers from core.
Presets added in arts/active-app/presets/ (4):
- applyCacheClearOnIdentityChange — listens to
SESSION_EVENT_IDENTITY_CHANGED, calls cache.clear().
- applyCacheClearOnRevoke — listens to SESSION_EVENT_REVOKED,
calls cache.clear().
- applyPermInvalidateOnIdentityChange — listens to
SESSION_EVENT_IDENTITY_CHANGED, calls perm.invalidate().
- applyStandardOrca — aggregator that registers every preset whose
services are present on App. Returns a single detacher.
Each preset is a function (App: AppShape) => () => void, where
AppShape is structurally typed against the orca instance and the
specific services the preset needs. Presets cherry-pick what they
need from each service via Pick<…, 'clear' | 'invalidate'>.
Tests: 8 cases for the presets (publish event → assert imperative
API called, detacher unregisters, aggregator skips absent services,
errors recorded in run trace). Total suite: 1402 tests pass
(1394 + 8).
Pending for the big-bang merge in a separate session:
- Rewrite createActiveApp() to consume the schema and remove the
legacy App.createActiveX() factories.
- Delete wireSessionTranslator, createAuthCacheInvalidator and
APP_ORCHESTRATION_*.
- Delete APP_EVENT_USER_IDENTITY_CHANGED / TENANT_SWITCHED /
PERMISSIONS_REFRESH_REQUESTED / CACHE_INVALIDATE_REQUESTED /
CONNECTIVITY_CHANGED. Keep only DISPOSE_STARTING.
- Migrate web/routes/* and the ecosystem integration tests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
d528652640 |
Add AppServiceSchema contract, builder and pure-art service factories
First half of step 2 of the active-app refactor. Lays the foundation
for the declarative service-schema model documented in
arts/active-app/refactorizacion.md (sections 8.4 and 11). Does NOT yet
rewrite createActiveApp() — that big-bang lands together with the
removal of the legacy translators in step 3.
Pieces added:
- services.ts: AppServiceFactory<TName, TCoreDeps, TServiceDeps,
TInstance>, CoreServices (logger, bus, timers, orca),
AppServiceSchema, ServiceInitMode, ServiceStatus,
ResolveServiceInstances<TSchema>.
- service-builder.ts: validates the schema (key === factory.name),
computes topological order with cycle detection, builds
`immediate` services in order, exposes lazy getter proxies for
`lazy` services, tracks per-service status, disposes in reverse
construction order swallowing dispose errors.
- errors.ts: AppServiceNameMismatchError,
AppServiceDependencyCycleError (carries the cycle path),
AppServiceConstructionFailedError (wraps the original cause).
Codes consolidated into APP_ERROR_MESSAGES via rule 6.
- service-factories/ — pure-art adapters that don't subscribe to
APP_EVENT_*: lang, storage, format, dom, frontend, http, sium,
auth. Each one is a thin wrapper that adapts createActiveX or
createEngineX into the AppServiceFactory shape.
Pending for step 2/3 merger:
- cache, perm, session, connections factories — they still
auto-subscribe internally to APP_EVENT_*; that subscription
lifts out as orca presets in step 3.
- Rewrite createActiveApp() to consume the schema and remove the
legacy `App.createActiveX()` factories.
Tests: 19 unit cases for the builder (schema validation, topology,
init modes, failure handling, disposal) + 4 integration cases that
build a real core (Logger, Bus, Timers, Orca) and instantiate every
factory through the builder. Total suite: 1394 tests pass (1371 + 23).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
0eddd2a0e6 |
Implement arts/orca v0.0 — orchestration engine (surface complete, motor minimal)
First step of the active-app refactor (see arts/active-app/refactorizacion.md).
orca is the orchestration motor that replaces the embryonic
APP_ORCHESTRATION_TRANSLATOR_* system in aapp. Presets registered via
App.Orca.onEvent() will replace the internal bus subscriptions in
arts/cache, arts/perm and arts/connection in step 3.
v0.0 implements the full public contract documented in the appendix E of
the refactorizacion document:
- createEngineOrca({ bus, timers, logger }) — engine factory.
- onEvent(event, action) — registration with detach. Lazy bus
subscription per event; auto-unsubscribes when the last action is
removed.
- Stages (guard/pre/main/post/cleanup/finally) executed in canonical
order; same-stage actions in registration order. FINALLY always
runs, even after abort.
- Result helpers (orcaSuccess, orcaSkipped, orcaError, plus orcaTimeout
and orcaFatal as v0.1+ shapes producible by authors but not by the
engine).
- Run trace: OrcaRunResult with startedAt/endedAt/durationMs and per-
action OrcaActionRun entries with status / emitted tokens.
- Concurrency: implicit QUEUE per event. Events arriving during a run
enqueue FIFO and are processed sequentially.
- Catalogued diagnostics via $libs/logger (RUN_STARTED, RUN_COMPLETED,
RUN_ABORTED, ACTION_STARTED, ACTION_COMPLETED, ACTION_FAILED,
ACTION_SKIPPED, CONFIGURATION_INVALID).
- Error infrastructure follows rule 6: codes + ErrorMessages dict +
classes + guards in arts/orca/errors.ts.
Accepted-but-ignored fields (forward-compat for v0.1+):
- after / unless / abortOn — token coordination ignored.
- actionTimeoutMs — long actions hang.
- compensate — never invoked.
- ABORT_ACTION / ABORT_STAGE — treated as CONTINUE.
Tests: 37 cases covering registration, execution, error policies,
concurrency, run trace, disposal, and forward-compat acceptance of the
@v0.1+ fields. Total suite: 1371 tests pass (1334 + 37).
Adds $orca alias to svelte.config.js. No other module references orca
yet — App.Orca will be wired in step 2 (aapp service-schema refactor).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
f49c60d144 |
Expand active-app refactorizacion.md with orca v0.0 contract
Adds three sections to the working document:
- §8 Architectural review round 1: concrete code proposals for
services.ts, active-app.svelte.ts and types.ts; decisions on the
open questions (Bus always present, big-bang migration, optional
TSchema with default {}); list of legacy code that disappears with
the refactor.
- §9 Architectural review round 2: orca v0.0 as a hard pre-requisite
for the aapp refactor (surface complete, engine minimal). Critical
correction over §3 — presets and define*() factories live in
arts/active-app/ (presets/ and service-factories/), not in each
art, so arts stay pure and the dependency inversion is preserved.
App.Orca chosen as the namespace label.
- §10-§11 Revised 4-step implementation plan and consolidated 17-item
decision list.
- Appendix D: final directory layout plus pure-art / factory / preset
examples.
- Appendix E: full EngineOrca v0.0 contract including consts.ts,
errors.ts, types.ts (with @v0.0 / @v0.1+ markers on every accepted-
but-ignored field), result.ts helpers, engine-orca.ts implementation
(~600 lines), index.ts barrel, test matrix and explicit list of what
the v0.0 engine does NOT do.
No code in src/* changed. Implementation pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
857fcd1049 |
Add refactorizacion.md documenting active-app redesign plan
Working document capturing the architectural analysis for arts/active-app: removal of APP_EVENT_* patches, decoupling cache/perm/connection from App-level events, and migration to a declarative AppServiceSchema with core (always present) vs services (opt-in) distinction. Lays out the phased plan for execution and the contract that orca v0 expects. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
3afcf00900 |
Move runtime types and SILENT_* implementations from libs/* to arts/*
Pure libs/* keeps only abstract contracts (TimerScheduler, EventPublisher, EventSubscriber, BusEnvelope, LangNode, plus codes and ERROR_MESSAGES dicts). Runtime interfaces (Engine*, Active*, Engine*Options) and concrete no-op implementations (SILENT_BUS, SILENT_LOGGER) move to arts/*. Cross-layer moves: - EngineLang, ActiveLang: libs/lang/types.ts → arts/lang/types.ts - EngineBus, EngineBusOptions: libs/bus/types.ts → arts/bus/types.ts - SILENT_BUS: libs/bus/silent-bus.ts → arts/bus/silent-bus.ts - SILENT_LOGGER: libs/logger/silent-logger.ts → arts/logger/silent-logger.ts - EngineTimers, EngineTimersOptions: libs/timer/types.ts → arts/timer/types.ts - bus-context.svelte.ts: libs/active-app/ → arts/active-app/ New: EventSubscriber<TEvents> contract in libs/bus/types.ts. EngineBus now extends EventPublisher + EventSubscriber. libs/active-app/events.ts uses it for AppEventBus instead of Pick<EngineBus, 'on' | 'once'> so the libs layer no longer references the runtime interface. libs/logger/diagnostics.ts uses a private NOOP_LOGGER fallback. The public SILENT_LOGGER exports from $logger. Apply rule 6 to arts/timer: ErrorMessages dict (libs/timer/errors.ts) now references the same canonical builders the Timer*Error classes use, so the message lives in one place. Class constructors take semantic parameters (method, key, delayMs) instead of pre-formatted strings. Apply rule 6 to libs/days: codes + ErrorMessages + classes + guards. arts/format/errors.ts removed (FORMAT_ERROR_MESSAGES.INVALID_LOCALE was dead code). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
369ba66097 |
Move error message constants from consts.ts to errors.ts
Each module's `_ERROR_MSG_*` strings and the helper functions that build
error messages now live in `errors.ts` alongside the codes, classes and
guards. `consts.ts` keeps only non-error values: log messages, method
names, event types, configuration defaults.
Modules updated: arts/{auth,cache,connection,http,perm,session,timer},
libs/perm, svrs/{cache,perm}. The libs/errs exception (codes inline in
consts.ts due to import cycle with the foundational error system) is
preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
0b2c4eb30b |
Consolidate error infrastructure into errors.ts per module
Apply the canonical error pattern (docs/conventions.md rule 6) to the remaining 16 modules: every module's <MOD>_ERR seed, <MOD>_ERR_* codes, <MOD>_ERROR_MESSAGES catalogue, error classes and is*Error guards now live in a single errors.ts file. consts.ts retains only configuration unrelated to errors (event names, default values, op identifiers). Modules consolidated: - libs/active-app, libs/auth, libs/bus, libs/cache, libs/dom, libs/lang, libs/perm, libs/timer (new errors.ts) - arts/active-app, arts/auth, arts/connection, arts/http, arts/logger, arts/perm, arts/session, arts/sium - svrs/perm `<MOD>_ERROR_MESSAGES: ErrorMessages` declared in every module, indexed by ErrCode. The ErrorMessages type was added to libs/errs/code in the previous pass; this commit propagates its use everywhere. `libs/errs/consts.ts` is the documented exception: its codes (`errs::format_invalid`, `errs::unknown`) live as string literals in consts.ts because errors.ts imports them and code.ts imports CodeFormatError from errors.ts — the import graph forbids using errCode() at that position. Side cleanups: - libs/cache: legacy CACHE_ERROR_MESSAGES dict (validation strings) renamed to CACHE_VALIDATION_MESSAGES so the canonical CACHE_ERROR_MESSAGES (ErrorMessages indexed by ErrCode) is the only symbol with that name. - libs/perm: PERM_MODULE = 'perm' added (it was implicit before). - arts/auth, arts/perm, arts/active-app: client-only error codes (request_failed, disposed, invalid_response, no_context, etc.) live in the artifact's errors.ts but reuse the libs <MOD>_ERR seed so every code in the family shares the same module prefix. - libs/timer: errors.ts created (didn't exist) housing the codes that were previously in consts.ts; library has no classes (timer error classes live in arts/timer/errors.ts and import the codes from libs/timer). - arts/sium: SIUM_ERR seed, codes and three error classes (SiumValidationError, SiumAsyncSchemaError, SiumDiscriminatedUnionError) consolidated. Classes were previously in core/types.ts; that file no longer carries error infrastructure. All call sites in arts/sium/core/* and arts/sium/types/* updated their imports from `./types`/`../core/types` to the module's `errors.ts`. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
7f2577c8da |
Rename permissions→perm, formats→format; consolidate sium error infra
Two more module renames flipping the direction of the previous pass: - arts/permissions, libs/permissions, svrs/permissions, libs/svrs/permissions.ts → arts/perm, libs/perm, svrs/perm, libs/svrs/perm.ts. Alias: $permissions → $perm. Constants: PERMISSION_* → PERM_*. Wire: 'permissions::*' → 'perm::*'. Module value: 'perm'. Class names: Permission*Error → Perm*Error. Helper functions: permissionDecisionKey → permDecisionKey (and similar). - arts/formats → arts/format (with the four sub-modules currency, numbers, units, dates carried along). Alias: $formats → $format. Constants: FORMATS_* → FORMAT_*. Wire: 'formats::*' → 'format::*'. Class names: Formats*Error → Format*Error. Both follow the auth/http/dom precedent: short word as the canonical name. The earlier full-word forms (permissions, formats) created asymmetric prefixes (PERMISSION_* singular, PERMISSIONS_REFRESH plural) that were already showing as drift in this commit's call sites. Plus a fix to sium error structure that was carried over from the previous audit round but never fully consolidated: - arts/sium/errors.ts now owns the full error infra: SIUM_ERR seed, all SIUM_ERR_* codes, SIUM_ERROR_MESSAGES catalog, error classes (SiumValidationError, SiumAsyncSchemaError, SiumDiscriminatedUnionError), guards and the SIUM_ERROR_MESSAGES type. The legacy SIUM_ERRORS string catalog stays for the few non-thrown sites until those are migrated. - arts/sium/consts.ts no longer carries error codes — only module identifier and diagnostic events. - arts/sium/core/types.ts no longer carries error classes — only schema types. - All call sites in arts/sium/core/* and arts/sium/types/* now import the error classes from `../errors` instead of `../core/types` / `../consts`. This is the canonical pattern documented in conventions.md rule 6: all of a module's error infrastructure lives in a single errors.ts file; consts.ts is for module configuration that has nothing to do with errors. Sium is now compliant; the rest of the modules will follow in subsequent commits. All 1334 tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
4db6bd2b3b |
Rename modules from 4-letter aliases to full English words
Drops the 4-letter alias convention in favour of a single homogeneous
naming axis: full English words across filesystem, alias, wire format
and constants.
Module renames:
- arts/aapp → arts/active-app (libs/aapp also)
- arts/buss → arts/bus (libs/buss also)
- arts/cach → arts/cache (libs/cach + svrs/cach also)
- arts/conn → arts/connection
- arts/fend → arts/frontend
- arts/fmts → arts/formats (curr→currency, nums→numbers, unts→units)
- arts/logr → arts/logger (libs/logr also)
- arts/perm → arts/permissions (libs/perm + svrs/perm also)
- arts/sess → arts/session
- arts/stor → arts/storage
- arts/timr → arts/timer (libs/timers → libs/timer)
Modules left as-is: auth, dom, errs, http, lang, sium (already match
their canonical name or are proper names).
Special case: `aapp` could not become `app` because `$app` is reserved
by SvelteKit (`$app/stores`, `$app/navigation`, ...). Compromise:
- Filesystem and alias use `active-app` / `$active-app`.
- Constants and class names use `App` / `APP_*` (no `active-` prefix).
The `active-` prefix only disambiguates the alias from SvelteKit's
namespace; the module is App.
Special case: `permissions` keeps the plural for filesystem/alias/wire
but constants and classes use the singular `PERMISSION_*` /
`Permission*` because they describe the concept ("a permission
effect"), not the module collection.
Constants follow the new module name in caps: `STORAGE_*`, `BUS_*`,
`CACHE_*`, `CONNECTION_*`, `FORMATS_*`, `LOGGER_*`, `SESSION_*`,
`TIMER_*`, etc. Module values: `STORAGE_MODULE = 'storage'`,
`BUS_MODULE = 'bus'`, `APP_MODULE = 'app'`,
`PERMISSION_MODULE = 'permissions'`, etc.
Wire/code format moved accordingly: `'storage::*'`, `'bus::*'`,
`'session::*'`, `'permissions::*'`, etc. Diagnostic event values
updated: `'storage.error'`, `'bus.event.published'`,
`'connection.auth_failed'`, etc. App events use `'app.*'`:
`AAPP_EVENT_* → APP_EVENT_*` with values `'app.user.identity.changed'`.
Class renames (where they used the abbreviation):
- AappAlreadyCreatedError → AppAlreadyCreatedError
- BussError* → BusError* (where applicable)
- Cach* → Cache*
- Conn* → Connection* (e.g. ConnDisposedError → ConnectionDisposedError;
ConnConnection* collapsed to Connection*)
- Logr*Error → Logger*Error
- Sess* → Session* (SessInvalidSessionError → SessionInvalidError)
- Stor* → Storage*
- Timr* → Timer* (TimrInactiveTimerError → TimerInactiveError)
- AuthCachPort → AuthCachePort
- AuthClientCach* → AuthClientCache*
- AuthPermPort → AuthPermissionsPort
Property renames in option types:
- `cach?:` → `cache?:` in AuthClient options
- `logr:` → `logger:` in svrs/auth ports
- `timr:` → `timer:` in svrs/auth ports
`docs/conventions.md` rewritten:
- Rule 1 dropped the 4-letter alias mandate; lists the full English
module names and special-cases active-app, lang, sium, permissions.
- Rule 2 documents the new constant prefix convention and its two
exceptions (APP_* for active-app, PERMISSION_* singular for
permissions).
- Rule 6 codifies that all error infrastructure (codes, messages,
classes, guards) lives in a single `errors.ts` per module —
removing the `consts.ts` / `errors.ts` split for error-related
symbols.
`libs/errs` adds `ErrorMessages` type so every module can declare its
catalog as `<MOD>_ERROR_MESSAGES: ErrorMessages` instead of repeating
the `Readonly<Record<ErrCode, string | (...args) => string>>` shape.
Storage migrated as the first proof of the canonical pattern.
All 1334 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
dace6d00d1 |
Add libs/errs README documenting the canonical error system
Captures the conventions that landed across the audit-1-5 migration: ErrCode and ModuleSeed shapes, the `errCode(parent, segment)` builder, the per-module recipe (consts.ts + errors.ts + guards), family matching with `matches(err, family)`, i18n derivation via `codeToLangPath()`, the immutable decorator API, wire-safe projections, and the don'ts (no string-concat construction, no parallel `<MOD>_ERROR_CODES` legacy enums, no `instanceof Error`). Closes the last open item from the errs migration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
97d8b605b9 |
Consolidate libs/auth errors onto CodeError (option B)
The auth artifact had its own parallel error system: AUTH_ERROR_CODES
(strings like `'AUTH_CONFIG_INVALID'`), AUTH_SAFE_MESSAGES
(`'auth.error.config_invalid'` i18n keys) and `AuthError` extending
`Error` with extra `messageKey`/`code`/`data` fields. With this commit
auth uses the same canonical error system as the rest of the codebase.
What changed:
- libs/auth/consts.ts: AUTH_ERROR_CODES and AUTH_SAFE_MESSAGES gone.
Adds AUTH_ERR seed plus 20 codes (`auth::config_invalid`,
`auth::adapter_failed`, …, `auth::webauthn_failed`).
- libs/auth/errors.ts: AuthError now extends CodeError; the 20
subclasses pass their ErrCode to the base via a thin
`(cause?, data?)` constructor. Type guards unchanged.
- libs/auth/types.ts: AuthErrorCode is now an `ErrCode` alias.
AuthClientSafeError reduces to `{ code: ErrCode }` — the UI derives
the i18n path on demand via `codeToLangPath(error.code)`.
- arts/auth/consts.ts: re-exports AUTH_ERR + the 20 libs codes;
keeps three client-only codes (`request_failed`, `disposed`,
`invalid_response`).
- svrs/auth/handler-runtime.ts: switch statements updated to the new
AUTH_ERR_* identifiers; route-not-found payload is `{ code }` only.
- arts/auth/active-auth-runtime.ts, arts/auth/client.ts: dropped
messageKey from AuthClientSafeError reads/writes.
- Tests in arts/auth + svrs/auth: switched to AUTH_ERR_* constants.
- READMEs (arts/auth, libs/auth, svrs/auth) refreshed: examples now
use codeToLangPath() to derive i18n keys; old AUTH_ERROR_CODES /
AUTH_SAFE_MESSAGES references replaced.
Breaking change: any consumer reading `error.messageKey` must call
`codeToLangPath(error.code)` instead. The wire shape sent to the
client now carries only `{ code }`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
5 months ago |
|
|
c2197f58d8 |
Replace remaining generic Error throws with typed CodeError classes
Eight `throw new Error(...)` sites across stor, logr, dom and sium-union now throw typed errors backed by `CodeError` and discoverable via `is*Error` guards: - arts/stor/entry-values.ts → StorValidationFailedError (`stor::validation_failed`) - arts/stor/adapters/cookie.ts → StorCookieServerRequiredError (`stor::cookie_server_required`) - arts/logr/transports.ts → LogrHttpTransportPushFailedError (`logr::http_transport_push_failed`) - arts/logr/adapters/loki.ts → LogrLokiPushFailedError (`logr::loki_push_failed`) - libs/dom/core.ts → DomDocumentRequiredError / DomWindowRequiredError (`dom::document_required`, `dom::window_required`) - arts/sium/core/union-combinators.ts → SiumDiscriminatedUnionError with three sub-codes (`sium::discriminated_union.member_not_object`, `…member_missing_key`, `…member_not_literal`) Adds `LOGR_MODULE`, `LOGR_ERR`, `STOR_ERR`, `DOM_MODULE`, `DOM_ERR` and the new error files / barrel exports. The string catalogs (LOGR_ERRORS, STOR_ERRORS, DOM_ERRORS) stay as message providers; the new classes wrap them. Runtime code now has zero `throw new Error(...)` sites outside vendored day-picker code (libs/days/_vendor) and tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
f98fad7e35 |
Migrate arts/http error tree to CodeError
The four engine errors (HttpNetworkError, HttpTimeoutError, HttpAbortError, HttpBodyValidationError) now extend CodeError directly. The intermediate `HttpEngineError` abstract base is gone — CodeError provides the shared shape, and the cause-bearing constructor is no longer needed at a separate layer. Adds HTTP_ERR seed plus four codes (`http::network`, `http::timeout`, `http::abort`, `http::body_validation`); the legacy `HTTP_ERROR_NAME_*` constants are removed. Type guards still compare on `name` (rather than `instanceof`) so they remain stable across worker boundaries; `CodeError` sets `name` from the code, so the cross-realm contract is preserved with a comment that explains why. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |
|
|
834775099e |
Migrate libs/aapp, arts/aapp and arts/auth errors to CodeError
Three small error catalogs converted to the canonical CodeError + ErrCode system: - libs/aapp: AappBusNoContextError, AappInvalidEventRuntimeError, AappUnsafeEventPayloadError. Adds AAPP_ERR seed plus the bus/event hierarchy (`aapp::bus.no_context`, `aapp::event.invalid_runtime`, `aapp::event.unsafe_payload`). - arts/aapp: AappAlreadyCreatedError. Reuses libs/aapp's AAPP_ERR seed and adds AAPP_ERR_ALREADY_CREATED. - arts/auth: AuthRequestFailedError, AuthDisposedError, AuthInvalidResponseError. Defines AUTH_ERR seed locally; once libs/auth migrates the seed converges (same module name, same `'auth::'` prefix, no conflict). The legacy `AAPP_*_ERROR_NAME_*` and `AUTH_ERROR_NAME_*` constants are removed — the error name now derives from the code, matching the pattern in buss/conn/sess/perm/cach/sium/lang/timr. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
5 months ago |