Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
108 changes: 108 additions & 0 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Existing documents and code get aligned to this section retroactively.
| document execution | one evaluation of a root document initiated through `execute()`, producing one output stream and one completion result while reading and appending a durable journal; its ongoing effects belong to the Effection scope in which the evaluation runs |
| workflow definition | what a workflow run is a run of: a versioned descriptor naming an immutable object — its format and object ID — together with the repository-relative path of the root document inside it, the exact canonical document target when one is selected, and the component bundle it is closed over when it declares one. A repository locator is not part of it, and it is distinct from every Repository created inside the run's Workspace |
| workflow component bundle | the closed set of authored Markdown components a workflow root declares, each named, located by a canonical repository-relative path inside the pinned commit, and identified by that blob's object ID. It is both definition identity and execution authority: the names it declares resolve to its exact pinned sources and to nothing else |
| declared Markdown component | exact first-party Markdown a trusted host declares to one document execution, read once and held by the invocation — schemas copied, not referenced — before any installation runs, as immutable data on an `ExecutionInstallation`: the public name, the reported origin, the bytes, the SHA-256 of those bytes, the accepted forms, the contract parsed from them, and any private component closure only those bytes may write. The host claims the name rather than offering a default for it, so it resolves ahead of a repository file, a workflow component bundle and every registration; a second claim on one name is a configuration failure. It is a composition mechanism and not a policy loader: no caller-facing option selects one, adds one, or names its source |
| private component closure | the components one declared Markdown component carries that only its own bytes may write. Each is minted like any other invocation-identity component and registered nowhere, so it resolves while canonical core is expanding that declaration's body and nowhere else — not from the caller's root, the content the caller projected through it, a sibling declaration, an imported component, middleware, or an implementation kept past teardown. Eligibility is the authored occurrence rather than the name: what an import may invoke is what canonical core produced inside that exact ask; an implementation the closure built is refused for every other name, copy, later site and later execution, because a run that has ended authorizes nothing; and a private name written anywhere else resolves to nothing before any other tier can answer. It is lexical availability, not authority: every private component still takes its operation from the invocation that carries it |
| retrieval metadata | replaceable, credential-free information about where a workflow definition can be fetched from now; it takes no part in run identity and is reauthorized by the host before use |
| stop reason | why a workflow run or a document execution stopped: a categorical host code, or a reference to an already-filtered journal event |
| run ID | an opaque stable public identifier generated by the host or selected by an authorized caller; it associates the run's durable records and effects, remains unchanged for the life of the run, and has no semantics beyond equality and lifecycle addressing |
Expand Down Expand Up @@ -3373,6 +3375,86 @@ durable is written.
`undefined`, and a replay reconstructs the same validated prop set through
ordinary expansion.

## The declared Markdown boundary

A trusted host may ship first-party Markdown, name it, and hand it to one
execution. Everything about it is decided before the root document is imported.

**The declaration is held to its own bytes.** The host states the name, the
origin, the source and its SHA-256, and may state the props schema, the return
and the accepted forms. Canonical core parses that source and refuses the
declaration when the digest is not the digest of those bytes, when a stated
schema is not the schema they declare, when the forms are not canonical, when
the name is not a component name or is structural syntax, when one name is
declared twice, or when a reserved registration already claims it. A build that
ships different bytes under a declared name therefore fails where it is
installed rather than where a document happens to write the name.

**The name is claimed, not offered.** Resolution places a declared component in
the same protected tier as a reserved registration, above the workflow component
bundle, repository files and every registered default. Two claims on one name
are refused rather than ordered, so the tier never chooses. A repository
`Policy.md`, a bundled `Policy`, an ordinary registration and another loaded
copy of the host package can none of them answer for a declared `Policy`.

**Invocation is canonical, for the declared names.** A declaring execution
imports through the same retention a bundled one does: canonical core keeps its
own copy of the definition it produced and invokes that copy, then compares the
answer that came back against it by own property descriptor.
`Component.importComponent` middleware may observe a declared import, delegate
it and refuse it by throwing; it cannot answer one, replace one, or change one
before it runs.

That closure is a property of the name. A workflow component bundle closes every
import its run makes, because a run is a run of one pinned tree. A declaration
closes only what it declares — the component and its private closure — and every
other name in the execution stays the ordinary open import it has always been.
Declaring one asset does not take component substitution away from every
document the host runs.

