Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
8e51657
πŸ”’ Make canonical core authoritative for execution
taras Aug 10, 2026
d215568
πŸ“ Describe capability-backed execution and host orchestration
taras Aug 10, 2026
ba11611
πŸ§ͺ Pin look-alike refusal and completion precedence
taras Aug 10, 2026
2fd652e
πŸ§ͺ Pin first-failure-wins across completion policies
taras Aug 10, 2026
206ae45
πŸ”’ Terminate the Execution chain in a per-invocation private Api
taras Aug 10, 2026
a860df9
πŸ”’ Snapshot accepted options, and make foreign-request coverage discri…
taras Aug 10, 2026
0c2ae02
πŸ”’ Give the invocation an owner task that closes on settlement
taras Aug 10, 2026
e777ff4
πŸ§ͺ Assert EP19 on observable per-invocation results
taras Aug 10, 2026
c7aa595
πŸ“ State the settlement-owned invocation scope
taras Aug 10, 2026
0fe417a
πŸ”’ Rank a document outcome against invocation teardown
taras Aug 11, 2026
4119666
πŸ”’ Rank fatal failures by kind, then by occurrence
taras Aug 11, 2026
67709b1
πŸ§ͺ Cover the full fatal precedence matrix
taras Aug 11, 2026
bda9c9c
πŸ§ͺ Prove returned-handle cancellation directly
taras Aug 11, 2026
a644c5c
πŸ§ͺ Make the options-snapshot regression materially discriminating
taras Aug 11, 2026
68f4242
πŸ“ Complete Β§8.1 lifetime and precedence, and dedupe Tier EP
taras Aug 11, 2026
1143ca2
πŸ§ͺ Make cancellation teardown deterministic and merge the invocation c…
taras Aug 11, 2026
4433a98
πŸ§ͺ Rank a fatal failure raised by cancellation teardown
taras Aug 11, 2026
96780e9
πŸ§ͺ Synchronize cancellation coverage without scheduler timing
taras Aug 11, 2026
2ce038b
πŸ§ͺ Make EP26 discriminate props and component directories
taras Aug 11, 2026
2073649
πŸ”’ Own the cancellation observer per invocation
taras Aug 11, 2026
7ae2eb2
πŸ”’ Capture the invocation observer by value, and order Tier EP
taras Aug 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ Existing documents and code get aligned to this section retroactively.
| checkpoint | a completed journal boundary associated with the logical Workspace root visible after that effect |
| history fork | a new workflow run that replays a compatible journal prefix and continues from its checkpoint and Workspace root under a new immutable document definition |
| loaded copy | one independently evaluated instance of a package, such as the copy bundled into the binary or a separately installed dependency |
| authority | the power to decide what an execution or effect *is* β€” whether it happens, what it may replay from, and what it settles to β€” as distinct from the power to observe or refuse one |
| authoritative behavior | behavior that exercises authority; non-authoritative behavior may inspect, narrow, refuse or add a failure, but cannot bring an execution into being, substitute one, or rescue one |
| trusted host | the code that decides what an execution is for β€” a CLI entrypoint or a workflow runner β€” as distinct from the document, the components it expands, and the middleware packages composed around it |
| `JournalProvenance` | a non-operational, equality-only witness that a live publication stream descends from the exact journal backend a provider selected for one workflow run; it grants no append, read, execution, publication or reconciliation capability, and is meaningful only because the provider retains the witness it established and later requires exact equality |

## Three axes
Expand Down Expand Up @@ -1008,6 +1011,93 @@ hidden inside library objects that accumulate. One exception: metadata an
author declares at module evaluation, about a value the author owns, may live
on that value.

## Authoritative behavior

**Authoritative** behavior decides what an execution *is*: whether it runs at
all, what history it may replay from, what options it runs under, and what it
settles to. **Non-authoritative** behavior observes, narrows, refuses, or adds β€”
it can stop something from happening and can make a success into a failure, but
it cannot bring an execution into being, substitute one, or rescue one.

Public middleware is non-authoritative by construction. `Execution` and
`ReplayGuard` handlers compose lexically, and a handler installed further out
may answer without delegating β€” so anything they could decide would be decided
by registration order. They may refuse; they may not complete.

### Capability-backed execution

Canonical core is authoritative for document execution. It invokes the stable
`Execution` middleware through a private, per-invocation same-name Api whose
instance-owned default handler is the authoritative terminal. A stable name
shares the middleware context, so every public handler β€” including one installed
through another loaded copy's descriptor β€” composes exactly as it always did;
what a name does not share is the default handler, and that is where authority
sits. The exported `Execution.execute` default always refuses, so calling it
with a captured request settles nothing.

`Execution.execute` middleware is handed an opaque `ExecutionRequest` and
returns nothing: it may
inspect the options, narrow them, register an additive completion policy,
install contextual behavior, refuse by throwing, and delegate. The document is
run afterwards, by the invocation that issued the request, under the options the
canonical terminal recorded.

