Skip to content

✨ Select structured data with Query and render YAML (#934) - #962

Merged
taras merged 5 commits into
mainfrom
agent/query-yaml-934-restart-20261019
Oct 19, 2026
Merged

taras merged 5 commits into
mainfrom
agent/query-yaml-934-restart-20261019

Conversation

@taras

@taras taras commented Oct 19, 2026

Copy link
Copy Markdown
Owner

Why

Documents need to select structured data without embedding a JavaScript transform, then choose explicitly how to render it.

What changes

Query accepts a value and a yq expression. With as, it binds the selected structured value silently. Without as, it prints every selected type as JSON, including quoted strings. Yaml renders a supplied value as YAML through the ordinary component and capture paths.

<Query value={report} yq=".records" as="selected" />
<Yaml value={selected} />

Selection uses the private published yq.rs@0.1.4 dependency. It accepts one complete JSON result, retains evaluation/refusal causes, and receives no host environment, file, clock or process capability. Existing binding, output and failure owners remain in place.

Review guide

Start with packages/core/src/components/Query.ts, Yaml.ts and their private query-engine.ts helper, then ordinary registry/docs/dependency wiring. The Deno-owned distribution harness still launches real source and packed Node/Bun consumers; its exclusion entry keeps only the unsupported driver invocation out of those runtime corpora.

How to verify it

Both original reviewers independently PASS identical signed commit fca8e1c27f5956e84734b3279d0492f1e21dbe8a, tree 4a294b044821d23cdc839522948b0090201d3143. Their complete reports and canonical completed results are retained in the local delivery archive.

  • Latest affected Query/Yaml/Json evidence: 12 tests / 48 steps passed on the unchanged product content at 9db42a14.
  • The single authorized Node environment assertion actually ran on 9db42a14: 1 pass, 0 skipped. The original pre-test IPC refusal remains preserved; no further retry is authorized.
  • The manifest-only child has 6 tests / 19 steps passed and complete shared-list evidence: Deno includes the harness; Node and Bun exclude its driver once each.
  • Ordinary REPL/docs evidence remains attributed to 642a9e59; real source, host-compiled and packed distribution evidence remains attributed to 4f666e0c. No historical execution is relabeled as a final-head run.
  • Committed deno task verify:clean ran once on exact signed fca8e1c2 and exited 0. Offline builds, release compile, resolution and concurrent ownership/non-interference proof passed.

New dependencies

yq.rs@0.1.4 supplies the ready expression evaluator shared privately by Query and Yaml. Dependency manifests and both lockfiles are committed; no loader or build workaround is introduced.

Risks and limitations

This remains a draft. Runner-owned test-weight measurement is required after the applicability change and is pending. Actual required CI and original Planner title/body APPLY followed by independent VERIFY remain delivery gates. If measurement incorporation produces a signed child, both original reviewers and committed verification must cover that exact final child before its push. No merge-readiness claim is made for this draft.

Scope confirmation

  • Changed files support ordinary Query/Yaml, their dependency/docs wiring and focused evidence.
  • No unrelated cleanup, engine protocol, persistence, automatic feedback or generated-loader change.
  • The description preserves exact review and execution attribution.

Closes #934.

taras added 5 commits October 19, 2026 11:02
The lockfile is frozen repository-wide, so taking a dependency is a separate
act from using one: resolve it, commit the lock, then run setup. This is that
act, and nothing in it reads the package yet.

`yq.rs` is pinned exactly, in the three places the repository resolves from —
core's own import map, core's npm manifest for the isolated builder, and the
root map the builder reads. No range, no fallback version.

The lock carries what resolution produced and nothing else: the specifier, the
package with its registry integrity, and the three membership entries that
place it under core. `pnpm-lock.yaml` is untouched, because canonical setup
owns the Node layout and resolves it there.
…934)

A document that reads a file, globs a list or computes data has the whole of
it and no way to say which part it wants. Writing an eval block to pick a
field is doing by hand what the producer already produced.

```xmd
<File path="package.json" as="packageText" />
<Query value={packageText} yq="from_json | .scripts" as="scripts" />
<Yaml value={scripts} />
<Query value={scripts} yq=".test" />
```

`value` and `yq` are required and ordinary; `as` is optional and ordinary. With
`as` the selected value is bound as data and nothing is written. Without it the
selected value is written as JSON — every type, strings included, quoted and
escaped the way `<Json>` writes them. One spelling, one reading: what a reader
sees never depends on which type the expression happened to select.

Text stays text until `from_json` or `from_yaml` decodes it, so a document that
meant to search a file's contents says so and one that meant to match the text
itself still can. Exactly one JSON value is an answer: `null`, `false` and `[]`
are answers, while selecting nothing and selecting several both refuse and say
to collect with `[ … ]`. A refused selection binds nothing and substitutes no
input, so a name it would have written keeps the value it had.

It queries the value it was handed. The producer has already finished and been
bound, so several queries over one value cost one read, and a producer's own
next-page marker is ordinary data this never follows.

`<Yaml>` is `<Json>`'s sibling for the other spelling of the same value: the
captured operand, serialized once, rendered by the query engine's identity
evaluation and returned exactly as it came back, terminating newline included.