**Every declaration is read once, before any installation runs.** Name, origin,
source, digest, forms, prose and each private declaration are captured by the
invocation and held by it — factory bound, arrays and schemas copied — on the
same terms as the admissions and the bundle. A schema is copied rather than
referenced because it is a whole object graph, and holding the caller's object
would leave the contract mutable after capture. A host that hands a declaration
over and then replaces a member of it, or mutates a schema it still holds, has
replaced nothing.

**The private closure is lexical and expansion-scoped.** A declaration's private
components are minted exactly like the identity components a host declares —
one domain, one claimant, revoked when the execution is torn down — and then
registered nowhere. Canonical core offers one for the duration of one import,
from the closure the segments being expanded carry, and the resolver takes it
once. Content the caller projected expands through the invocation's projection
handle, which restores the caller's frame and the caller's closure, so a
`<Content />` inside a declaration reaches no private name.

Eligibility belongs to the authored occurrence rather than to the name. What a
private import may invoke is the object canonical core produced inside that ask,
so an answer a handler retained from a legitimate private import authorizes
nothing at a later site — and because neither the name nor the current execution
is what is checked, it authorizes nothing under an alias, inside a copy of the
definition, after the invocation that produced it is over, or in a later
execution that declares no Markdown at all. A run that has ended authorizes
nothing. What is restricted is the implementation the closure built, recognized
wherever an answer would become something the engine invokes; a definition a
handler wrote carries a different function and stays the open import it always
was. And a private name written where no offer was made
resolves to nothing before the workflow bundle, the repository or any
registration can answer for it — one decision, shared by execution, inspection
and validation, so a repository file under a private name is described by none
of them and runs nowhere.

**The journal records the asset, not a lookup.** A declared import records
exactly `{ kind: "declared-markdown", origin, digest, content }`, and a private
one records exactly `{ kind: "declared-private", origin }`. A continuation reads
both as hostile data, verifies the recorded origin, digest and bytes against
what this run declares, and reads no file: a host that no longer declares the
name, or that declares different bytes under it, refuses rather than continuing
somebody else's policy. A recorded private import refuses unless the element
asking for it is inside the same declaration.

## The syntax catalog boundary

`xmd syntax` answers what a document may write here, and answering must cost
Expand All @@ -3397,6 +3479,14 @@ reads it, so a set admissible to one is admissible to the other. Describing an
environment no document could run in would be describing a run that cannot
happen.

**A declared Markdown component is described from the same declaration.** The
exact Markdown a host would declare to an execution is admitted here on
execution's terms too, and each declared name contributes one complete entry
under built-in, reporting `declared-markdown` as its source kind and the
declared origin and digest as its origin. Its private closure contributes
nothing: those names are not syntax a document may write, so a catalog that
listed them would describe an environment that does not exist.

**Inspection is observation, never authority.** Producing a catalog installs
only the declarative registration layer selection needs. It does not enter
`execute()`, construct a durable stream, install a Files, Service, Agent or
Expand Down Expand Up @@ -3498,6 +3588,23 @@ only when every applicable check is statically proven, invalid when any such
check proves a failure, and otherwise not statically checkable. Opacity alone
does not make the document invalid.

**A declared Markdown component is checked as the contract it declares.** The
declarations syntax inspection reads are read here too, admitted the same way,
and an invocation of one is held to its parsed props, forms and return mode
exactly as a registration's invocation is. Its body is not walked: those bytes
are the host's, an author cannot change them, and the names only they may write
resolve nowhere else — so reporting on them would report a document's author for
the engine's own asset.

**A structural answer is the same walk without the run's values.** One
operation validates a document against the props a run would supply; the other
validates the source alone and skips only the root's own props check. Everything
else — the source walk, component selection, forms, body shape, bindings,
captures, returns and the diagnostic catalog — is one traversal and one rule set,
so a root that declares required props is structurally valid and invalid to a
run that supplies none, and the two answers cannot come to disagree about
anything else.

