Skip to content

Replace host evaluation profiles with Component.generated (#181) - #920

Merged
taras merged 4 commits into
mainfrom
agent/upstream-u5-generated-selection
Oct 16, 2026
Merged

taras merged 4 commits into
mainfrom
agent/upstream-u5-generated-selection

Conversation

@taras

@taras taras commented Oct 15, 2026 •

Copy link
Copy Markdown
Owner

Why

Hosts currently configure generated evaluation by assembling read/write contract tables, duplicating each component's forms, props and identity. That also mixes the generated vocabulary with live request policy and Workspace state.

This PR replaces that input with Component.generated: a contextual selection of component names and authored forms. Core retains the selected implementations before execution policy runs; live Files and Fetch operations use the providers and permissions of the scope performing them.

This is U5, the final upstream prerequisite for #181, following #905, #906, #912 and #919. #902's consumer integration follows this merge and removes its existing forwarding.

What changes

Before: hosts supplied ExecutionInstallation.evaluation, with separate read/write tables. Duplicate profiles refused, some names bypassed host replacements, and generated admission retained Workspace and request inventories.

After: hosts compose one name/form selection. For example, middleware installed in the execution's setup scope can offer File reads:

yield* Component.around({
  generated: () => ({ File: ["self-closing"] }),
});

Core resolves File once and derives its props, implementation, read effect and identity URL from the owning definition. A paired File remains unadmitted.

  • undefined means no generated evaluation. Defined empty or malformed selections refuse setup before policy, root import or a paired producer.
  • Ordinary host defaults delegate first and apply only on undefined, including each REPL entry. Custom selections are preserved without automatic union.
  • Replaceable names such as Glob, Fetch and Elicit resolve to the actual host answer. A selected replacement without the required generated metadata refuses setup.
  • Fetch destinations, methods and complete headers are checked after middleware transformation, immediately before transport. Timeout comes from contextual Config. Dynamic destinations require no static request inventory.
  • Generated records use marker 4, retaining selected classes and name/form/identity/effect rows without a Workspace basis or request inventory.

How it works

Execution.init and captured installation callbacks
  -> assemble canonical component resolution
  -> read and validate Component.generated once
  -> resolve each selected name once; retain definition and metadata together
  -> public Execution.execute policy
  -> authored document and generated execution
  -> joined teardown

Authored imports still use the detached options accepted by execution policy, including supported search-path replacements. Those changes cannot widen the already captured generated selection.

A bounded Workflow host derives both implicit Fetch membership and its live Permissions restriction from the same captured request list.

Review guide

  1. packages/core/src/generated-selection.ts and component-api.ts: the value property, whole-selection validation and owned capture.
  2. packages/core/src/execute.ts: assembly before policy, canonical publication at the point that produces the answer, and authored resolution on settled options.
  3. packages/core/src/evaluation-profile.ts and generated-xmd.ts: retained definitions, lifetime guards, safe request-refusal classification for Plan recovery, and marker-4 reconciliation.
  4. CLI host setup and packages/workflow/src/deno/workspace/evaluate.ts: undefined-only fallback, explicit Workflow vocabulary, and live request bounds.

What must stay true

  • One validated reading supplies the retained allowance. Selection and metadata capture keep owned data; Workflow's Fetch selection and policy use the same request-list capture. Covered by U5-1–U5-3 and WGX15.
  • Policy cannot substitute a generated implementation. Canonical publication precedes outer middleware, and retained answers keep their dispatcher and execution lifetime. Covered by answer-identity, provider-lifetime and loaded-copy tests.
  • Completed local effects restore without live work. Permissions checks remain inside live performers; replay does not repeat I/O or evaluator inventory. Covered by generated replay and Workflow journal tests.

How to verify it

The focused regressions prove distinct boundaries:

  • EP26 and the U5-2 search-path row preserve accepted authored options while preventing generated widening.
  • Real REPL entries preserve narrower custom selections and refuse defined empty ones.
  • WGX13 uses the production Workflow policy and real HTTP transport: unchanged headers succeed; a case-colliding header transformation sends nothing. WGX9 mutates a bound while its policy remains live.
  • WGX15 supplies an accessor-backed request list: its first empty reading offers no Fetch, is read once, and sends nothing.
  • Generated record tests restore current admissions and refuse old or malformed shapes.

At reviewed head 86c10507: the affected Workflow group passed 30 tests/224 steps; workflow-fetch and workflow-agent passed 2/19; execution-protocol, generated-selection and REPL passed 13/127. The Workflow regression suite passed 3/15, and check/lint passed. The Planner independently reran that suite and the retained request-capture probe.

Earlier focused baseline 2f105ed7: all twelve frozen commands passed, including Node and Bun (257 tests each), documentation, dead-code, build and compiled/npm-bin journeys (7/21). The final two-file Workflow correction reran its affected evidence; this does not claim every baseline command ran again at the final head. Delivery verification remains separate.

Scope

Included

  • Contextual generated selection across ordinary runs, REPL entries, isolated run children, Plan writers and Workflow hosts.
  • Removal of the old evaluation installation input, host table builders and evaluator-only Workspace/request plumbing.
  • Current record readers/writers, migrated fixtures, specifications and architecture documentation.

Intentionally unchanged

  • Fixed filesystem components, protected Evaluate/Syntax, and always-on Json composition.
  • Workflow storage, root associations, transactions, restoration and forks.
  • Ordinary default vocabulary: File, File.Delete, Glob, Syntax and Elicit. Dir and Fetch remain absent from that default.

New abstractions

  • generated-selection.ts validates the host's JavaScript-facing value before resolution.

  • useGeneratedFallback() shares undefined-only composition across ordinary host setup and REPL entries.

  • The test selection helper supplies the same contextual property from either a surrounding scope or an installation callback.

  • Each new abstraction has concrete consumers or a required boundary.

  • No speculative functionality is included.

Risks and limitations

  • Old generated admissions cannot resume on this build. Marker-3 and earlier records refuse as unreadable history. Stored journals are neither rewritten nor migrated.
  • Host API migration is required. ExecutionInstallation.evaluation, FragmentEvaluationInput and the old public entry/evaluation builders are removed. In-repository hosts migrate in this PR.
  • Selecting a replaceable name now validates the actual host answer at setup, even if the fragment never uses it. A replacement lacking the selected generated form refuses instead of falling back to core's body.
  • A live runtime refusal can follow an earlier committed effect; whole-fragment language validation does not provide rollback of completed effects.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Fixture migrations and intentional behavior changes are identified.
  • The description distinguishes final-head evidence from the earlier baseline.

A trusted host used to state what a generated fragment may do as a whole
`ExecutionInstallation.evaluation` profile: two contract tables of pinned
definitions, a Workspace basis read per invocation, a frozen Fetch timeout and
an exact request inventory. Most of that restated facts the components
themselves already owned, and the parts that did not — the retained roots and
the request allowlist — were live state a continuation was then compared
against.

A host now offers one thing: `Component.generated`, an ordinary contextual value
property mapping component name to the authored forms it offers. Core reads that
answer once, after initialization and the captured installation callbacks and
before public `Execution.execute` policy, validates it whole, resolves each
selected name once through the ordinary import chain, and retains the
implementation with its declaration's metadata. Which identity a spelling runs
under, which class it reaches, what its props are validated against and what
documents it all come from the declaration that owns the implementation.

Before:

- a host wrote `evaluation: { read: [fileReadEntry(), …], write: […] }`, and two
  installations stating one refused;
- selecting `Glob`, `Fetch` or `Elicit` ran a body pinned past whatever the host
  had legitimately registered for that name;
- `<Fetch>` carried an exact request inventory that preflight classified the
  element against, and the admission retained it;
- the admission retained the run's Workspace roots and compared a continuation
  against them.

After:

- a host composes `Component.generated` middleware; two handlers compose, and an
  empty or malformed selection refuses setup rather than being defaulted;
- every selected name resolves to the actual host answer, and a replacement
  offering no generated use of the selected spelling refuses setup;
- a destination bound is a live `Permissions.fetch` restriction in the owner
  that performs the request, asked of the transformed request immediately before
  transport, so a dynamic destination is an ordinary input to preflight. The
  attachment reads what the host stated once and derives both halves from that
  copy, so the reading that offers `<Fetch>` is the reading whose bound was
  installed. The
  bound is the complete request — destination, method and every header as it
  will be sent — and a request or a bound that names one header twice is refused
  rather than normalized, because a transport joins differently cased
  occurrences into one sent header that neither of them stated;
- the `generated_xmd` record is marker 4: the selected rows and the class
  selection, with no Workspace basis and no request inventory. Marker-3 records
  are refused as unreadable history.

How it works:

    host middleware answers Component.generated
      -> invoke(): init, installation callbacks, documentation
      -> assembleComponents(): registry, bundle, declarations, protected tier,
         identity domains
      -> admitGeneratedSelection(): one read, validated whole
      -> resolveSelectedComponents(): one import per name, published where the
         answer was produced
      -> captureEvaluationProfile(): read/write tables derived from each owner's
         generated metadata
      -> public Execution.execute policy
      -> executeDocument() reuses that same assembly

Canonical component assembly moved out of `executeDocument()` into the
invocation owner so the selection can resolve against the host environment
before policy runs; authored execution reuses the result rather than building a
second one. What that assembly does not decide is where the document's own
imports resolve from: those still go through the options the canonical request
settled to, so a policy's legitimate `request.withOptions()` search-path
replacement reaches authored imports and this execution's own description, while
the capture keeps the paths it resolved against.

Removed: `FragmentEvaluationInput`, `ExecutionInstallation.evaluation`,
`statesEvaluation`, `TWO_PROFILES`, the entry constructors,
`src/fragment-capabilities.ts`, `workspaceRootSelection`, the generated
`fetchTimeout` capture, and the `generated-observations.ts` wrapper whose only
consumer was its own tests. The refusal normalization that module also owned —
an admitted component's ordinary `Err` becoming a marked generated-request
refusal with a safe reason — moved into the capture guard.

Hosts migrated together: ordinary CLI run, REPL entries (the selection is data
on the profile, offered in each entry's own execution scope as the ordinary
fallback, so a plugin's or a host installation's own selection is the one that
entry runs under), isolated `host="run"` children, the Plan writer, and the
Workflow attachment, which offers its selection inside `host.attach` and states
its optional request bound beside it. Every ordinary host's default goes through
one helper that delegates first and answers only on `undefined`.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 5 redundant comments. Inline suggestions to remove them below.

// The owner's own dispatcher, when its answer has one. The definition above
// runs behind core's lifetime guard, so what a selection would read off it
// is core's function rather than the owner's — and a form dispatcher read
// off the wrong function selects no body at all.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// off the wrong function selects no body at all.

// nothing about it would tell an agent a name and no more.
// The owning declaration's own prose. A host selects a name and says
// nothing about it, so what describes an admitted entry is what describes
// the component everywhere else.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// the component everywhere else.

"File.Delete": ["self-closing"],
// Offered only where this host bounds what a request may reach. A name
// whose every request the live terminal would refuse is a name a fragment
// could write and never successfully call.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// could write and never successfully call.

const components = options.components;
// `bounds`, not the options: the name is offered exactly where a request
// policy was installed, so a reading that stated no bound cannot be followed
// by one that offers `<Fetch>` under no policy at all.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// by one that offers `<Fetch>` under no policy at all.

// Ambiguity is refused, never resolved. This bound admits a request whose
// headers it can read exactly, and two spellings of one name are not one
// header anybody stated — so there is nothing here to compare and the
// request is not this bound's.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// request is not this bound's.

@github-actions

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

PR #920: Replace host evaluation profiles with Component.generated (#181)

71 files, +4779 / -4893

Scope

🔴 PR has 9672 lines changed. Split into focused PRs.

🟡 9672 lines changed. PRs under 400 receive more thorough review.

🟡 71 files changed. Are all changes related?

🟡 Changes span 8 directories.

Structural

Oxlint structural signals:

  • no-unused-vars ×23: packages/core/src/components/Evaluate.ts, packages/cli/src/plan-writer-profile.ts, packages/cli/src/repl/program.ts (+6)
  • no-empty-function ×5: packages/cli/src/repl-profile.ts, packages/cli/src/repl/expansion.ts, packages/core/src/execute.ts (+2)
  • no-unnecessary-type-assertion ×5: packages/core/src/execute.ts
  • no-unnecessary-type-arguments ×3: packages/cli/src/repl/session.ts, packages/cli/src/repl/expansion.ts

Slop

  • packages/core/src/execute.ts:864 (removed)

Oxlint slop signals:

  • no-console ×3: packages/cli/src/cli.ts

Static Analysis

Oxlint: 110 diagnostics across 20 files (19 rules)
Density: 0.023 violations/added-line

no-unused-vars (23): packages/core/src/components/Evaluate.ts, packages/cli/src/plan-writer-profile.ts, packages/cli/src/repl/program.ts (+6)
no-shadow (22): packages/core/src/components/Fetch.ts, packages/core/src/components/Elicit.ts, packages/cli/src/repl/program.ts (+4)
no-floating-promises (12): packages/cli/src/repl/session.ts, packages/cli/src/repl/program.ts, packages/cli/src/cli.ts (+1)
unbound-method (11): packages/workflow/src/deno/workspace/effect.ts, packages/cli/src/cli.ts, packages/cli/src/testing-host.ts (+1)
no-unsafe-type-assertion (6): packages/cli/src/cli.ts, packages/core/src/execute.ts
no-empty-function (5): packages/cli/src/repl-profile.ts, packages/cli/src/repl/expansion.ts, packages/core/src/execute.ts (+2)
consistent-return (5): packages/cli/src/repl/program.ts, packages/core/src/execute.ts, packages/cli/src/cli.ts
no-unnecessary-type-assertion (5): packages/core/src/execute.ts
no-useless-spread (3): packages/cli/src/workflow-fork.ts, packages/cli/src/workflow.ts, packages/cli/src/cli.ts
no-console (3): packages/cli/src/cli.ts
no-unnecessary-type-arguments (3): packages/cli/src/repl/session.ts, packages/cli/src/repl/expansion.ts
no-base-to-string (3): packages/cli/tests/fixtures/repl/reference.ts, packages/core/src/evaluation-profile.ts
no-array-sort (2): packages/core/src/generated-selection.ts, packages/core/src/execute.ts
consistent-function-scoping (2): packages/cli/src/plan-writer-profile.ts, packages/cli/src/repl/expansion.ts
no-useless-fallback-in-spread (1): packages/workflow/src/deno/workspace/evaluate.ts
no-empty-pattern (1): packages/cli/src/repl/expansion.ts
no-unmodified-loop-condition (1): packages/cli/src/cli.ts
no-unnecessary-template-expression (1): packages/cli/src/workflow.ts
no-duplicate-type-constituents (1): packages/core/src/execute.ts

Correctness

FILE: packages/core/src/components/Evaluate.ts
PATTERN: no-unused-vars
CONCERN: Unused variable execution in useOrdinaryGeneratedSelection
QUESTION: Is execution required for future expansion or can it be removed?

FILE: packages/cli/src/plan-writer-profile.ts
PATTERN: no-unused-vars
CONCERN: Unused import useOrdinaryGeneratedSelection
QUESTION: Why is this import retained if not used in the file?

FILE: packages/cli/src/repl/program.ts
PATTERN: no-unused-vars
CONCERN: Unused variable execution in useOrdinaryGeneratedSelection
QUESTION: Is execution required for future expansion or can it be removed?

FILE: packages/core/src/components/Fetch.ts
PATTERN: no-shadow
CONCERN: Shadowing generated parameter in useGeneratedFallback
QUESTION: Is the shadowing intentional or a redundant declaration?

FILE: packages/core/src/components/Elicit.ts
PATTERN: no-shadow
CONCERN: Shadowing generated parameter in useGeneratedFallback
QUESTION: Is the shadowing intentional or a redundant declaration?

FILE: packages/cli/src/repl/program.ts
PATTERN: no-shadow
CONCERN: Shadowing generated parameter in useGeneratedFallback
QUESTION: Is the shadowing intentional or a redundant declaration?

FILE: packages/core/src/execute.ts
PATTERN: no-empty-function
CONCERN: Empty function useNoGeneratedEvaluation
QUESTION: Is this function necessary or can it be removed?

FILE: packages/core/src/execute.ts
PATTERN: no-unsafe-type-assertion
CONCERN: Unsafe type assertion in useGeneratedFallback
QUESTION: Can this be refactored to avoid the assertion?

FILE: packages/core/src/evaluation-profile.ts
PATTERN: no-unused-vars
CONCERN: Unused ordinaryGeneratedSelection export
QUESTION: Why is this export retained if not used in the file?

FILE: packages/core/src/generated-selection.ts
PATTERN: no-array-sort
CONCERN: Unnecessary array sort in validateGeneratedSelection
QUESTION: Is the sort required for correctness or can it be removed?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-assertion
CONCERN: Redundant type assertion in useGeneratedFallback
QUESTION: Can this be simplified to avoid the assertion?

FILE: packages/core/src/execute.ts
PATTERN: no-duplicate-type-constituents
CONCERN: Duplicate type constituents in useGeneratedFallback
QUESTION: Are these duplicates intentional or a code smell?

FILE: packages/cli/src/repl/expansion.ts
PATTERN: no-empty-pattern
CONCERN: Empty destructuring pattern in useOrdinaryGeneratedSelection
QUESTION: Is this pattern necessary or a leftover from prior code?

FILE: packages/cli/src/cli.ts
PATTERN: no-console
CONCERN: Debug console.log in useNoGeneratedEvaluation
QUESTION: Should this be removed or replaced with a logger?

FILE: packages/core/src/evaluation-profile.ts
PATTERN: no-unused-vars
CONCERN: Unused useOrdinaryGeneratedSelection import
QUESTION: Why is this import retained if not used in the file?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-arguments
CONCERN: Redundant type arguments in useGeneratedFallback
QUESTION: Can these be omitted to simplify the code?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-assertion
CONCERN: Redundant type assertion in useGeneratedFallback
QUESTION: Can this be simplified to avoid the assertion?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-arguments
CONCERN: Redundant type arguments in useGeneratedFallback
QUESTION: Can these be omitted to simplify the code?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-assertion
CONCERN: Redundant type assertion in useGeneratedFallback
QUESTION: Can this be simplified to avoid the assertion?

FILE: packages/core/src/execute.ts
PATTERN: no-unnecessary-type-arguments
CONCERN: Redundant type arguments in useGeneratedFallback
QUESTION: Can these be omitted to simplify the code?

@taras taras changed the title 🔒 Offer generated components as one contextual selection (#181) Replace host evaluation profiles with Component.generated (#181) Oct 15, 2026
taras added 2 commits October 15, 2026 16:30
…ult (#181)

An `xmd workflow` execution that this host offers no generated selection — a
completed replay, and both passes of a fork's compatibility preflight — was
executed under the ordinary `xmd run` default instead. That default is
contextual middleware installed in the scope enclosing every command, and it
answers wherever nothing nearer does, so an execution that stated nothing
inherited a vocabulary the workflow host never offered.

With a host that registers the suspension `<Elicit>`, the leaked default then
selected a name whose implementation in that run offers no generated use, and
the whole fork was refused:

    the candidate definition diverges from the inherited history after 0 of 4
    events, at the fork's own run record (workflow_run): a host selected
    "Elicit" in its paired form, and the declaration that owns that
    implementation offers no generated use of it.

Each such execution now states `undefined` for itself — the answer core starts
from, and what this command's installations stated before the selection became
contextual. `PRR27` in `packages/cli/tests/pull-request-read-fork.test.ts` is the
end-to-end evidence: it forks a run that already read a pull request, and it
fails without this.

`offering()` and the new statement live in `workflow-generated.ts` because the
command imports the fork preflight, so the preflight cannot import the command
back.
Four of this stack's new assertions sorted with `toSorted()`, which is ES2023.
`deno check` accepts it and `pnpm exec tsc --project tsconfig.node.json` does
not, so every `test-node` shard failed before running a test:

    error TS2550: Property 'toSorted' does not exist on type 'string[]'.

`Object.keys()` hands back a fresh array, so sorting it in place moves nothing
a caller holds — which is what the rows beside these already do, for this
reason.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found 8 redundant comments. Inline suggestions to remove them below.

// the scope around this already offers, and only absence defaults. An empty
// or malformed selection is a host statement core validates and refuses;
// replacing it here would answer a host's mistake with this command's
// permission.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// permission.

// offered its own would be choosing its ceiling instead of being given
// one. The target-run description `installSyntax()` bound above stays
// separate — describing a component neither imports it nor grants its
// effects.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// effects.

// compared against the copy taken here, and middleware that returns
// anything else returns something nobody published. A foreign
// implementation carrying core's own URLs is not this object, so
// copying them publishes nothing either.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// copying them publishes nothing either.

// identification refuses it above, because there would be nothing to hand
// back. What is left is an answer that copied fine and is not something a
// fragment can invoke.
// fragment can invoke — a Markdown definition, or a structural one.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// fragment can invoke — a Markdown definition, or a structural one.

// The paths *this capture* resolves against, copied: the selection below is
// resolved against the environment the host assembled, and a later edit to
// the caller's own array describes a different one. What the document's
// authored imports resolve against is the accepted request's, read there.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// authored imports resolve against is the accepted request's, read there.

// The private closures, built from the minted implementations rather than
// from the declarations: what a private name resolves to is the function
// this execution built, and it names nothing once the execution is torn
// down above.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// down above.

// middleware or document code ran, except the search paths — those are the
// accepted request's, so the caller closes over this and supplies them. Asked
// only when an occurrence renders: a run whose document never writes
// `<Syntax />` enumerates nothing.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// `<Syntax />` enumerates nothing.

}
// One read of this member, into data this module owns. The copy below is
// what every check and the retained map read; the host's object is never
// consulted again.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// consulted again.

A host selects a name, and canonical execution resolves it through the ordinary
import chain — so a repository component on the search path is what a selected
name means. Those files could state nothing about generated use: the metadata
for `<Elicit>`, `<Glob>` and `<Fetch>` lived in core's registry, beside the
registration rather than on the component. Core's own component sources on a
search path were therefore a *different* declaration from the registration of
the same implementation, and selecting the name refused the run:

    xmd test packages/core/src          → 10 of 10 documents failed
    xmd -e '<File path="x" />' --include packages/core/src/components
      → a host selected "Elicit" in its paired form, and the declaration that
        owns that implementation offers no generated use of it.

Both are shipped commands: `xmd test <directory>` puts each document's own
directory on the search path, and CI smoke-tests the built-ins that way.

A function component may now export `generated`, read through the same
`admitGeneratedForms()` validator a registration goes through and held to the
forms the component accepts. `Elicit.ts`, `Glob.ts` and `Fetch.ts` state theirs
there, and the registry reads those values instead of restating them — one
declaration per component, whichever way an execution reached it. A module that
exports none is offered for no generated use, exactly as before; one that
exports something unusable refuses where it is loaded.

Exporting it is not permission. A fragment still needs the host's selection, the
authored form, the `allow` class and whole-fragment admission, and every effect
still crosses the installed providers and the effective `Permissions`.

`U5-3: a repository component is offered for what it declares, and no more`
drives both halves through the real loader: the offered copy runs in the
fragment, and a same-shaped copy that declares nothing refuses the selection.
@taras
taras merged commit 986948e into main Oct 16, 2026
44 checks passed
@taras
taras deleted the agent/upstream-u5-generated-selection branch October 16, 2026 01:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant