Commit c6c1e60
feat(cuelite): phase 2 — schema and query on the façade, in-house flip (plan 238) (#555)
* feat(cuelite): add LookupPath/Fields/Exists/String/Decode accessors
Extend the CUE-backed cuelite façade with the read methods surfaces A
(schema) and B (query) need before they can migrate off cuelang.org/go.
A LookupPath/Fields result keeps rebuildable provenance (root source
plus path), so a section lookup against a cached schema crosses
contexts without mutating the shared value. Each method ships a
dedicated unit test (100% statement coverage held) and a differential
arm in cuelitetest comparing it against a direct-CUE oracle.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* refactor(schema,query,requiredstructure): adopt cuelite façade
Migrate the schema validator (MDS020), the requiredstructure rule's
CUE checks, and the query/where matcher off cuelang.org/go onto the
cue/cuelite façade. No non-test file in the three packages imports
cuelang.org/go anymore.
RunCache shape: cache the source-retaining cuelite.Value and fix the
Unify operand order so the shared schema is the operand and the
per-file data is the receiver (dataVal.Unify(schemaVal)). The receiver
context is the one rebuilt into, so the shared cached value is read
(never mutated) and parallel workers stay race-clean while the
compile-once cache contract holds. CompiledCUE dropped its Ctx field.
MDS020 diagnostics stay byte-identical: the error walkers consume
[]*cuelite.PathError via cuelite.Errors, same .Path() route.
Added Value.Err() to the façade (compile/bottom status without the
concreteness check Validate applies).
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* docs(plan-238): record task 1+2 done, task 3+4 remaining
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* docs(plan-238): record parser-frontend decision and the test-contract blocker
Task 3 (flip the value engine in-house) is paused for a decision. Record
in the plan:
- Parser frontend: reuse cuelang's syntax-only parser (cue/parser →
cue/ast) in phase 2, with a fully in-house evaluator; phase 4 swaps in
a hand-rolled parser and drops cuelang.org/go.
- Blocker: several existing cue/cuelite tests pin CUE-specific behavior a
context-free pure-Go Value cannot reproduce (cross-context bottom
semantics, cueerrors.Error errors.As checks, the exact CUE conflict
message, and internal_test.go's CUE-only helper tests). Per plan 218
these are interim scaffolding the flip erases, but they are pinned, so
the flip cannot land green without sign-off to rewrite them to the
post-flip in-house contract.
No engine code changed and no test was weakened. Status stays 🔳.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* feat(cuelite): flip Compile/Unify/Validate onto the in-house engine
Add the pure-Go value model (engine.go), AST→value compiler over
cuelang's parser frontend (compile.go), lattice-meet Unify (unify.go),
concreteness Validate (validate_engine.go), direct map/JSON lifters
(lift.go), and the ported strict-JSON duplicate-key scanner (dupkeys.go).
Value becomes a context-free immutable struct; operand order no longer
matters. Rewrite the four pinned interim test classes to the post-flip
single-context contract per the coordinator authorization recorded in
plan/238, and align the differential oracle's deliberate-divergence rows.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* feat(cuelite): direct map validation + thunk eval for cross-field schemas
Wire schema validate.go, query.go, and requiredstructure onto the
in-house engine's direct map validation (CompileMap/LiftMap), dropping
the json.Marshal -> CompileJSON round-trip on the MDS020 hot path.
Add a deferred-thunk evaluator (eval.go) for the release-channels
proto.md ternary idiom — list index, if-comprehension, == comparison,
and sibling-field references resolved once data fixes them. Handle
defaulted disjunctions (bool | *false) and optional absent fields so a
field with a default or an absent optional is not reported missing.
mdsmith check . is clean.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* feat(cuelite): comparisons, !~, real-schema sweep, FuzzValidate
Treat a binary relational (A != "", mechanism == "push") as a scoped
comparison rather than dropping the left operand; add !~ regex
non-match; reject a thunk referencing an undeclared name at compile
(reference X not found). Lift invalid-UTF-8 and trailing-data JSON to
data-compile errors. Add the real-repo-schema differential sweep
(realschemas_test.go) and the FuzzValidate schema x data differential
fuzzer with documented tolerances for CUE's duplicate-leaf quirk,
eager-regex strictness, and the close+missing superset.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): align edge cases with CUE found by FuzzValidate
Reject definition/hidden field labels, standalone * default, unary +
on non-numbers, and a top-level free reference; keep concrete int and
float distinct (0 != 0.0); treat an open list [...int] as concrete
(empty default); dedupe equal concrete disjuncts (0|0 -> 0); reject
invalid-UTF-8 and trailing-data JSON; recover the oracle from cuelang
parser panics. Fuzzer tolerates CUE's lone-surrogate data rejection.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelite): subset/eval/decode unit coverage; fix freeRefs field labels
Add engine_test.go and eval_test.go covering the compiler subset, every
unify/bound rule, comparisons inside forced thunks, decode targets, the
lift branches, and the describe/String renderers. Fix freeRefs to skip
field-label and selector-member identifiers so a comprehension body's
struct field (e.g. {title: ...}) is not mistaken for a sibling
reference. Reject a comparison of non-concrete types (_ > 0) at compile.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelite): unify-rule matrix and branch coverage to 93%
Add unify_test.go (one accept/reject per lattice-meet rule, both operand
orders) and coverage_test.go (nested error propagation, call arities,
unsupported constructs, string comparisons, number-overflow lift). Raises
cue/cuelite statement coverage from 57% to 93%.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(schema): cover closed-struct extra-field diagnostic
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* docs(cuelite,plan-238): rewrite concurrency/stability for the in-house engine
Update doc.go: Value is now a context-free immutable struct, safe for
concurrent use with no synchronization and no per-document context
growth; the evaluator is in-house with cuelang only as the AST frontend.
Record task 3 done and task 4's benchmark numbers in plan 238, and trim
the resolved blocker section under the line cap.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject the bare top token _ as a field label
The 300s FuzzValidate run found {_: int}: _ is a valid VALUE (top) but
CUE rejects it as a field label. Reject it in fieldLabel so the schema
arm agrees with the oracle. The full 300s fuzz then passes clean
(483k execs, no divergence).
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* refactor(cuelite): one scope-threaded builder for struct/list/disjunction
Collapse eval.go's duplicate struct/list/disjunction/ident builders into
compile.go's via a single scope-threaded evalExpr. compileExpr is now the
unscoped face of evalExpr: it evaluates with a nil scope and defers an
index/relational construct over an unresolved sibling to a kThunk, while a
bare reference stays a hard "reference X not found" error. evalChild routes
field/element/branch positions through that deferral at compile time. Deletes
compileIdent/compileStruct/compileList/compileBinary/compileDisjunction.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelite): drive engine helpers and compiler error paths to 97.6%
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelite): drive statement coverage to 100%
Close the residual statement-coverage gaps left by the scope-threading dedup:
engine-level helpers and compiler error positions driven red/green, the
kind-exhaustive switch defaults exercised with constructed values, and two
structurally-impossible LabelName-error branches removed (the parser always
yields an *ast.Ident selector member, read directly via selectorName).
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): cover normalizePath and extractJSONSafely panic recovery
Drive internal/cuelitetest to 100% statement coverage: normalizePath's
nil-input and quote-unwrap branches, and extractJSONSafely's panic-to-error
recovery on a malformed cuejson input.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelite): cover per-kind unify discriminants for gobco branch coverage
Drive the kBytes/kFloat/kBool/kNull discriminant disjuncts of the unify
switches and the isReferenceName/isDeferrable operator disjuncts. Lifts gobco
-branch from 1253/1278 to 1276/1278; the two residual conditions are the same
path.go sepBracket switch-arm and multiline.go walk-back bound recorded as
structural in plan 237.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* perf(cuelitetest): tighten factor-gate budgets to 1.0x at the 238 flip
The in-house engine must never be slower than the CUE oracle it replaced, so
HotFactorBudget and ColdFactorBudget drop from 2.5x/2.0x to 1.0x — the
tightening plans 218/236/240 intended, realized at the 238 flip rather than
deferred. Rework the interim hot-looser-than-cold guard to assert both <= 1.0x.
Armed gate passes with margin (hot ~0.26x, cold ~0.38x). Update plans 236/238:
status 238 -> done, coverage 100%/100%, gobco 1276/1278, alloc + factor notes.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): align engine semantics with CUE for defaults, bounds, lift, thunks
Seven empirically-confirmed divergences from CUE v0.16.1, each pinned by a
red/green unit test:
- Disjunction defaults follow CUE's default-of-meet rule: multiple * marks in
one disjunction are ambiguous (non-concrete); the default of a meet is the
meet of the defaults; a parenthesized nested default is carried up the
flatten. The engine now tracks a set of default disjuncts, not one pointer.
- Equal concrete disjuncts collapse at build time ("x"|"x" is "x").
- An empty numeric/string bound interval reduces to bottom at compile time
(>=10 & <=5), restoring schema/extend.go checkUnifiable's conflict detection.
!= , =~ , !~ are not folded, matching CUE.
- Relational == / != compare numbers across kinds (2 == 2.0 is true).
- A float64 always lifts to a float leaf — no integral coercion — so the
CompileMap and CompileJSON lift paths agree with each other and with CUE.
- An all-bottom disjunction is a compile error (empty disjunction).
- A thunk nested in a list element, open-list tail, or disjunction branch is
forced against its sibling scope (the real scope-threading fix), instead of
false-rejecting with an *ast leak; an undeclared nested reference stays a
compile-time "reference not found".
cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject out-of-subset comparison/literal constructs eagerly; restore lone-surrogate-key rejection; narrow fuzz hatches
Engine subset alignment (each a red/green unit test, surfaced by the
cross-engine fuzzer):
- A lone-surrogate-ESCAPE object KEY is rejected at compile (restoring the
pre-flip CompileJSON contract) — two distinct keys collapsing to U+FFFD must
not last-wins merge. A literal U+FFFD key and a lone-surrogate VALUE stay
accepted, matching CUE.
- An int/float literal outside int64/float64, a bound over a type (>string),
unary +/- on a non-number, and an ordered/equality compare with a
non-concrete TYPE operand (A > _, a == string) now report a documented
out-of-subset / invalid-operation class at schema compile, matching CUE
(which defers but also rejects). An undeclared reference inside an embedded
disjunction branch ({A > "" | ""}) now rejects via the recursive thunk-ref
scan.
Harness honesty (cuelitetest):
- Both fuzz tolerance hatches re-justified and SCOPED to their documented
class: hatch 1 (strict-subset schema compile) fires only when the in-house
error names a documented class and is now checked BEFORE the oracle, which
also guards against cuelang.org/go non-termination on cyclic schemas; hatch 2
(lone-surrogate VALUE) requires a surrogate escape in the data. The leaf
superset tolerance is bounded to one extra leaf.
- Seeded every round-1 / newly-found minimized divergence as a corpus row
(P0 agreement cases) and a FuzzValidate f.Add seed (strict-subset cases).
- doc.go and plan 238 wording: "identical sets of rejecting leaf paths
(deduplicated)"; TestValue_Unify_singleContextOracle backs the
single-context claim with a direct one-context cuecontext oracle.
cue/cuelite and internal/cuelitetest hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject non-bool if-conditions and bare type-keyword field labels; drop dead helpers
Two more empirically-confirmed subset divergences from CUE, each fixed
red/green and surfaced by the cross-engine fuzzer:
- An `if` comprehension whose condition is not a concrete bool now rejects at
schema compile. A concrete non-bool (`if ""`, `if 1`) is a type error; a
non-concrete type/top condition (`if string`, `if _`) can never resolve, so
compileExpr no longer indexes an empty free-reference slice (the prior
behavior panicked).
- A bare type-keyword field label (`int:`, `string:`) is rejected out-of-subset.
CUE resolves a same-named reference in the field value as a self-reference
rather than the type, so `{int: {int}}` accepts `{}` where a quoted label
rejects; the in-house engine cannot model the shadowing. A field literally
named `int` stays expressible quoted (`"int":`).
Both classes are covered by the fuzzer's strict-subset hatch and seeded.
Dead-code cleanup (P2): removed the trivial evalUnary scope-only wrapper (call
compileUnary directly), the bytesReader wrapper (use bytes.NewReader as
dupkeys.go already does), and containsByte (strings.IndexByte). The kThunk
describe case is live and covered, kept. lift.go comments corrected: lifted
data structs are OPEN on purpose so closedness stays a schema property. plan
238 wording scoped: per-field diagnostics byte-identical, the unreachable
front-matter-shape diagnostic re-worded for the round-trip removal.
cue/cuelite and internal/cuelitetest hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject misplaced * default marks statically; add hatch for self-cycle leaf paths; rewrite plans 239/240
Engine: a `*` default mark is valid only as a disjunction branch. CUE rejects
a misplaced mark at parse — even in a list element the evaluator never forces
(`[if c {}, (*"")][0]`). The in-house engine only reached compileUnary on a
forced element, so it silently accepted the unreached mark. A new static
checkNoMisplacedDefault pass walks the whole AST after parse and rejects every
misplaced mark up front, matching CUE.
Harness: a structural self-cycle (a field referencing its own label,
`{a: [if a {}][0]}`) is rejected by both arms but at different leaf paths —
CUE at the root ("cycle with field"), the in-house engine at the field. Hatch
3 detects the self-cycle via the AST and tolerates that leaf-path-only
difference when the oracle's sole rejecting leaf is the root, never masking a
wrong accept or a stage mismatch. Both classes are seeded.
Plans (P2): plan 239 records what phase-2 eval.go already provides (the
scope-threaded evalExpr and its node coverage) and what surface C must add
(interpolation, for-clauses, scoped selector eval, a builtin registry), with
tasks reframed as "extend evalExpr". Plan 240 gains the real task 1 — replace
the cue/parser+ast+literal+token syntax frontend with a hand-rolled
CUE-subset parser — fixes the stale test-file inventory, and moves model
sonnet to opus with a sizing note.
cue/cuelite and internal/cuelitetest hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): propagate a hard comparison-operand error over a deferred reference
`A > !0` pairs an unresolved reference (A) with an unsupported construct (!0).
evalComparison returned the reference's errUnresolved first, deferring the
whole comparison to a thunk and silently accepting the schema; CUE rejects
"invalid operation !0" at compile. The operand evaluation now propagates any
hard (non-errUnresolved) error from either position before the deferral, so an
operand that can never resolve rejects the comparison eagerly, matching CUE.
Seeded both operand positions; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject an undeclared reference hidden in a top-level disjunction or list
`0x0 | 0 < A` and `[0 < A]` carry an undeclared reference (A) in a disjunction
branch or list element with no enclosing struct to bind it. compileSource only
checked a BARE top-level thunk, so the reference buried in a disjunction or
list slipped through and the schema was accepted; CUE rejects "reference A not
found" at compile. The top-level check now uses the recursive checkThunkRefsIn
against an empty declared set, descending the same positions the force pass
reaches.
Seeded both forms; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* docs(cuelite): fix stale compileDisjunction comment references
compileDisjunction was merged into evalDisjunction; update the two comments
that still named it. Cross-reference the * default check (compileUnary catches
a reached misplaced mark; checkNoMisplacedDefault catches an unreached one).
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject a hard error in an unreached list element eagerly
A list element with an unsupported construct (`[if c {}, (string*"")][0]`)
went uncaught: evalListElems returned on the first element's errUnresolved
deferral, never evaluating the `(string*"")` element, so the in-house engine
accepted the schema. CUE rejects "invalid operand string ('*' requires
concrete value)" at compile regardless of whether `[…][0]` reaches the
element. evalListElems now evaluates every element, returning a HARD
(non-errUnresolved) error from any position immediately while still deferring
the list when the only errors are unresolved references.
Seeded both forms; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): catch a hard error in a deferred comprehension body at compile
A comprehension body with an invalid construct (`[if mechanism {string != ""}][0]`)
went uncaught when the condition deferred: evalComprehension returned
errUnresolved without compiling the body, so the in-house engine accepted the
schema. CUE rejects the body's invalid operand at compile regardless of
whether the condition selects it. evalComprehension now compiles the body on
both deferral paths (unresolved-reference and non-concrete-type condition),
returning a hard body error while still deferring when the body's own
references merely await data.
Seeded both deferral paths; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): defer a non-indexed list field; reject a paren-wrapped default mark
Two more CUE-subset alignments:
- A non-indexed list field whose comprehension references a sibling
(`xs: [if c {1}, 2]`) was rejected "reference c not found" — isDeferrable did
not include a bare list literal, so the list never became a thunk. CUE
accepts and resolves it against data; a list literal is now deferrable, so
the comprehension resolves once the sibling binds. An undeclared reference
still rejects at compile.
- A `*` default mark wrapped in its own parens (`(*0) | 1`, `1 | (*0)`) was
accepted; CUE rejects it ("preference mark not allowed at this position") —
the mark must be the OUTERMOST operator of a disjunct. checkNoMisplacedDefault
now passes a non-mark position through a ParenExpr, so a paren-wrapped mark
is rejected while `*(a|b)` and a `(a|b)` sub-disjunction's own direct marks
stay valid. Verified against CUE across the full paren/mark matrix.
Seeded all forms; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): extend the self-cycle hatch to the compile-vs-validate stage divergence
Making a bare list literal deferrable surfaced a self-cycle shape the leaf-path
hatch did not cover: `{a: [if a {}]}` uses the field `a` as its own `if`
condition. CUE rejects it eagerly at schema compile ("cannot use list as type
bool"); the in-house engine defers the list and rejects at validate when the
self-reference cannot resolve. Both REJECT — only the stage and leaf differ.
Hatch 3 now tolerates a self-cycle whose in-house arm rejects at validate while
CUE rejects at schema compile, in addition to the both-reject root-leaf shape.
The hatch never fires when the in-house engine ACCEPTS, so a wrong accept of a
self-cycle still fails the fuzzer.
Seeded both shapes.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): skip cyclic schemas before the oracle instead of hatching self-cycles
A disjunction-hidden self-cycle (`{mechanism: "" | [if mechanism {}]}`)
produced a WRONG ACCEPT — the in-house engine accepted data CUE rejects — which
the leaf-path/stage self-cycle hatch correctly did not tolerate, so the fuzzer
flagged it. Matching CUE's cycle handling exactly would require implementing
its eager cycle detection (a disjunction defers the cycle to validate, a bare
field rejects it at compile), which is out of scope: a field reference cycle is
outside the documented front-matter subset, and no real schema cross-references
its own fields.
Replace the per-shape self-cycle hatch with a single pre-oracle guard:
schemaHasReferenceCycle builds the top-level field-reference graph and skips any
schema with a cycle (self, mutual, or disjunction-hidden) before consulting the
oracle — the same treatment as an out-of-subset construct. An acyclic schema
never trips it, so a genuine divergence is never masked. This is more honest
than tolerating a wrong-accept on a cyclic schema.
Both packages hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): widen the regex hatch to a non-string operand; split seed table
`({0!~""|0})` pairs a regex `!~` on an int operand (`0 !~ ""`) with a `0`
branch. The in-house engine rejects the non-string regex operand eagerly at
schema compile ("!~ requires strings"); CUE drops the bottom branch in the
disjunction and accepts. This is the same eager-strictness as the
regex-pattern hatch — in-house is stricter on a regex construct, never a
wrong-accept — so hatch 1's regex class now also matches "requires strings".
The non-disjunction form (`{a: 0 !~ ""}`) rejects at compile in both arms, so
the pre-oracle skip stays sound.
Split extraFuzzSeeds into baseFuzzSeeds (the well-formed grammar) and
edgeFuzzSeeds (the documented subset edges) to stay under the funlen budget.
Both packages hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): classify a non-list index as an invalid operation for the hatch
`({A: "" | "0"[0]})` indexes a string ("0"[0]). The in-house engine rejects it
eagerly at schema compile; CUE rejects it standalone but drops the bottom
branch in a disjunction and accepts. The index errors (non-list target,
non-integer index) are now worded "invalid operation" — the class CUE itself
reports — so hatch 1 recognizes the documented eager-strictness. The
standalone form rejects in both arms, so the pre-oracle skip stays sound.
Seeded both forms; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): tolerate top-level-disjunction leaf granularity
A bare top-level disjunction (`{m: "0"} | ""`) is the one shape where the
rejecting-leaf SET differs in granularity, not content: CUE adds a root-path
"does not satisfy disjunction" summary and reports the fields of the branch it
tried, while the in-house engine enumerates every failing field. Both REJECT —
the accept/reject decision agrees — and a bare top-level disjunction is an edge
schema shape (real front matter declares a struct). Hatch 3 tolerates the
leaf-set difference for a top-level disjunction both arms reject at validate; a
stage mismatch or a wrong accept on such a schema still fails.
Seeded the single- and multi-field forms; both packages hold 100% coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): generalize the leaf hatch to CUE's root-summary path
Replace the top-level-disjunction-only leaf hatch with a precise root-summary
hatch. CUE attributes a failure to the ROOT [] (a "does not satisfy
disjunction" / "incomplete value" summary) in two cases: a top-level
disjunction that matches no branch, and a deferred thunk referencing a
non-concrete field (`{mechanism: "" | "0", A: [if mechanism {}][0]}`). The
in-house engine instead names the precise field whose thunk could not resolve.
CUE emits the bare root [] leaf ONLY for these summaries — a plain field
mismatch carries the field path in both arms, and a genuine root-scalar
mismatch carries [] in both — so the hatch fires only when CUE reports the root
[], the in-house engine does not, and the in-house engine still covers every
NON-root field leaf CUE reports. A dropped field leaf, a wrong accept, or a
stage mismatch still fails. Replaces the schema-shape check with a leaf-set
signature check, dropping schemaIsTopLevelDisjunction.
Seeded the disjunction and deferred-thunk forms; both packages 100% covered.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): apply a disjunction-meet default regardless of operand order
`(0 | int) & (*1 | int)` should default A to 1, but the in-house engine left it
non-concrete and rejected — while the reversed `(*1 | int) & (0 | int)` worked.
retainByValue only kept a concrete default when a SURVIVOR was that bare
scalar, but a surviving meet can stay a disjunction (`int & (*1|int)` survives
as `1|int`), so the default 1 lived inside that survivor's value set and was
wrongly dropped. survivorContainsValue now descends a disjunction survivor's
branches, so the default is retained from either operand order. Verified
order-independent against the CUE oracle, with conflicting defaults
(`(*1|int) & (*2|int)`) still leaving the field non-concrete.
Seeded both orders; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): detect reference cycles through a close() wrapper
The cycle guard's unwrapStruct descended parens and a bare struct but not a
close({...}) wrapper, so `close({s: [(s)][0]})` — a self-cycle on s inside a
closed struct — slipped past the pre-oracle cycle skip and the in-house engine
(which defers the self-reference) diverged from CUE's eager cycle detection.
unwrapStruct now descends a single-argument close(...) call, so a cycle inside
a closed struct is skipped like any other.
Seeded the closed self-cycle.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): reject a single-quoted bytes literal as out-of-subset
`''` and `'x'` are CUE BYTES literals (token.STRING kind, single-quoted), a
distinct type from a string with no JSON front-matter representation. The
in-house engine, lacking a bytes kind, decoded them as strings via
literal.Unquote and accepted string data CUE rejects (`'' ` vs `""`).
compileBasicLit now rejects a single-quoted literal as an unsupported bytes
literal, so the cross-engine fuzzer's strict-subset hatch covers it.
Seeded both forms; cue/cuelite holds 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): accurate out-of-subset literal wording; exempt closed structs from the leaf bound
Two changes:
- An int/float literal Go's parser rejects is now reported "unsupported int/
float literal" without the misleading "outside int64/float64 range" suffix —
it also covers CUE's SI-suffix literals (1M, 1Ki), a syntax the in-house
parser does not accept, not only a range overflow. The strict-subset hatch
still recognizes the "unsupported" class.
- CUE's close-suppression: a CLOSED struct with an extra key reports just the
close violation and suppresses every absent-required-field error, while the
in-house engine reports the close violation AND each missing field. The
leaf-superset hatch exempts a closed schema from the surplus bound (the
suppressed missing fields are unbounded), while an OPEN schema stays bounded
to one extra leaf so a leaf-count blow-up still fails. schemaIsClosed detects
a top-level close({…}).
Seeded the SI-suffix and close-suppression forms; both packages 100% covered.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): skip the nested-disjunction duplicate-default case (carry to round 2)
The fuzzer found one disjunction-default shape the in-house engine gets wrong:
a parenthesized nested disjunction whose marked default value also appears
unmarked among its siblings (`{A: (*0|0)|10}`) wrongly accepts an absent A. The
engine flattens the disjunction and keeps the *0 default; CUE evaluates the
sub-disjunction first, so the nesting cancels the default and CUE rejects.
Matching CUE needs nesting-preserving default propagation in buildDisjunction —
a structural evaluator change deferred to round 2 (recorded in plan 239).
schemaHasNestedDuplicateDefault skips the EXACT pattern before the oracle: a
nested disjunction with an equal marked+unmarked disjunct. The flat `*0|0|1`
and the non-duplicate nested `(*0|1)|10` both behave correctly and do NOT trip
the detector, so no other wrong-accept is masked. Seeded the case.
Both packages hold 100% statement coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* test(cuelitetest): seed string/bool nested-duplicate-default variants
The nested-disjunction duplicate-default class is not numeric-only: the string
(`(*"x"|"x")|"y"`) and bool (`(*true|true)|false`) forms diverge the same way,
and schemaHasNestedDuplicateDefault catches them (via BasicLit text and Ident
name). Seed both so the detector's literal and identifier paths stay exercised.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): bound-check list index and MinRunes in wide-integer space
CodeQL flagged int64-to-int conversions before the bounds checks in
list indexing and strings.MinRunes. On 32-bit targets (the plan-240
wasm build) a wide literal would truncate and could select a valid
wrong element or invert the rune-count check. Compare in int64/float64
space before any narrowing.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* fix(cuelite): make the list-index narrowing provably bounded for CodeQL
The int64-space length comparison was correct but CodeQL cannot
connect int64(len(elems)) to the int range; bound the index by
math.MaxInt32 explicitly before narrowing, which is semantically
free for list literals.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: ⟨value,default⟩ pair model for disjunction defaults (P0a-c)
Replace the flatten-and-mark-pointers default machinery with CUE's
per-disjunct default mode threaded through build and meet:
- engineValue.modes (parallel to branches) carries each disjunct's
defaultMode (dfltMaybe < dfltNot < dfltIs), replacing the defaults
pointer slice. combineMode is is-default-wins max.
- evalDisjunction flattens by VALUE (flattenDisjunct), so a
parenthesized sub-disjunction whose value collapses to one branch
loses its default (nesting-sensitive cancellation): (*0|0)|10 now
rejects, matching CUE. A flat *0|0|1 keeps the default.
- unifyDisjunction takes the branch cross product with hasBottomLeaf
pruning: a struct/list/bound branch with a NESTED bottom is pruned
when any branch meets cleanly, so close({x:int})|close({y:string})
accepts {x:1}. When every branch fails, nested-dirty branches are
kept so the failing field leaf still surfaces.
Probed against cuelang v0.16.1. Red/green unit tests in
p0_default_semantics_test.go.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: fixpoint thunk forcing, sibling-default resolution, meet thunk-ref check (P0d-f)
- forceThunkFixpoint iterates soft force passes until no thunk newly
resolves, then a final hard pass collapses leftovers — an acyclic
chain n: [if m…], o: [if n…] resolves across passes (P0d).
- evalIdent resolves a defaulted-disjunction sibling to its default for
a comparison; a non-defaulted disjunction defers (P0e).
- evalBinary defers an & meet carrying an unforced thunk so the struct
ref check sees the undeclared reference instead of the eager meet
erasing it (P0f).
Probed against cuelang v0.16.1. Red/green in p0_default_semantics_test.go.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelitetest: delete nested-default carry skip, bound root-summary hatch
The nested-disjunction duplicate-default class is fixed by the ⟨value,
default⟩ model (P0b), so its pre-oracle skip and the
schemaHasNestedDuplicateDefault / disjunctionHasMarkedUnmarkedDup /
disjunctText machinery are deleted. The carry seeds become live seeds
exercising the cancellation on every run.
The root-summary hatch now bounds the in-house non-root leaf count by
the schema's top-level field count (or maxExtraLeaves) when the oracle
reports only the root, so a phantom-leaf fan-out cannot pass vacuously.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: backslash-tokenize raw surrogate scan; refresh stale post-flip comments (P1 g,i,j)
- rawHasLoneSurrogateEscape consumes escaped backslashes (\\) before
matching a \u escape, so a key like "<FFFD>\\ud800" (literal
text \ud800, not a unicode escape) is accepted, matching CUE. A real
lone-surrogate escape after \\ still rejects. Red/green unit tests +
fuzz seed.
- compile_cache.go, validate.go, runcache_wiring.go: rewrite the
cross-context / source-retention / Ctx-field comments to the post-flip
truth (immutable, context-free Value; CompileMap hot path).
- lift.go liftAny: structs lift OPEN, not closed.
- architecture/index.md: Compile/Unify/Validate are in-house; only the
parser frontend delegates (removed in phase 4).
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: restore 100% coverage after the default-model redesign
- Drop dead dedupeConcrete (branches are already deduped at build/meet
time) and the unreachable non-defaulted-disjunction-in-scope branch in
evalIdent; an in-scope disjunction is always concrete-with-default.
- White-box tests for hasBottomLeaf list-tail/disjunction arms, the
o-side final hard thunk force, and the truncated-\u raw scan bound.
cue/cuelite and internal/cuelitetest both at 100% statements.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: reconcile the meet's default from operand defaults (fuzz fix)
The 600s FuzzValidate run (first unrestricted pass over the default
semantics) found (*0|int)&(0|*int) wrongly rejected: the raw branch-mode
cross product fabricated a spurious second default {0,int}. unifyDisjunction
now computes the meet default once from the OPERAND defaults (U2): a
default survives when compatible with the other operand; both surviving
reconcile by meet (0&int=0), one surviving stands (·(*1|2|9)&(*2|3|9)→2),
build-time double marks stay ambiguous (*string|*"" rejects).
Regression seeds committed (code + testdata corpus). cue/cuelite and
internal/cuelitetest at 100%.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: concrete list/struct defaults (depends-on regression fix)
The ⟨value,default⟩ redesign restricted default reconciliation, dedup,
and branch-mode marking to concrete SCALARS, regressing concrete
list/struct defaults: [...int] | *[] (the plan/ proto depends-on schema)
wrongly rejected a PROVIDED empty list, breaking mdsmith check on 50
plan files.
Add concreteValueEqual (scalar/list/struct deep equality) and route
concreteOrNil, dedupeBranchModes, and the meet branch-mode marking
through it plus isConcrete, so *[], *{x:0}, and *[1,2] deduplicate and
reconcile like scalar defaults. Probed against cuelang v0.16.1.
Regression tests + mdsmith check . clean. 100% coverage held.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* plan: record the ⟨value,default⟩ redesign (238); drop the carried-work item (239)
Plan 238 gains a Task 5 recording the round-2 default-semantics
redesign (per-disjunct mode, nesting-sensitive build, meet pruning +
default reconciliation, fixpoint thunks, deleted carry). Plan 239's
carried-in nested-default fix becomes a 'resolved in phase 2' note; the
surface-C inventory is unchanged.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: reject ordered comparison on a non-orderable operand (fuzz fix)
The unrestricted 600s FuzzValidate found a pre-existing divergence:
0>0>A parses as (0>0)>A = false>A, which CUE rejects at schema compile
(bool is not orderable), while the in-house engine deferred a thunk that
rejected at validate with a root path.
evalComparison now rejects an ordered comparison (>, >=, <, <=) on a
concrete bool/null operand eagerly at compile, even when the other
operand is unresolved — the 'invalid operation' class hatch 1 already
tolerates. == / != and string ordering stay valid. Probed against
cuelang v0.16.1. Regression test + seeds; 100% coverage.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: reject chained ordered comparison even when inner defers (fuzz fix)
The run-4 fuzz found 0>A>0 = (0>A)>0: the inner 0>A is bool-typed
regardless of A, so the outer ordered op is invalid, but my prior fix
only caught a concrete non-orderable operand and this one deferred.
evalComparison now also rejects an ordered comparison whose operand is
SYNTACTICALLY a comparison expression (isComparisonExpr) — bool-typed by
construction — at compile, matching CUE. Legitimate single comparisons
and == in comprehensions still defer and resolve. Regression tests +
seed; 100% coverage; lint clean.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: reject indexing a non-list target even when the index defers (fuzz fix)
The run-5 fuzz found 0[mech] (mech unresolved): CUE rejects indexing a
non-list at compile (invalid operand, want list or struct) regardless of
the index, but the in-house engine deferred a thunk because the index
ref was unresolved.
evalIndex now checks the index TARGET is a list literal BEFORE evaluating
the index, so a non-list target is a hard compile error regardless of the
index. Valid list-index-by-reference still resolves. Regression test +
seed; 100% coverage; lint clean.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* cuelite: seed regex-match operands into the non-orderable reject set
A =~ / !~ operand is bool-typed, so an ordered comparison over it must
reject at compile even when the matched operand is a deferred string.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* ci(cuelite): exploratory differential fuzz job on engine-touching PRs
FuzzValidate and FuzzParsePath hunt new engine-vs-oracle divergences
out-of-band (240s each, path-filtered), with minimized crashers
uploaded as artifacts. Seed replay stays in the test job; review
rounds no longer run long fuzz loops. Retires with the oracle at
plan 240.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
* ci(cuelite): parallelize the fuzz targets via a job matrix
One job per fuzzer (fail-fast off, per-target crasher artifacts)
halves the wall time of the exploratory fuzz gate.
https://claude.ai/code/session_01LkXAFkv3U2K7Bcmz6dDS62
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2660481 commit c6c1e60
55 files changed
Lines changed: 8710 additions & 982 deletions
File tree
- .github/workflows
- cue/cuelite
- docs/development/architecture
- internal
- cuelitetest
- testdata/fuzz/FuzzValidate
- query
- rules/requiredstructure
- schema
- plan
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
156 | 156 | | |
157 | 157 | | |
158 | 158 | | |
159 | | - | |
| 159 | + | |
160 | 160 | | |
161 | | - | |
| 161 | + | |
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
0 commit comments