**Parsing stays one language rule.** Validation uses the scanner and definition
parsers execution uses. Component-like text that scanner treats as text remains
text, and spread attributes retain their execution meaning. A stricter MDX
Expand Down Expand Up @@ -3559,6 +3666,7 @@ Status is measured against main.
| document-aware `xmd run … --help` | describes what one document declares and every target it addresses, each as a full document reference with the description its section states, by inspection alone | built on the #463 stack |
| targeted `xmd run` | reads a file argument as a document reference and executes the one exact target its selector resolved to, replacing the selector before execution rereads the file | built on the #412 stack |
| targeted workflow definition | the V1 workflow definition optionally carries the exact canonical document target, which takes part in definition identity and in compatible reuse | built on the #412 stack; the workflow CLI does not supply one yet |
| declared Markdown component | a trusted host declares exact first-party Markdown to one execution as immutable data on an `ExecutionInstallation`: name, origin, source, its SHA-256, the accepted forms, an optional statement of the props and return that must agree with the parsed source, and an optional private component closure. Admission parses the bytes and refuses a mismatched digest or schema, a non-canonical form, a name that is not a component name or is structural, a duplicate, a reserved-registration collision and a private name a registration also claims. Resolution places it in the protected tier with reserved registrations, above the workflow component bundle, repository files and every registered default. Live import and retained history are held to the declared origin, digest and bytes, private names resolve only while canonical core expands the declaring bytes' own body — by the authored occurrence rather than by the name, so an answer kept from a legitimate private import authorizes no later site, no alias, no copy of the definition, no invocation that is over and no later execution — including one that declares no Markdown at all — while a private name written anywhere else resolves to nothing before the bundle, the repository or a registration can answer for it — and `xmd syntax` and document validation describe the declared contract from the same declaration without describing the closure. Closure is per name: only the declared component and its private closure become canonical imports, and every other name in the execution stays the ordinary open import middleware may still answer | built on the #660 stack; no public component uses it yet (#660 PR 2) |
| workflow component bundle | a workflow root declares a closed set of authored Markdown components; the V1 workflow definition optionally carries them as one array sorted by component name, each entry holding the name, its canonical repository-relative path inside the pinned commit and that blob's object ID, and an absent member identifies a run closed over no components — so a definition retained before the member existed reads unchanged. `start` and `resume` read every component from the definition's own pinned commit; the array takes part in definition identity and is compared as part of the same V1 descriptor in compatible reuse; and canonical core resolves those names and holds both live import and retained history to that exact bundle | built on the #301 stack; the full adversarial implementation loop and its scheduling remain unbuilt (#300), and generated XMD admits no bundled Markdown component (#369) |
| `workflowInstallation()` / `getWorkflowRun()` | associates one document execution with a workflow run, through an `ExecutionInstallation` the trusted host passes to `executeInstalled()` | built on the #366 stack |
| `retainedWorkflowInstallation()` | associates one document execution with a run storage already created, requiring exact journal agreement | built on the #366 stack |
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/syntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,9 @@ function describeOrigin(origin: ComponentOrigin): string {
if (origin.kind === "registered") {
return `${code(origin.origin)} (${origin.reserved ? "reserved registration" : "registered default"})`;
}
if (origin.kind === "declared-markdown") {
return `${code(origin.origin)} (declared Markdown)`;
}
return `structural syntax (${code(origin.construct)})`;
}

Expand Down
14 changes: 14 additions & 0 deletions packages/core/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,20 @@ export type { IdentityClaimant, IdentityComponent } from "./src/invocation-ident
export { props as elicitProps, returns as elicitReturns } from "./src/components/Elicit.ts";
export { WorkflowBundleError } from "./src/components/bundle.ts";
export type { WorkflowBundleComponent, WorkflowComponentBundle } from "./src/components/bundle.ts";

/**
* Exact Markdown a trusted host declares to an execution — see
* `src/components/declared-markdown.ts`.
*
* The fifth act of infrastructure, and the same shape as the rest: plain
* immutable data the host holds and passes. The host states the bytes, their
* origin and their digest, and canonical core refuses the declaration if what
* the host said about them is not what they say about themselves. `sourceDigest`
* is the same hash core checks against, so a build states the digest of what it
* actually shipped rather than a constant someone updates by hand.
*/
export { DeclaredMarkdownError, sourceDigest } from "./src/components/declared-markdown.ts";
export type { DeclaredMarkdownComponent } from "./src/components/declared-markdown.ts";
export {
evaluateGeneratedXmd,
GeneratedXmdError,
Expand Down
6 changes: 5 additions & 1 deletion packages/core/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,11 @@ export type {
export { ComponentIncludeError } from "./src/components/candidates.ts";
// Document validation — one supplied document read as authored program
// structure, with nothing in it executed.
export { documentValidationCodeRank, validateDocument } from "./src/document-validation.ts";
export {
documentValidationCodeRank,
validateDocument,
validateDocumentStructure,
} from "./src/document-validation.ts";
export type {
DocumentValidation,
DocumentValidationCode,
Expand Down
Loading
Loading