Terminal acceptance stores a detached, immutable structural snapshot of the
options: the containers are copied and frozen while the operational identities
inside them β€” the selected `DurableStream`, each modifier factory β€” are carried
across unchanged. The chain unwinds before the document runs, so an outer
handler that delegates and then edits what it delegated changes only its own
data. Replacing options before delegating, through `withOptions()`, remains the
supported path.

Each invocation owns a child scope, held by one structured owner task, and
**settlement closes it** β€” on success, on failure, and on cancellation alike.
Contextual behavior an installation establishes is visible to the document and
to its teardown, isolated from a concurrent invocation, and absent from the next
execution in the same host scope. The final `Result` is published only after
that scope has finished tearing down, so a caller continuing on the completion
continues after cleanup, and a completed handle carries no live scope.

A document outcome and an invocation-teardown failure are kept apart until the
scope has closed and then ranked, never replaced: a durability failure wins from
wherever it came and is returned by identity, then a Files infrastructure
failure on the same terms, then an existing document failure, and only a success
is converted by teardown. Every finalizer runs, exactly once, before the result
is observable. Once a handle exists, teardown contributes to its `Result` rather
than escaping as a thrown completion. Canonical
core constructs the one authoritative handle; it exposes the inner execution's
replay-safe output directly rather than bridging it through a second channel.

The request is the capability, and it is **one-use**. It carries a private
reference to one invocation; a reconstructed look-alike, a superseded request, a
foreign invocation's request, and a second delegation are each refused with a
fresh, cause-free `ExecutionProtocolError`, before any journal read, expansion
or append. Reaching the end of the chain without consuming the request is the
same refusal.

### Trusted host orchestration

A **trusted host** is the code that decides what an execution is for β€” a CLI
entrypoint, a workflow runner β€” as distinct from the document, the components it
expands, and the middleware packages composed around it.

A host attaches requirements to one execution through
`@executablemd/core/host`: `executeInstalled(options, installations)`, where an
installation carries `admissions` and an optional `install()`. Admissions are
copied and frozen **before** any installation runs, so what ends up
authoritative is fixed before any installed code, any middleware and any
document code exists. Each runs inside the execution's own journal read, on the
retained snapshot, in capture order, stopping at the first refusal β€” ahead of
root-history admission, `ReplayGuard`, terminal reuse, authored work and any
append.

Admissions are refusal-only functions: they receive the retained history, return
nothing, and never receive a `next`. They travel as values the host holds and
passes β€” not through a context, an Api, a stable name, structural metadata or
module-scoped state β€” which is why a separately loaded package composes here by
handing over a closure rather than by agreeing on a name, and why no middleware
can read, transport or remove the collection.

### The weak journal-provenance association

Journal provenance is the one further exception, and it is deliberately narrow.
Expand Down
3 changes: 2 additions & 1 deletion packages/core/deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
"name": "@executablemd/core",
"version": "0.8.0",
"exports": {
".": "./mod.ts"
".": "./mod.ts",
"./host": "./host.ts"
},
"imports": {
"@effectionx/context-api": "npm:@effectionx/context-api@0.6.0",
Expand Down
28 changes: 28 additions & 0 deletions packages/core/host.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @module
*
* The infrastructure boundary of document execution.
*
* Attaching an admission to an execution is a host act, not an authoring one:
* it decides what a retained history must satisfy before the document is
* allowed to replay from it. Keeping it behind its own entrypoint is what makes
* that visible at the import β€” nothing a document, a component or a middleware
* package reaches by importing `@executablemd/core` can require anything of a
* journal.
*
* The value crosses as a plain function the host holds and passes:
*
* ```ts
* import { executeInstalled } from "@executablemd/core/host";
*
* const execution = yield* executeInstalled(options, [installation]);
* ```
*
* That is also why a separately loaded package composes here. It hands the host
* a closure and the host hands it to canonical core; neither of them agrees on
* a name, looks anything up, or shares a registry, so there is nothing for a
* second copy to disagree about and nothing for anyone else to reach.
*/

export { executeInstalled } from "./src/execute.ts";
export type { ExecutionInstallation, JournalAdmission } from "./src/execute.ts";
4 changes: 4 additions & 0 deletions packages/core/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,10 @@ export type {
ExecutionApi,
DocumentExecution,
} from "./src/execute.ts";
// The narrowing surface a middleware handler needs, and nothing that completes
// an execution: `executeInstalled` and `JournalAdmission` are host boundary.
export { ExecutionProtocolError } from "./src/execution-request.ts";
export type { CompletionFailure, ExecutionRequest } from "./src/execution-request.ts";
export {
fileSource,
formatDocumentReference,
Expand Down
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"description": "Core engine that evaluates executable.md documents.",
"type": "module",
"exports": {
".": "./mod.ts"
".": "./mod.ts",
"./host": "./host.ts"
},
"dependencies": {
"@effectionx/context-api": "0.6.0",
Expand Down
Loading
Loading