Skip to content

Make repeated documentation bootstrap idempotent (#758) - #765

Merged
taras merged 6 commits into
mainfrom
agent/syntax-bootstrap-correction
Sep 6, 2026
Merged

Make repeated documentation bootstrap idempotent (#758)#765
taras merged 6 commits into
mainfrom
agent/syntax-bootstrap-correction

Conversation

@taras

@taras taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Why

PR #759 merged before its final correction was pushed. PR #765 carries that follow-up work, but the product layering contract has now changed: the same declarative package bootstrap may legitimately be entered by inherited and local trusted host layers.

The signed correction commit 5c62cca2f365a41c7786b11a8cebfafd3733752c implements the former rule that every second documentation contribution for one owner/component refuses. Its earlier architecture PASS was correct for that former contract. It does not approve the idempotent behavior now required, so PR #765 needs a new exact-commit handoff and architecture verdict for the amended behavior.

Outcome

One package bootstrap and two value-identical package bootstraps produce the same registrations and documentation. An identical documentation repetition contributes nothing new and succeeds. A genuinely conflicting contribution still refuses before root execution, regardless of middleware order.

Complete contribution equality

The collector snapshots and compares the complete contribution:

  • owning package;
  • asset identity;
  • exact documentation text; and
  • component-name set.

Equality is by captured value, not JavaScript object identity. Component names compare as a set, independent of insertion order.

An exact repetition is folded to one captured contribution. Asset-only equality is insufficient because changed text or a changed component-name set must not be hidden.

Conflict rule

Two non-identical contributions that overlap on one owning-package/component-name pair refuse the whole collection before root import. Reversing middleware or bootstrap order cannot change the result and cannot select a winner.

A different owner does not conflict merely because it documents the same component spelling: named documentation joins by component name and origin. Different owners are unequal and are not coalesced. Disjoint component sets from one owner also coexist.

Lifecycle and scope

  • Package bootstraps install additive namespaced documentation middleware beside their registrations.
  • Canonical core remains the documentation middleware terminal.
  • Canonical execution collects once after trusted bootstrap and before root import, then snapshots by value.
  • Document-time middleware cannot affect the captured snapshot.
  • Separate bootstrap and execution scopes remain isolated.
  • Nested-run and Evaluate-related trusted layering may repeat the same package bootstrap without losing registrations or documentation.
  • Documentation remains descriptive only. Coalescing it grants no component authority and does not broaden <Evaluate allow>.

Collection is already execution-scoped. “Refuse only within one scope” is therefore not a separate policy; it describes the contribution list both the former strict rule and the new idempotent rule inspect. Strict refusal would incorrectly reject nested or independently layered hosts that inherit and locally enter the same declarative package bootstrap even though the captured values are identical and no winner is needed.

Correction guide

Start from signed commit 5c62cca2f365a41c7786b11a8cebfafd3733752c, the reviewed predecessor whose strict-repeat behavior this amendment supersedes. A later branch head is implementation state, not architecture approval, until it is handed off and reviewed by exact commit.

Keep:

  • canonical collection after trusted bootstrap and before root import;
  • the namespaced middleware terminal and additive wrappers;
  • by-value snapshots and document-time exclusion;
  • conflict refusal before root execution;
  • scope and overlapping-execution isolation; and
  • the Syntax vocabulary and ownership corrections.

Replace:

  • unconditional exact-repetition refusal in capturedDocumentation();
  • SYN25l's repeated-bootstrap refusal cases; and
  • comments, architecture, specification and PR wording that require one bootstrap per execution scope.

Frozen evidence

Replace SYN25l with discriminating evidence that:

  1. One bootstrap and two identical bootstraps produce byte-identical documentation and one captured contribution. Use fresh objects and a differently ordered name Set.
  2. Named <Syntax>, bare <Syntax />, and a document containing no <Syntax> all accept the identical repetition. The no-Syntax root reaches a sentinel effect.
  3. For one owner and an overlapping component, different assets, exact text, or complete component-name sets refuse before root execution in both orders. Disjoint sets and same-spelled components under different owners are positive controls. Owner inequality must never be silently coalesced; an assembly with a real overlap for either owner still refuses for that overlap.
  4. Overlapping executions and separate scopes remain isolated.
  5. Nested-run and Evaluate-related trusted layering can repeat the same package bootstrap without losing registrations, named documentation, child-specific symbols, or allow narrowing. A non-identical overlap refuses before child root or fragment effects.

The controls must fail asset-only comparison, object-identity comparison, unconditional repeat refusal, last-write-wins behavior, and document-time collection.

Review guide

Start with packages/core/src/documentation-api.ts, then review SYN25l and the nested-run/Evaluate-layering controls in packages/core/tests/syntax-component.test.ts. Confirm the architecture and executable-MDX specification state the same complete-value rule.

The focused review remains independent of CI. Return a new signed feedback SHA and the exact focused commands and results as soon as the frozen evidence passes.

Scope confirmation

  • The correction is limited to the package-documentation composition contract and its evidence.
  • Public <Syntax> behavior and protected ownership remain unchanged.
  • <Evaluate> consumes the corrected captured reference; it does not receive another collector or duplicate policy.
  • Production implementation is not approved until a new exact feedback commit passes architecture review.

Collection refused two contributions for one component of one package only
when they named different assets. An exact repetition — what a profile that
called one bootstrap twice produces — passed. Whether the second call also
installed a provider, a launcher or an execution policy is not a question this
boundary can answer, and the assembly is wrong either way, so it refuses now.

It has to refuse at collection rather than in the named form's index. A
document that writes bare `<Syntax />` builds no index, and one that writes no
`<Syntax>` at all builds no reference either, so deferring would let both run
to completion on an assembly nobody validated. SYN25l proves all three
document shapes refuse, with a single bootstrap as the positive control.

architecture.md said the assembled contributions travel by value on the
execution installation, which stopped being true when the installation field
was removed. It now states what is implemented: bootstraps install additive
namespaced middleware, core is the terminal, and execution collects once after
trusted bootstrap and snapshots by value before the root import.

The rest is vocabulary the rename left behind — the `xmd syntax` broken-pipe
diagnostic an operator actually reads, the `--json` help text, comments in
`plan-component.ts` and `protected.ts`, the spec's "lexical observation", and
the test helpers and authored bindings in the Syntax suite. Uses of
"observation" that name the error-observation boundary, the document-target
catalog and the diagnostic catalog are other product concepts and are left
alone.
@taras

taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Follow-up to #759, which merged as 9d5402ca before this correction was pushed. The commit was signed and evidenced on the #759 branch but never reached the PR head, so the merge shipped without it — leaving main admitting a repeated documentation bootstrap and carrying an architecture.md statement about the ExecutionInstallation.documentation field that #759 itself deleted.

Cherry-picked unchanged from d2f9f35fcc50925fcc2424cb666362760dcca56e onto latest main; the cherry-pick applied cleanly with the same 8 files and +176/−100.

Reopened #758 until this lands. <Evaluate> (#713) depends on the finalized SyntaxReference and documentation-bootstrap contract, so its implementation should wait for this to reach main.

@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown

PR #765: Make repeated documentation bootstrap idempotent (#758)

8 files, +702 / -211

Scope

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

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

Structural

Oxlint structural signals:

  • no-unused-vars ×3: packages/cli/src/plan-component.ts, packages/cli/src/cli.ts
  • no-empty-function ×1: packages/cli/src/cli.ts

Slop

Oxlint slop signals:

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

Static Analysis

Oxlint: 22 diagnostics across 2 files (9 rules)
Density: 0.031 violations/added-line

no-base-to-string (5): packages/cli/src/plan-component.ts
unbound-method (4): packages/cli/src/plan-component.ts, packages/cli/src/cli.ts
no-unused-vars (3): packages/cli/src/plan-component.ts, packages/cli/src/cli.ts
no-console (3): packages/cli/src/cli.ts
no-unsafe-type-assertion (3): packages/cli/src/plan-component.ts, packages/cli/src/cli.ts
no-empty-function (1): packages/cli/src/cli.ts
no-unmodified-loop-condition (1): packages/cli/src/cli.ts
no-floating-promises (1): packages/cli/src/cli.ts
consistent-return (1): packages/cli/src/cli.ts

Correctness

No extraneous code patterns detected.

The Syntax section still said contributions "travel by value on the execution
installation", describing the `ExecutionInstallation.documentation` field #758
removed. It now states what is implemented: a package bootstrap installs its
registrations and additive `Documentation` middleware together, canonical core
is the middleware terminal, and canonical execution collects the composed
contributions once after trusted bootstrap and snapshots them by value before
the root import. That captured snapshot — not a field on an installation — is
what `xmd syntax` and an authored `<Syntax>` both consume.

The duplicate rule is stated where it is enforced: any second contribution for
one owning package and component refuses during collection, an exact repetition
included, because collection is the only boundary every execution passes
through. SYN25l joins the Tier SYN table with the three document shapes it
covers and its positive control.

The rest finishes the vocabulary audit this feature promised, in the focused
specification section and the Syntax and `xmd syntax` test titles, helpers and
comments. Uses of "observation" that name the import chain's own observing, the
document-target catalog and the diagnostic catalog are other product concepts
and are untouched.

No runtime behavior changes.
Refusing every second contribution broke `xmd workflow`. The
repository-composition vocabulary is deliberately installed twice — once by an
ordinary run's bootstrap and again inside the workflow attachment, because
either may be the only one — and the attachment's scope descends from the run's,
so both wrappers sit in one chain. Collection saw two identical contributions
and refused, and seven CI shards failed on it.

`installation.ts` says so in its own words: "One array, three consumers." The
repeat is the product's layering, not a wrong assembly, and an identical
contribution cannot change what a document is told — which is the thing the
refusal exists to protect.

So a bootstrap that finds its own statement already in the chain does not append
it again. Two contributions are the same statement when they agree on the owning
package, the asset and the exact set of components; differing in any of the
three makes them two statements, and two statements about one component still
refuse at collection.

SYN25l now proves the repeat is idempotent — the named form, the bare form and a
document with no `<Syntax>` all work, and the collected snapshot holds the
contribution exactly once — with two bootstraps naming different assets as the
control that keeps it from passing vacuously. The specification and
architecture.md say the same.
@taras

taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Correction to this PR's own risk note. I wrote that no first-party profile bootstraps a package twice. That was wrong, and CI caught it: seven deno shards failed, all on workflow tiers.

packages/workflow/src/composition/installation.ts documents the layering in its own words — "One array, three consumers… useCompositionComponents() registers it inside a workflow attachment; useRunProfileRegistry()installDocumentComponents() registers it for an ordinary run." Under xmd workflow, two of those run in one scope chain (cli.ts:928 and workflow/src/deno/workspace/host.ts:184), and the attachment's scope descends from the run's — so both wrappers were in the composed chain and the second identical contribution refused.

Resolved as idempotent for an identical contribution: a bootstrap that finds its own statement already in the chain does not append it again. Two contributions are the same statement when they agree on owning package, asset and the exact set of components. Two that disagree — one component from two different assets — still refuse at collection, in either order.

SYN25l now proves the repeat is idempotent across the named form, the bare form and a document with no <Syntax>, and that the collected snapshot holds the contribution exactly once, with a conflicting pair as the vacuity control. Specification and architecture.md updated to match.

@taras taras changed the title 🚑 Refuse a repeated bootstrap, and finish the Syntax vocabulary (#758) Make repeated documentation bootstrap idempotent (#758) Sep 6, 2026
Coalescing on owner, asset and component set left the documentation text out,
so two bootstraps naming one path and reading different bytes — a stale build
tree, a substituted reader — would have coalesced silently and kept whichever
ran first. Equality now covers all four values a contribution is: owning
package, asset identity, exact text, and component-name set. By value, so a
fresh object each ask and a name set built in a different order are still the
same statement.

What reaches collection is therefore a real disagreement, and its refusal says
which of the three differs rather than guessing. Two owners documenting a
same-spelled component still coexist, because documentation joins by name and
origin; so do disjoint sets from one owner.

SYN25l becomes five rows: value-identical repetition adds nothing for the named
form, the bare form and a document with no `<Syntax>`, with one bootstrap
capturing exactly what two capture; a changed asset, text or overlapping set
refuses in either order; distinct owner and disjoint set are the controls that
keep that from over-refusing; overlapping executions stay isolated; and a
layered trusted bootstrap keeps every layer's registrations *and* one
documentation value.

The specification and architecture.md state the four values, the coalescing
rule, and the two non-conflicts.
@taras

taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Amended duplicate contract implemented at b951c835d6a8818f034a7ef6fae4e6321e4b09c9.

Complete-value idempotence. Equality now covers all four values a contribution is — owning package, asset identity, exact documentation text, component-name set — by value, so a fresh object per ask and a differently ordered name set are still the same statement. My previous 4b71a643 compared owner, asset and set but not the text, which would have coalesced two bootstraps naming one path and reading different bytes, keeping whichever ran first.

Refusal preserved for ambiguity. Two non-identical contributions overlapping one owning-package/component-name pair refuse before root import, in either order, with the message naming which of asset, text or component set differs. No asset-only comparison, no last-write-wins. A distinct owner is not a conflict — documentation joins by name and origin — and disjoint sets from one owner coexist.

SYN25l replaced by SYN25l.1–.5, matching the handoff: value-identical repetition across named/bare/no-<Syntax> with one bootstrap capturing exactly what two capture; changed asset, text and overlapping set refusing in both orders; distinct-owner and disjoint-set controls; overlapping-execution isolation; and layered trusted bootstraps keeping registrations and one documentation value.

Focused evidence, all exit 0:

  • deno task test packages/core/tests/syntax-component.test.ts — 7 passed (50 steps), 0 failed
  • deno task test packages/cli/tests/syntax-cli.test.ts packages/cli/tests/verbose-component.test.ts packages/core/tests/syntax-catalog.test.ts — 16 passed (69 steps), 0 failed
  • deno task test packages/cli/tests/testing-execution-host.test.ts packages/cli/tests/workflow-retention.test.ts — 3 passed (20 steps), 0 failed
  • deno task check, deno task lint, git diff --check — 0

Specification and architecture.md updated to state the four values, the coalescing rule and the two non-conflicts.

`Documentation` is public. A package may compose `around(...)` itself and hand
back two value-identical contributions without going through
`contributeDocumentation()`, and that assembly is exactly as valid — but
classification lived in the helper, so collection saw two contributions for one
owner and component and refused. Correctness depended on which spelling a
package happened to choose.

Collection now owns it: snapshot the composed chain by value first, fold every
completely identical contribution to one, then classify what is left. The helper
appends and nothing more, so there is one rule in one place rather than two that
can drift.

Non-identical contributions overlapping one owner and component still refuse
before root execution in either order, and a different owner or a disjoint set
is still no conflict.

SYN25l.6 installs two direct middleware wrappers that never call the helper —
fresh objects, the same four values, name sets in opposite orders — and requires
the named form, the bare form and a no-`<Syntax>` root to work with one
contribution captured, then requires the three disagreements reached that way to
refuse in both orders. Moving classification back into the helper fails that row
and passes every other one in the tier.
@taras
taras enabled auto-merge (squash) September 6, 2026 09:10
@taras

taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

Architecture and independent verification both pass on signed head 28e2030ce805e30b1873da0c22c05d32ac28e073.

  • Independent review: duplicate classification is owned by canonical collection after value snapshotting; direct Documentation.around(...) contributions and the helper have identical behavior.
  • Independent focused command: 10 passed (71 steps) | 0 failed.
  • Architect session verdict: PASS on the same exact head and PR base.
  • Signature valid for tarasm@gmail.com; git diff --check clean.

Auto-merge may proceed once required delivery checks and the current-main integration requirement are satisfied.

@taras

taras commented Sep 6, 2026

Copy link
Copy Markdown
Owner Author

The current-main integration head 3393031d1de98b563965b597ce80b2bb1deb830d is approved.

Auto-merge may proceed when the restarted required checks are green.

@taras
taras merged commit bb2c1c4 into main Sep 6, 2026
38 checks passed
@taras
taras deleted the agent/syntax-bootstrap-correction branch September 6, 2026 09:31
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