@cynodia/axiom 0.8.0-alpha.1 → 0.8.1-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/ACTIONS_TRANSACTIONS.md +1 -1
- package/docs/AGENT_API.md +1 -1
- package/docs/AGENT_REFERENCE.md +11 -7
- package/docs/ANTI_PATTERNS.md +6 -3
- package/docs/AUTHORITY.md +45 -2
- package/docs/CONSTRAINTS.md +1 -1
- package/docs/EFFECTS.md +35 -6
- package/docs/EVENTS.md +5 -1
- package/docs/EXPRESSIONS.md +1 -1
- package/docs/GRAPH_MODEL.md +1 -1
- package/docs/INTEGRATIONS.md +23 -4
- package/docs/LOCATIONS.md +1 -1
- package/docs/PRESENTATION.md +1 -1
- package/docs/RUNTIME.md +1 -1
- package/docs/SEMANTIC_CONTRACT.md +1 -1
- package/docs/STATE.md +1 -1
- package/docs/TRIGGERS.md +44 -6
- package/docs/UI.md +1 -1
- package/docs/VALIDATION.md +4 -1
- package/package.json +5 -5
package/docs/AGENT_API.md
CHANGED
package/docs/AGENT_REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Agent reference
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Compressed operational contract. Read this plus the `.d.ts`
|
|
4
4
|
declarations before authoring or modifying an Axiom application.
|
|
5
5
|
|
|
6
6
|
Formal guarantees: [`SEMANTIC_CONTRACT.md`](SEMANTIC_CONTRACT.md). Mistakes that compile:
|
|
@@ -605,12 +605,13 @@ before authoring an application that reaches an external system or reacts to tim
|
|
|
605
605
|
event.
|
|
606
606
|
|
|
607
607
|
1. **INTEGRATION INVARIANT** — external systems are accessed through typed integration operations; the graph never carries an SDK, a host name or a secret.
|
|
608
|
-
2. **QUERY INVARIANT** — external queries are explicit execution, resolved before the transaction they feed opens — never a pure `Expression`.
|
|
609
|
-
3. **EFFECT INVARIANT** — external effects are not rollback-capable state mutations. Reaching `integration-effect` only records intent; the adapter runs only after commit.
|
|
608
|
+
2. **QUERY INVARIANT** — external queries are explicit execution, resolved before the transaction they feed opens — never a pure `Expression`. `timeoutMs` is enforced by the runtime itself, not left to adapter cooperation; a non-cooperating adapter cannot wedge the invocation past its deadline (spec 8.1 §15-25).
|
|
609
|
+
3. **EFFECT INVARIANT** — external effects are not rollback-capable state mutations. Reaching `integration-effect` only records intent; the adapter runs only after commit. The outcome reaches a follow-up action as a structured envelope (`effectOutcomeEntity` — `EFFECT_ID_FIELD`/`EFFECT_OPERATION_ID_FIELD`/…), never a raw result or a formatted string requiring text parsing to correlate (spec 8.1 §37-41).
|
|
610
610
|
4. **OUTBOX INVARIANT** — effect intent is committed atomically with the state write that requested it, before external execution, so a crash between the two does not lose it.
|
|
611
|
-
5. **TRIGGER INVARIANT** — a trigger invokes an ordinary action, under exactly the guards, constraints, transition constraints and authorization any other caller is subject to.
|
|
611
|
+
5. **TRIGGER INVARIANT** — a trigger invokes an ordinary action, under exactly the guards, constraints, transition constraints and authorization any other caller is subject to. Every invocation this authority runs — client request or trigger tick — is serialized against every other one, so simultaneous same-period triggers commit one at a time identically on the deterministic and the real host (spec 8.1 §26-30).
|
|
612
612
|
6. **EVENT INVARIANT** — an event is a typed fact, validated against its declared payload type before any action sees it; an action is where work happens.
|
|
613
613
|
7. **SECRET INVARIANT** — integration credentials live in host configuration (`AxiomServerOptions.integrations`), never in `ApplicationGraph`.
|
|
614
|
+
8. **INVOCATION SOURCE INVARIANT** — a system-originated invocation (trigger, event, effect outcome) and an anonymous client request are distinct authoritative facts; a client cannot forge the former (`ExecutionContext.source` is server-computed, never read from protocol data), and `ActionDef.invocation.allowedSources` lets an action restrict which it accepts independently of `authorization`'s identity check (spec 8.1 §3-14).
|
|
614
615
|
|
|
615
616
|
```ts
|
|
616
617
|
{ kind: 'integration', id: INTEGRATION_DEVICE_PROVIDER }
|
|
@@ -630,7 +631,7 @@ event.
|
|
|
630
631
|
|
|
631
632
|
- `mode: 'query'` may bind its result into scope (`bindAs`) for later operations in the same action; `mode: 'effect'` never runs synchronously and its outcome reaches an action only through a dispatched `succeededEventId`/`failedEventId`.
|
|
632
633
|
- A trigger's target action runs where the action itself runs — server if it writes server state or calls an integration, client only for `route-enter`/`route-leave`. Derived, never declared, exactly like ordinary action authority.
|
|
633
|
-
- A triggered/event-originated invocation runs with `principal: null`, `source: 'system'` — the same as an anonymous client request, never an impersonated user. Authorization still evaluates.
|
|
634
|
+
- A triggered/event-originated invocation runs with `principal: null`, `source: 'system'` — the same as an anonymous client request, never an impersonated user. Authorization still evaluates, and `invocation.allowedSources` is checked before it: `{ invocation: { allowedSources: ['system'] } }` refuses a direct client `InvokeRequest` with `INVOCATION_SOURCE_NOT_ALLOWED`, which is what protects a webhook- or effect-outcome-only action from being forged by a client that guessed its id.
|
|
634
635
|
- `createDeterministicServerHost().advance(ms)` fires due timers deterministically; no trigger test waits on a real clock.
|
|
635
636
|
|
|
636
637
|
```ts
|
|
@@ -639,6 +640,8 @@ agent.getActionsUsingIntegration(id) / agent.getEffectsForAction(actionId);
|
|
|
639
640
|
agent.getTriggersForAction(actionId) / agent.getTimedTriggers();
|
|
640
641
|
agent.getActionsTriggeredByEvent(eventId) / agent.getWebhookEvents();
|
|
641
642
|
agent.getExternalDependencies(); // { integrations, operations } — the deployment manifest
|
|
643
|
+
agent.getSystemOnlyActions() / agent.getTriggersTargetingClientOnlyActions();
|
|
644
|
+
agent.isClientInvocable(actionId) / agent.isSystemOnly(actionId);
|
|
642
645
|
```
|
|
643
646
|
|
|
644
647
|
Portable artifacts, for a runtime written in another language:
|
|
@@ -651,8 +654,9 @@ Portable artifacts, for a runtime written in another language:
|
|
|
651
654
|
```
|
|
652
655
|
|
|
653
656
|
Boundary diagnostics: `UNKNOWN_SERVER_ACTION` `ARGUMENT_TYPE_MISMATCH` `AUTHORIZATION_DENIED`
|
|
654
|
-
`
|
|
655
|
-
and `REMOTE_ACTION_UNAVAILABLE` on the
|
|
657
|
+
`INVOCATION_SOURCE_NOT_ALLOWED` `CONCURRENCY_CONFLICT` `MALFORMED_REQUEST`
|
|
658
|
+
`AUTHORITY_UNREACHABLE`, plus `SERVER_STATE_WRITE` and `REMOTE_ACTION_UNAVAILABLE` on the
|
|
659
|
+
client.
|
|
656
660
|
|
|
657
661
|
## Metadata classes
|
|
658
662
|
|
package/docs/ANTI_PATTERNS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Anti-patterns
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Each of these compiles. Each is wrong. Each is followed by the correct
|
|
4
4
|
alternative.
|
|
5
5
|
|
|
6
6
|
## 1. Field names as entity runtime keys
|
|
@@ -390,8 +390,11 @@ credential belongs to the host, not the graph.
|
|
|
390
390
|
{ kind: 'native', implementationId: 'app.sendEmail' }
|
|
391
391
|
```
|
|
392
392
|
|
|
393
|
-
|
|
394
|
-
|
|
393
|
+
Use `IntegrationDef` + an `integration-effect` operation instead — a typed semantic
|
|
394
|
+
operation, dispatched post-commit through the durable outbox, with retry and an
|
|
395
|
+
idempotency key, whose outcome reaches a follow-up action as a structured event rather than
|
|
396
|
+
a side effect nothing can roll back. See [`INTEGRATIONS.md`](INTEGRATIONS.md) and
|
|
397
|
+
[`EFFECTS.md`](EFFECTS.md).
|
|
395
398
|
|
|
396
399
|
## 31. Restating what the model already knows
|
|
397
400
|
|
package/docs/AUTHORITY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Authority
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. How an application crosses the trust boundary.
|
|
4
4
|
|
|
5
5
|
Until 0.5.x an Axiom application executed locally. 0.6 adds an **authority**: a generic
|
|
6
6
|
runtime that owns state, decides mutations and persists them. The same semantic graph
|
|
@@ -39,6 +39,7 @@ describes both halves, so there is no backend to write.
|
|
|
39
39
|
18. **TRIGGER** — a trigger invokes an ordinary action, under the same guards, constraints, transition constraints and authorization any other caller is subject to. See [Triggers](#triggers).
|
|
40
40
|
19. **EVENT** — an event is a typed fact, validated against its declared payload type before any action sees it; an action is where work happens. See [External events](#external-events).
|
|
41
41
|
20. **SECRET** — integration credentials live in host configuration, never in `ApplicationGraph`. See [External systems](#external-systems).
|
|
42
|
+
21. **INVOCATION SOURCE** — a system-originated invocation (trigger, event, effect outcome) and an anonymous client request are distinct authoritative facts; a client cannot forge the former, and an action may restrict which it accepts independently of caller identity. See [Invocation source](#invocation-source).
|
|
42
43
|
|
|
43
44
|
## Authority and persistence are different questions
|
|
44
45
|
|
|
@@ -88,8 +89,49 @@ claim that validation already happened.
|
|
|
88
89
|
| Forge an action id | resolved from the authority's own IR → `UNKNOWN_SERVER_ACTION` |
|
|
89
90
|
| Send an argument of the wrong shape | checked against the declared type → `ARGUMENT_TYPE_MISMATCH` |
|
|
90
91
|
| Invoke without permission | `authorization`, evaluated on the authority → `AUTHORIZATION_DENIED` |
|
|
92
|
+
| Invoke an action reachable only by a trigger, event or effect outcome | `invocation.allowedSources` → `INVOCATION_SOURCE_NOT_ALLOWED` |
|
|
93
|
+
| Claim to be a trigger, event or effect outcome | `context.source` is server-computed; no protocol field lets a request supply it |
|
|
91
94
|
| Read server-only state | it is not in the client IR, the snapshot or any answer |
|
|
92
95
|
|
|
96
|
+
## Invocation source
|
|
97
|
+
|
|
98
|
+
`authorization` answers *who* may invoke an action; invocation source answers *how the
|
|
99
|
+
invocation reached the authority at all* — a distinct question, because the two can diverge.
|
|
100
|
+
An action meant only as a webhook's target, or only as an `integration-effect`'s
|
|
101
|
+
`succeededEventId`/`failedEventId` handler, may declare no `authorization` at all — it was
|
|
102
|
+
never meant to need one, since only the trigger runtime was ever supposed to call it. Before
|
|
103
|
+
this existed, any client that guessed the action's id could invoke it directly, forging a
|
|
104
|
+
fake webhook delivery or a fake effect outcome (spec 8.1 §3-9).
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
{
|
|
108
|
+
id: ACTION_APPLY_STATUS_CHANGE,
|
|
109
|
+
kind: 'action',
|
|
110
|
+
invocation: { allowedSources: ['system'] }, // a client InvokeRequest is refused
|
|
111
|
+
operations: [ /* … */ ],
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`allowedSources` is `['client', 'system']` — both — unless declared otherwise, so every
|
|
116
|
+
existing graph keeps its current behavior. `'system'` covers every trigger kind (interval,
|
|
117
|
+
delay, lifecycle, event) and every effect-outcome dispatch; there is no finer distinction,
|
|
118
|
+
because a trigger invokes an action "through the same semantics as any other caller"
|
|
119
|
+
([TRIGGER](#load-bearing-server-invariants)) and does not get a different one here either.
|
|
120
|
+
|
|
121
|
+
The source itself is `ExecutionContext.source`, computed by the authority and never read
|
|
122
|
+
from client-supplied protocol data — `InvokeRequest` and `EventRequest` carry no `source`
|
|
123
|
+
field to forge in the first place. A client request is always `'client'`; a trigger-, event-
|
|
124
|
+
or effect-outcome-originated invocation is always `'system'`, with `principal: null` exactly
|
|
125
|
+
as an anonymous client's is, so `authorization` still evaluates and cannot be bypassed by a
|
|
126
|
+
trigger either (spec §69,104) — invocation source and principal are deliberately separate
|
|
127
|
+
questions, and a system invocation is not a stand-in identity.
|
|
128
|
+
|
|
129
|
+
`checkInvocationSource` runs before `authorization`, before argument-driven work, and before
|
|
130
|
+
any transaction opens: a refusal changes nothing, commits nothing, and dispatches no effect
|
|
131
|
+
or event. `validateGraph` also catches the case a graph author can state statically — a
|
|
132
|
+
trigger targeting an action whose `allowedSources` excludes `'system'` could never succeed —
|
|
133
|
+
with `TRIGGER_TARGET_SOURCE_MISMATCH`.
|
|
134
|
+
|
|
93
135
|
## Server IR
|
|
94
136
|
|
|
95
137
|
```ts
|
|
@@ -435,6 +477,7 @@ does for a local failure.
|
|
|
435
477
|
| `INTEGRATION_ADAPTER_MISSING` | The Server IR requires an integration with no registered adapter — refused at `start()`, never deferred to first invocation. |
|
|
436
478
|
| `EVENT_DISPATCH_DEPTH_EXCEEDED` | An event → action → effect → event chain was stopped before it could recurse unboundedly. |
|
|
437
479
|
| `WEBHOOK_VERIFICATION_FAILED` | A webhook delivery failed provider signature verification and was refused before an event was ever constructed. |
|
|
480
|
+
| `INVOCATION_SOURCE_NOT_ALLOWED` | The action's `invocation.allowedSources` does not include this invocation's source (spec 8.1 §3-9) — refused before `authorization` is even evaluated, because no caller reaching the authority this way may invoke the action at all. |
|
|
438
481
|
|
|
439
482
|
Two client-side codes belong to the boundary as well:
|
|
440
483
|
|
|
@@ -725,7 +768,7 @@ Stated plainly rather than left to discovery:
|
|
|
725
768
|
- **Generated values cannot be bound within an action, in general.** An operation cannot name a value an earlier operation produced: `uuid()` evaluated in one `insert` cannot be referred to by a later `insert` in the same action. Give the record an identity the action already has — a parameter, or a field of something it read — or perform the second write in a second action. 0.8 adds exactly one narrow, purpose-built exception: an `integration-query`'s `bindAs` result, resolved before the transaction opens (see [External systems](#external-systems)) — not a general operation-result binding mechanism.
|
|
726
769
|
- **Read authorization per caller or per record.** Visibility is per state.
|
|
727
770
|
- **Absolute/cron schedules.** `interval` and `delay` triggers cover "every N milliseconds" and "once after N milliseconds"; a calendar schedule ("every day at 09:00") is not modeled.
|
|
728
|
-
- **Client-side execution of interval, delay and lifecycle triggers.**
|
|
771
|
+
- **Client-side execution of interval, delay and lifecycle triggers.** The browser runtime implements no trigger kind at all. Before spec 8.1 a client-authority trigger silently compiled into `ApplicationIR.triggers` and simply never fired; now `validateGraph`/`compileToIR` reject it with `CLIENT_TRIGGER_UNSUPPORTED` instead, so the gap is a compile-time error rather than a runtime discovery. Only the authoritative runtime executes triggers today.
|
|
729
772
|
- **Durable effect delivery beyond the two shipped `PersistenceAdapter`s.** At-least-once delivery across a restart is real for `createMemoryPersistence` (within the process) and `createSqlitePersistence`; a third adapter earns the same claim only by implementing `loadPendingEffects`/`recordEffectAttempt` itself.
|
|
730
773
|
- **Realtime synchronization**, subscriptions and collaboration. Request/response only.
|
|
731
774
|
- **Query semantics.** Authoritative collections are loaded into runtime state; large-data querying needs its own design.
|
package/docs/CONSTRAINTS.md
CHANGED
package/docs/EFFECTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Effects
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. External effects are not rollback-capable state mutations. This file
|
|
4
4
|
is the delivery model; [`AUTHORITY.md`](AUTHORITY.md#external-effects) is the load-bearing
|
|
5
5
|
statement of why, and [`INTEGRATIONS.md`](INTEGRATIONS.md) is the operation vocabulary this
|
|
6
6
|
builds on.
|
|
@@ -79,6 +79,10 @@ doubles it each time. The wait uses the host's own scheduling
|
|
|
79
79
|
(`ServerHost.scheduleOnce`), so a test can drive it with `createDeterministicServerHost()`
|
|
80
80
|
+ `advance(ms)` and never wait on a real clock.
|
|
81
81
|
|
|
82
|
+
**`IntegrationFailure.retryable: false` is control flow, not metadata.** It stops the
|
|
83
|
+
remaining retry policy immediately, regardless of `maxAttempts` — an adapter that always
|
|
84
|
+
answers `retryable: false` never retries at all, whatever `policy` says (spec 8.1 §73).
|
|
85
|
+
|
|
82
86
|
## Effect status and observability
|
|
83
87
|
|
|
84
88
|
```ts
|
|
@@ -107,12 +111,37 @@ mutation, and mixing the two would misrepresent what actually happened (spec §7
|
|
|
107
111
|
An effect's outcome is never folded back into the transaction that requested it. Instead,
|
|
108
112
|
`succeededEventId`/`failedEventId` — ordinary `EventDef` nodes — are dispatched through
|
|
109
113
|
the same event pipeline an external webhook uses (see [`EVENTS.md`](EVENTS.md)), once the
|
|
110
|
-
outcome is known:
|
|
114
|
+
outcome is known, as a **structured envelope** (spec 8.1 §37-41):
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
import { EFFECT_ID_FIELD, EFFECT_OPERATION_ID_FIELD, EFFECT_RESULT_FIELD, effectOutcomeEntity } from '@cynodia/axiom-core';
|
|
118
|
+
|
|
119
|
+
graph.addNode(effectOutcomeEntity(ENTITY_EFFECT_OUTCOME, primitiveType('string'))); // the operation's resultType
|
|
120
|
+
graph.addNode<EventDef>({ id: EVENT_SUCCEEDED, kind: 'event', payloadType: entityType(ENTITY_EFFECT_OUTCOME) });
|
|
121
|
+
graph.addNode<EventDef>({ id: EVENT_FAILED, kind: 'event', payloadType: entityType(ENTITY_EFFECT_OUTCOME) });
|
|
122
|
+
```
|
|
111
123
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
124
|
+
One shape covers both outcomes — field ids are graph-global, so two entities could not both
|
|
125
|
+
declare `effectId`/`operationId`/`integrationId` without colliding:
|
|
126
|
+
|
|
127
|
+
| Field | Present on success | Present on failure |
|
|
128
|
+
| --- | --- | --- |
|
|
129
|
+
| `EFFECT_ID_FIELD` | always | always |
|
|
130
|
+
| `EFFECT_INTEGRATION_ID_FIELD` | always | always |
|
|
131
|
+
| `EFFECT_OPERATION_ID_FIELD` | always | always |
|
|
132
|
+
| `EFFECT_IDEMPOTENCY_KEY_FIELD` | when declared | when declared |
|
|
133
|
+
| `EFFECT_CORRELATION_ID_FIELD` | when the action's transaction has one | when the action's transaction has one |
|
|
134
|
+
| `EFFECT_RESULT_FIELD` | the operation's own `resultType` value | absent |
|
|
135
|
+
| `EFFECT_CODE_FIELD` | absent | the adapter's failure code |
|
|
136
|
+
| `EFFECT_MESSAGE_FIELD` | absent | the adapter's failure message |
|
|
137
|
+
| `EFFECT_RETRYABLE_FIELD` | absent | whether a retry might have succeeded |
|
|
138
|
+
|
|
139
|
+
A follow-up action correlates the outcome to the effect that caused it through
|
|
140
|
+
`EFFECT_ID_FIELD`/`EFFECT_OPERATION_ID_FIELD` — never by parsing text. Before 8.1, the
|
|
141
|
+
success payload was the raw `resultType` value with no envelope, and the failure payload
|
|
142
|
+
was a single formatted string `"<code>: <message>"`; an application still declaring
|
|
143
|
+
`primitiveType('string')` as either event's `payloadType` now fails validation, because the
|
|
144
|
+
dispatched value is always this entity shape.
|
|
116
145
|
|
|
117
146
|
Both are checked against the declared `EventDef.payloadType` the same way any event is,
|
|
118
147
|
so a mismatched declaration is caught rather than silently dropped.
|
package/docs/EVENTS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Events
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. An event is a typed fact — something that happened — never work
|
|
4
4
|
itself. [`AUTHORITY.md`](AUTHORITY.md#external-events) is the load-bearing statement;
|
|
5
5
|
this file is the vocabulary and the webhook delivery mechanism.
|
|
6
6
|
|
|
@@ -75,6 +75,10 @@ unverified request never reaches the semantic layer at all (`WEBHOOK_VERIFICATIO
|
|
|
75
75
|
401). Provider-specific protocol — headers, signing scheme, payload shape — stays entirely
|
|
76
76
|
in `verify`/`decode`; nothing crosses into `ApplicationGraph`.
|
|
77
77
|
|
|
78
|
+
`serveAxiomApplication({ ..., webhooks })` accepts the same option directly (spec 8.1
|
|
79
|
+
§56-58) — a webhook-receiving application no longer has to drop to `createAxiomServer` +
|
|
80
|
+
`serveOverHttp` just to register one.
|
|
81
|
+
|
|
78
82
|
## Duplicate delivery
|
|
79
83
|
|
|
80
84
|
`decode`'s optional `deliveryId` is deduplicated against a **bounded, per-route,
|
package/docs/EXPRESSIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Expressions
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. An expression describes **what value is computed**. It is a tree of
|
|
4
4
|
plain data, never source text and never a callback. Evaluation is pure: an expression MUST
|
|
5
5
|
NOT change state.
|
|
6
6
|
|
package/docs/GRAPH_MODEL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Graph model
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. The `ApplicationGraph` is the authoritative representation of an
|
|
4
4
|
application. Everything else — the IR, the page, the DOM — is derived from it and is never
|
|
5
5
|
edited.
|
|
6
6
|
|
package/docs/INTEGRATIONS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Integrations
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. How an application declares and calls an external system, without
|
|
4
4
|
embedding a transport, an SDK or a secret in the graph. The authority boundary this
|
|
5
5
|
depends on is [`AUTHORITY.md`](AUTHORITY.md#external-systems); this file is the vocabulary.
|
|
6
6
|
|
|
@@ -85,6 +85,22 @@ that does not conform is never handed to the application as `unknown` — it is
|
|
|
85
85
|
`INTEGRATION_RESULT_INVALID` (a runtime diagnostic; see [`RUNTIME.md`](RUNTIME.md)) before
|
|
86
86
|
`ref(bindAs)` would ever resolve to it.
|
|
87
87
|
|
|
88
|
+
## Timeout
|
|
89
|
+
|
|
90
|
+
**The Axiom runtime enforces `timeoutMs`, not the adapter.** `queryIntegration` races
|
|
91
|
+
`adapter.query(...)` against a `ServerHost.scheduleOnce(timeoutMs, ...)` deadline (spec 8.1
|
|
92
|
+
§15-25) — a non-cooperating adapter whose promise never settles cannot wedge the semantic
|
|
93
|
+
invocation, or by extension a polling interval trigger, forever. On timeout, the invocation
|
|
94
|
+
fails with `INTEGRATION_TIMEOUT` immediately; the adapter's promise is never cancelled (Axiom
|
|
95
|
+
cannot know whether that is safe for an arbitrary provider call), but if it eventually
|
|
96
|
+
settles, that result is simply discarded — it can never mutate state or fire a follow-up,
|
|
97
|
+
because the deadline already answered pre-transaction.
|
|
98
|
+
|
|
99
|
+
An adapter MAY still race its own deadline internally (`createHttpIntegrationAdapter` does,
|
|
100
|
+
via `AbortController`) to cancel the underlying provider call early, but this is an
|
|
101
|
+
optimization, never a correctness requirement: the runtime's own enforcement is what a graph
|
|
102
|
+
author can rely on regardless of which adapter is registered.
|
|
103
|
+
|
|
88
104
|
## Registering an adapter
|
|
89
105
|
|
|
90
106
|
```ts
|
|
@@ -113,9 +129,12 @@ REST service: a base URL, a method and a path template per operation (`{param}`
|
|
|
113
129
|
substituted from the operation's arguments), a JSON body built from the remaining
|
|
114
130
|
arguments, and a timeout via `AbortController`. It is explicitly not the canonical
|
|
115
131
|
integration model — a typed `IntegrationOperationDef` is — only a way to prove one works
|
|
116
|
-
without writing a bespoke adapter for a demo. `createFakeIntegrationAdapter({ query?,
|
|
117
|
-
|
|
118
|
-
|
|
132
|
+
without writing a bespoke adapter for a demo. `createFakeIntegrationAdapter({ query?, effect? })` returns deterministic, caller-supplied
|
|
133
|
+
results: what conformance fixtures and tests use, since semantics must never depend on a
|
|
134
|
+
real network call. Its callbacks receive the same `context` (`{ timeoutMs }` for a query,
|
|
135
|
+
`{ idempotencyKey }` for an effect) the real `IntegrationAdapter` interface does, so a test
|
|
136
|
+
can simulate a hanging call, a declared timeout, or a stable idempotency key without
|
|
137
|
+
dropping to a hand-written adapter.
|
|
119
138
|
|
|
120
139
|
## Validation
|
|
121
140
|
|
package/docs/LOCATIONS.md
CHANGED
package/docs/PRESENTATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Presentation
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Presentation is **semantic UX intent**, expressed as data on a UI
|
|
4
4
|
node. It names roles, tokens and device classes. It never names a colour, a length, a media
|
|
5
5
|
query or a CSS property.
|
|
6
6
|
|
package/docs/RUNTIME.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Semantic contract
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Runtime guarantees, stated formally. This file defines behavior; it
|
|
4
4
|
does not teach. Where this file and any specification in `../specs/` disagree, this file
|
|
5
5
|
describes the implementation and is authoritative.
|
|
6
6
|
|
package/docs/STATE.md
CHANGED
package/docs/TRIGGERS.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Triggers
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. A `TriggerDef` says **when** an action should be invoked, without
|
|
4
4
|
embedding callback code. `docs/AUTHORITY.md`
|
|
5
5
|
[§ Triggers](AUTHORITY.md#triggers) is the load-bearing statement of the execution model;
|
|
6
6
|
this file is the vocabulary.
|
|
@@ -44,6 +44,14 @@ can only be satisfied by a real, authenticated principal is correctly refused wh
|
|
|
44
44
|
trigger targets it; this is not a special case in the authorization check, it is the
|
|
45
45
|
ordinary one applied to a `null` principal, exactly as an anonymous client request gets.
|
|
46
46
|
|
|
47
|
+
`source: 'system'` is also what `invocation.allowedSources` checks (spec 8.1 §3-14, full
|
|
48
|
+
model in [`AUTHORITY.md`](AUTHORITY.md#invocation-source)): an action meant only to be a
|
|
49
|
+
trigger's target — an effect's `succeededEventId`/`failedEventId` handler, a webhook's — can
|
|
50
|
+
declare `invocation: { allowedSources: ['system'] }` so an anonymous client that guessed its
|
|
51
|
+
id cannot invoke it directly. A trigger targeting an action that has opted out of
|
|
52
|
+
`'system'` entirely is rejected at validation (`TRIGGER_TARGET_SOURCE_MISMATCH`), since the
|
|
53
|
+
trigger could then never succeed.
|
|
54
|
+
|
|
47
55
|
## Where a trigger executes
|
|
48
56
|
|
|
49
57
|
Derived from where its target action executes, not declared:
|
|
@@ -60,10 +68,16 @@ triggers targeting a server-authority one, are rejected at validation
|
|
|
60
68
|
(`TRIGGER_WRONG_AUTHORITY`) — the mismatch can never reach a runtime that would silently
|
|
61
69
|
do nothing with it.
|
|
62
70
|
|
|
63
|
-
**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
**A client-authority trigger of a kind the browser cannot execute is a validation error,
|
|
72
|
+
not a silent no-op.** The browser runtime implements no trigger kind at all today
|
|
73
|
+
(`BROWSER_TRIGGER_CAPABILITIES.supportedTriggerKinds` is empty), so `validateGraph`/
|
|
74
|
+
`compileToIR` reject such a trigger with `CLIENT_TRIGGER_UNSUPPORTED` — the same
|
|
75
|
+
capability-gate pattern `RendererCapabilities` already applies to UI node kinds. Before
|
|
76
|
+
spec 8.1, the trigger validated and compiled into `ApplicationIR.triggers` and simply never
|
|
77
|
+
fired, which is exactly the "publicly declared, typechecks, passes validation, has no
|
|
78
|
+
defined runtime behaviour" shape the framework forbids. Compiling for a trigger runtime
|
|
79
|
+
that *does* implement a kind (`compileToIR(graph, { triggerRuntime })`) accepts it. Only the
|
|
80
|
+
authoritative runtime executes triggers today. See [Not in 0.8.0](AUTHORITY.md#not-in-080).
|
|
67
81
|
|
|
68
82
|
## Interval semantics
|
|
69
83
|
|
|
@@ -142,6 +156,23 @@ No test verifying interval/delay behavior needs to wait on a real second (spec
|
|
|
142
156
|
`advance(ms)` fires every host timer that becomes due, re-scheduling intervals, in the
|
|
143
157
|
order they would fire on a real clock.
|
|
144
158
|
|
|
159
|
+
**Every invocation — client request or trigger tick — is serialized against every other one
|
|
160
|
+
this authority runs** (spec 8.1 §26-30), the same FIFO ordering `AxiomServer.handle()`
|
|
161
|
+
already gave client requests. `advance(ms)` firing several same-period triggers in one call
|
|
162
|
+
does not race their commits against each other: each waits its turn, exactly as it would
|
|
163
|
+
under real, staggered timer callbacks. This is why the deterministic host and the real host
|
|
164
|
+
agree on outcome for the same schedule — the ordering guarantee does not depend on which
|
|
165
|
+
host is running. Only the overlap check itself (`inFlight`, above) runs unserialized, since
|
|
166
|
+
it exists specifically to detect *concurrent* ticks of the *same* trigger, which requires
|
|
167
|
+
running immediately when the timer fires.
|
|
168
|
+
|
|
169
|
+
**A hung query cannot wedge a trigger forever.** `integration-query`'s `timeoutMs` is
|
|
170
|
+
enforced by the runtime itself (spec 8.1 §15-25, full model in
|
|
171
|
+
[`INTEGRATIONS.md`](INTEGRATIONS.md#timeout)) — a non-cooperating adapter's promise that
|
|
172
|
+
never settles still causes the invocation, and therefore the tick, to fail within
|
|
173
|
+
`timeoutMs`, clearing `inFlight` so the next scheduled tick runs normally instead of being
|
|
174
|
+
skipped as an overlap forever.
|
|
175
|
+
|
|
145
176
|
## Validation
|
|
146
177
|
|
|
147
178
|
| Code | Raised when |
|
|
@@ -150,6 +181,8 @@ order they would fire on a real clock.
|
|
|
150
181
|
| `TRIGGER_INTERVAL_NOT_POSITIVE` | `everyMs`/`afterMs` is not a positive number. |
|
|
151
182
|
| `UNKNOWN_EVENT` | An `event` trigger's `eventId` does not resolve to an `EventDef`. |
|
|
152
183
|
| `TRIGGER_WRONG_AUTHORITY` | An authority mismatch between the trigger kind and its target action, described above. |
|
|
184
|
+
| `TRIGGER_TARGET_SOURCE_MISMATCH` | The target action's `invocation.allowedSources` excludes `'system'`, so this trigger could never invoke it. |
|
|
185
|
+
| `CLIENT_TRIGGER_UNSUPPORTED` | A client-authority trigger of a kind the named trigger runtime does not execute. |
|
|
153
186
|
|
|
154
187
|
Full table: [`VALIDATION.md`](VALIDATION.md#integrations-effects-triggers-and-events).
|
|
155
188
|
|
|
@@ -159,7 +192,12 @@ Full table: [`VALIDATION.md`](VALIDATION.md#integrations-effects-triggers-and-ev
|
|
|
159
192
|
agent.getTriggersForAction(actionId); // TriggerDef[]
|
|
160
193
|
agent.getTimedTriggers(); // TriggerDef[] — interval and delay only
|
|
161
194
|
agent.getActionsTriggeredByEvent(eventId); // ActionDef[]
|
|
195
|
+
agent.getSystemOnlyActions(); // ActionDef[] — invocation.allowedSources excludes 'client'
|
|
196
|
+
agent.getTriggersTargetingClientOnlyActions(); // TriggerDef[] — could never succeed
|
|
197
|
+
agent.isClientInvocable(actionId); // boolean
|
|
198
|
+
agent.isSystemOnly(actionId); // boolean
|
|
162
199
|
```
|
|
163
200
|
|
|
164
201
|
"What runs automatically" and "what happens every 5 seconds" (spec §78) are answerable
|
|
165
|
-
without reading source
|
|
202
|
+
without reading source, and so is "which actions are reachable only by a trigger, event or
|
|
203
|
+
effect outcome" (spec 8.1 §13).
|
package/docs/UI.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# UI
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Eleven semantic UI node kinds describe **what exists and what it does**.
|
|
4
4
|
How it looks is [presentation](PRESENTATION.md).
|
|
5
5
|
|
|
6
6
|
All eleven share `UIBase`:
|
package/docs/VALIDATION.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Validation
|
|
2
2
|
|
|
3
|
-
Axiom 0.8.
|
|
3
|
+
Axiom 0.8.1-alpha.1. Validation is authoring-time structural checking. It is not the same
|
|
4
4
|
as runtime constraint evaluation — see [`CONSTRAINTS.md`](CONSTRAINTS.md) for the four
|
|
5
5
|
layers of correctness.
|
|
6
6
|
|
|
@@ -130,6 +130,9 @@ Full model: [`INTEGRATIONS.md`](INTEGRATIONS.md), [`EFFECTS.md`](EFFECTS.md),
|
|
|
130
130
|
| `TRIGGER_INTERVAL_NOT_POSITIVE` | An `interval` trigger's `everyMs`, or a `delay` trigger's `afterMs`, that is not a positive number. |
|
|
131
131
|
| `UNKNOWN_EVENT` | An event id that does not resolve to an `EventDef` — a trigger's `eventId`, or an `integration-effect`'s `succeededEventId`/`failedEventId`. |
|
|
132
132
|
| `TRIGGER_WRONG_AUTHORITY` | An `event` trigger targeting a client-authority action (only the server dispatches events), or a `route-enter`/`route-leave` trigger targeting a server-authority one (only the client router dispatches those). |
|
|
133
|
+
| `TRIGGER_TARGET_SOURCE_MISMATCH` | A trigger — which always invokes with `source: 'system'` — targets an action whose `invocation.allowedSources` excludes `'system'`; the trigger could never succeed (spec 8.1 §3-9). |
|
|
134
|
+
| `INVALID_INVOCATION_SOURCE` | `ActionDef.invocation.allowedSources` is present but empty — the action could never be invoked at all. |
|
|
135
|
+
| `CLIENT_TRIGGER_UNSUPPORTED` | A client-authority trigger of a kind the intended trigger runtime does not execute — before 8.1 this validated and compiled anyway, and simply never fired (spec 8.1 §31-36). The browser trigger runtime executes no kind today. |
|
|
133
136
|
|
|
134
137
|
### UI and routing
|
|
135
138
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cynodia/axiom",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1-alpha.1",
|
|
4
4
|
"description": "AI-native semantic web application framework.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "AskTech AS",
|
|
@@ -32,10 +32,10 @@
|
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@cynodia/axiom-core": "0.8.
|
|
36
|
-
"@cynodia/axiom-runtime": "0.8.
|
|
37
|
-
"@cynodia/axiom-compiler": "0.8.
|
|
38
|
-
"@cynodia/axiom-agent-api": "0.8.
|
|
35
|
+
"@cynodia/axiom-core": "0.8.1-alpha.1",
|
|
36
|
+
"@cynodia/axiom-runtime": "0.8.1-alpha.1",
|
|
37
|
+
"@cynodia/axiom-compiler": "0.8.1-alpha.1",
|
|
38
|
+
"@cynodia/axiom-agent-api": "0.8.1-alpha.1"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -b tsconfig.json"
|