The engine is the published `yq.rs` pin, loaded by the invocation that needs it
through one literal dynamic import observed with `until`. It reaches the
environment, the filesystem and the clock only through options, and both call
sites pass empty maps and omit the clock — so an environment read is an empty
string, a file load and a clock refuse, and no process is ever started.

The distribution is proven rather than assumed: one document runs under source
Deno, source Node and source Bun, from one compiled host binary with nothing on
PATH, and from the built npm closure packed and installed into a directory of
its own under both Node and Bun — with the emitted core manifest declaring the
exact pin.

`pnpm-lock.yaml` carries canonical setup's own resolution of that pin.
… Q7 claim (#934)

Both reviews asked for the same four finite corrections. None changes what a
document observes: the interface, the uniform JSON output, the optional-as
binding and the production boundary are exactly what they were.

The engine's loaded module was asserted into a hand-written shape. The pinned
package already publishes `yq`, `Yq`, `EvalOptions` and `Format`, so the import
is destructured and inferred instead, and the duplicated local interfaces are
gone. The fixture's empty array is declared rather than asserted, and the
comments that narrated the declaration beneath them are gone too — the
surprising ones stay: why the literal import is what a distribution carries,
why `matched` decides nothing, why the whole answer is parsed at once, and why
the renderer serializes once.

Selection answers with Effection's `Result<string>` rather than a local
ok/json/refusal union: the JSON text is the value, and the cardinality refusal
is an Error carrying the message an author already read. Query re-raises it with
that error as the cause, so what the document says is unchanged and the vendor
failure still travels through its own wrapper.

Q3 was one document asserting one aggregate message, and `@sh` is an encoder
rather than the disabled process operation. It is now four independent
observations with canaries this test owns and removes: a real host environment
value that `strenv` answers as empty, an actual existing file that `load`
refuses, a clock that refuses, and the package's own `system` refusing with the
marker it would have created absent. Each asserts its own cause and that the
target name was never bound.

Q7 ran real artifacts but kept no identity: the binary's digest was computed and
discarded, and the packed closure's were never taken. The one compile and one
closure build now record the binary's own SHA-256 and byte length, every packed
tarball's, each resolved consumption cwd, argv, runtime version and child exit,
and the emitted core manifest's exact dependency beside the literal root import
that survived the build — printed before the owned scratch is released, and the
binary re-read afterwards to prove the hash names what ran.
…934)

`query-component.test.ts` belongs to the corpus every runtime runs, and its
environment canary reached for `Deno.env` directly. The Node adapter installs
no Deno global, and the file carries no exclusion — so the shared suite named a
global only one of its three runtimes has.

The canary stays a real one. It is written and put back through `node:process`,
which is the surface this repository's own `API.Env` reads the environment by,
and the host readback now goes through that exported `env()` operation — so
what the assertion compares is the value a document's own environment read
would see. Restoration is registered before the value is set, so a scope ending
between the two still puts back what this process had.

All four observations stay as they were: the environment answer, the refused
load of a file that exists, the refused clock, the refused process operation
with its marker absent, each with its own cause and its own unbound target.

Node and Bun evidence for this file remains unresolved: the one Node check taken
was refused by tsx's IPC listener before any test registered, and no retry,
alternate launch path or substitute runtime was used here.
Discovery finds every `scripts/tests/*.test.ts` and hands it to all three
runtimes unless the manifest says otherwise, so the new packaging harness was
being scheduled under Node and Bun — where `Deno.execPath()`, the canonical
host compile and the dnt closure build have no meaning. Classifying it is the
act that file was missing.

Nothing it proves is dropped. The Node and Bun consumers are the ones this
harness launches: source Node through the repository's `tsx` loader, source
Bun, and the packed closure under both. Excluding the driver from those
runtimes leaves every one of those launches exactly where it was, and what the
engine does inside a document stays proven on every runtime by
`query-component.test.ts`.

One entry, in the list the adjacent Deno build and npm tooling already use,
under the issue the manifest keeps that decision in. Discovery, the runners,
the harness, its assertions and `exclusions.deno` are untouched.
@github-actions

Copy link
Copy Markdown

PR #962: ✨ Select structured data with Query and render YAML (#934)

17 files, +1823 / -4

Scope

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

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

🟡 Changes span 6 directories.

🟡 PR mixes config and source changes.

Structural

✅ No structural bloat detected.

Slop

✅ Slop indicators look low.

Static Analysis

Oxlint: 2 diagnostics across 1 file (2 rules)
Density: 0.001 violations/added-line

no-shadow (1): packages/core/src/components/Query.ts
no-base-to-string (1): packages/core/src/components/Query.ts

Correctness

FILE: packages/core/src/components/query-engine.ts, PATTERN: signal cluster, CONCERN: Multiple Oxlint violations in the same file, QUESTION: Are these violations indicative of unreviewed code or structural issues?

FILE: packages/core/src/components/query-engine.ts, PATTERN: speculative abstraction, CONCERN: Functions select and yaml used by 2 components (below Rule of Three), QUESTION: Is this abstraction justified or premature?

@taras
taras marked this pull request as ready for review October 19, 2026 23:27
@taras
taras merged commit 137f3b3 into main Oct 19, 2026
81 of 87 checks passed
@taras
taras deleted the agent/query-yaml-934-restart-20261019 branch October 19, 2026 23:27
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.

Add <Query> for XMD values

1 participant