Skip to content

Fix five spec-conformance defects (parser, @input types, layout min, json() lens, CLI stdout) - #9

Merged
rokoss21 merged 3 commits into
masterfrom
fix/spec-conformance-regressions
Aug 21, 2026
Merged

Fix five spec-conformance defects (parser, @input types, layout min, json() lens, CLI stdout)#9
rokoss21 merged 3 commits into
masterfrom
fix/spec-conformance-regressions

Conversation

@rokoss21

Copy link
Copy Markdown
Owner

Found while writing a real FACET contract end to end against facet-fct 0.1.2. Each defect makes a documented, spec-required construct unusable.

# Defect Effect
1 A comment line with an empty body (bare #) fails the whole document with F003: Unclosed delimiter Appendix B defines the comment body as zero-or-more chars; is_not required at least one
2 @input validated only primitives and any Every composite FTS type (list<T>, map<string,T>, struct {...}, unions, multimodal) failed F453 on conforming values — structured state could not be passed to a contract at all
3 min larger than a section's content Section was neither truncatable nor droppable, so the layout silently exceeded the budget (150 units rendered against a budget of 120)
4 json() lens Emitted the internal tagged ValueNode ({"kind":"Map","value":...}) into rendered content and ignored indent when passed as a named arg
5 CLI logging INFO diagnostics went to stdout, mixed into Canonical JSON, with ANSI escapes when piped — run --format json | jq was impossible, and scripts/spec_matrix_examples.sh had to strip logs to work

Repro for #3, which is the one that silently breaks a guarantee rather than failing loudly:

printf '@context\n  budget: 120\n\n@system\n  shrink: 0\n  content: "%s"\n\n@user\n  priority: 900\n  min: 400\n  shrink: 1\n  content: "%s"\n' "$(printf 'C%.0s' {1..100})" "$(printf 'F%.0s' {1..50})" > ov.facet
facet-fct run --input ov.facet --exec --format json

Approach

  • 就离谱(附数据) #2 reuses the type system that already exists rather than duplicating it: new fct_validator::runtime_value_matches_type parses the declared FTS type expression and matches the value. int is still widened to float per §8.4. This adds a fct-engine → fct-validator dependency (acyclic).
  • [建议] 简单记录一下我的发现过程 #3 clamps min to the section size before the truncate and drop decisions. §11.3 does not cover size < min at all, so the spec is updated in the same PR (effective_min[i] = min(min[i], size[i]), recorded in §21 as a targeted normative clarification within v2.1.3). The same clarification went into facet-standard as dfc3d7b.
  • 这也太明显了 #5 also stops --verbose from installing a second global subscriber, and reports the effective layout budget (after @context budget) instead of the host default.

Verification

  • 477 workspace tests pass, 12 of them new in tests/spec_conformance_regressions.rs (including negative cases: list<string> still rejects a list of ints, a struct still rejects a missing required field)
  • scripts/smoke_examples_spec.sh and scripts/spec_matrix_examples.sh both green
  • cargo fmt --all -- --check clean; no new clippy findings (the 3 existing ones under clippy 1.97 are pre-existing and untouched)

🤖 Generated with Claude Code

Эмиль and others added 3 commits August 21, 2026 17:58
Each of these makes a documented, spec-required construct unusable.

Parser: a comment line with an empty body (a bare `#`) failed the whole
document with `F003: Unclosed delimiter`, pointing at offset 0 rather
than the offending line. Appendix B defines the comment body as
zero-or-more characters, so `is_not` is one repetition too strict.

Runtime inputs: `@input` validated only primitives and `any`. Every
composite FTS type — `list<T>`, `map<string, T>`, `struct { ... }`,
unions, multimodal assets — failed with `F453` even for conforming
values, which makes structured state impossible to hand to a contract.
Validation now goes through the type system that already exists: the
new `fct_validator::runtime_value_matches_type` parses the declared FTS
type expression and matches the value against it. `int` is still
widened to `float` per §8.4.

Token Box Model: a `min` larger than the section's own content left the
section neither truncatable (already below min) nor droppable (the drop
test required `size == min`), so the layout silently exceeded the
budget — 150 units rendered against a budget of 120 in the added test.
`min` is now clamped to the section size before both decisions, which
is what §11.3 means by a floor on retained content. The spec's own
wording does not cover `size < min`; the clamp is the reading that
keeps the resource bound.

`json()` lens: serialized `ValueNode` directly, leaking the internal
tagged representation (`{"kind":"Map","value":{...}}`) into rendered
content, and treated `indent` as a positional argument only, so the
documented `json(indent=2)` was silently ignored. It now emits plain
JSON per Appendix A, honours `indent` in both forms, and rejects
unevaluated nodes explicitly.

CLI: `tracing` wrote INFO diagnostics to stdout, mixed into the
Canonical JSON that `run --format json` emits, with ANSI escapes even
when piped — so the integration pattern documented in
docs/18-integration-guide.md could not be piped to `jq` (and
scripts/spec_matrix_examples.sh had to strip logs to work). Logs now go
to stderr, colour is gated on `stderr().is_terminal()`, and `--verbose`
no longer installs a second global subscriber. The startup line also
reported the host default budget instead of the effective layout budget
after `@context budget` was applied.

Verified: 477 workspace tests pass (12 new in
tests/spec_conformance_regressions.rs), smoke_examples_spec.sh and
spec_matrix_examples.sh both green, cargo fmt clean, no new clippy
findings.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
§11.3 asked implementations to truncate "not below min" and to drop a
section once `size[i] == min`. When a section declares a `min` larger
than its own content, neither branch applies: it cannot be truncated
(already below min) and never satisfies the equality, so a conforming
implementation terminates with a layout that exceeds B — the one
guarantee the Token Box Model exists to provide.

Adds `effective_min[i] = min(min[i], size[i])` and states that `min` is
a floor on retained content, never a way to exceed the budget. Recorded
in §21 as a targeted normative clarification within v2.1.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`cargo clippy --all-targets --all-features -- -D warnings` is a required
CI job, and three findings fail it on current stable (they postdate the
last green run in April):

- two `collapsible_match` in the assertion parser — rewritten as match
  guards, which is also how the neighbouring arms already read
- one `unnecessary_sort_by` in the layout stable-order restore

No behaviour change; 477 tests, smoke and matrix scripts still green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
rokoss21 pushed a commit that referenced this pull request Aug 21, 2026
@rokoss21
rokoss21 merged commit 4bdceea into master Aug 21, 2026
12 checks passed
@rokoss21
rokoss21 deleted the fix/spec-conformance-regressions branch August 21, 2026 19:32
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