You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Document production-ready ramp and add run diagnostics
Update docs around the project goal: liquid-spec is an implementation
harness for gradually building a full production-ready Liquid engine. Align
AGENTS/CLAUDE, README, SPECS, generated init docs, and the complexity guide
with the current 0-1000 ramp: trivial passthrough first, then literals,
variables, control flow, standard features, compatibility quirks, and
production recordings.
Add dumb-adapter audit guidance and warn that raw pass counts can be
misleading for naive implementations; Max complexity reached is the better
progress signal. Document using source-echo, always-empty, and always-raise
adapters to catch weak specs and confusing failures.
Add runner diagnostics:
- --list-passed prints passing specs with complexity/source for ramp audits.
- --json emits a single non-benchmark JSON summary with totals, failures,
skipped suites, max complexity, and optional passed specs.
Also include a well-hinted complexity-500 self[...] nested-loop scope spec for
dynamic lookup publication across nested loops.
Copy file name to clipboardExpand all lines: CLAUDE.md
+60-37Lines changed: 60 additions & 37 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
4
4
5
5
## What This Project Is
6
6
7
-
liquid-spec is a test suite and CLI for testing Liquid template implementations. It captures test cases from the reference Shopify/liquid implementation and can verify that any Liquid implementation produces correct output.
7
+
liquid-spec is a test suite and CLI for testing Liquid template implementations. Its purpose is to act as a harness for the gradual construction of a full, production-ready Liquid implementation: start with trivial passthrough specs, then progressively add variables, filters, control flow, partials, compatibility quirks, and finally production/theme recordings. It captures test cases from the reference Shopify/liquid implementation and can verify that any Liquid implementation produces correct output.
8
8
9
9
## CLI Usage
10
10
@@ -38,6 +38,25 @@ liquid-spec my_adapter.rb -l
38
38
liquid-spec my_adapter.rb --list-suites
39
39
```
40
40
41
+
42
+
### Dumb Adapter Ramp Audits
43
+
44
+
When changing early complexity scores or adding beginner specs, play dumb and verify the harness still behaves like an implementation curriculum:
45
+
46
+
```bash
47
+
# Source echo adapter: should pass only raw text, then fail on first object output
Use `--list-passed` to inspect accidental passes and `--json` for tooling. Prefer `Max complexity reached` / `max_complexity_reached` over raw pass count when judging partial or deliberately naive adapters.
59
+
41
60
### Result Logging
42
61
43
62
Each test run appends results to `/tmp/liquid-spec-results.jsonl` with the format:
LiquidSpec.compile do |ctx, source, parse_options|
@@ -230,6 +250,17 @@ Spec-level settings override source-level settings. For example, a spec with its
230
250
231
251
## Good Specs
232
252
253
+
Good specs preserve the project goal: help someone build a production-ready Liquid implementation gradually. A spec should teach one behavior at the right time, fail with an actionable message, and point to implementation guidance when the behavior is not obvious.
254
+
255
+
### Ramp discipline
256
+
257
+
- First-contact specs for a feature must be tiny, gentle, and hinted. If needed, score the first spec one point lower than follow-up specs so it appears first.
258
+
- Keep the 0-50 band boring: passthrough, literals, missing variables, simple variable lookup, a few simple filters, and assign.
259
+
- Keep whitespace control (`{{-`, `-}}`, `{%-`, `-%}`), drop/to_liquid boundaries, generated filter matrices, parser recovery, date/time quirks, and filesystem/security quirks out of the beginner band.
260
+
- Generated specs should not flood the early ramp. Prefer curated beginner specs early; generated compatibility breadth generally starts at 120+ or much later.
261
+
- If a dumb adapter that returns the input, returns `""`, or raises for everything passes a spec unexpectedly, either the spec is too weak or the complexity/hint needs review.
262
+
- When judging naive adapters, prefer `Max complexity reached` over total pass count. An always-empty adapter can pass later specs whose correct output is empty, but it should not advance through the contiguous ramp.
263
+
233
264
### Error specs: prefer raised errors over inline errors
234
265
235
266
Most specs that exercise an error path should let the error **raise** and
@@ -376,20 +407,20 @@ defaults:
376
407
377
408
### Complexity Scoring
378
409
379
-
Each spec should have a `complexity` field indicating implementation difficulty. Lower scores = simpler features to implement first. Specs without explicit complexity default to 1000 or the suite's `minimum_complexity`.
410
+
Each spec should have a `complexity` field indicating implementation difficulty. Lower scores = simpler features to implement first. Specs without explicit complexity default to 1000 or the suite's `minimum_complexity`. Complexity is capped at 1000; do not score specs above 1000.
380
411
381
412
| Range | Feature |
382
413
|-------|---------|
383
-
| 10-20 | Literals, raw text output |
384
-
| 30-50 | Variables, filters, assign |
385
-
| 55-60 | Whitespace control, if/else/unless |
386
-
| 70-80 | For loops, operators, filter chains |
387
-
| 85-100 | Math filters, forloop object, capture, case/when |
| 1000 | Production recordings and unscored specs (default) |
393
424
394
425
See [`liquid-spec docs complexity`](`liquid-spec docs complexity`) for the full guide with examples.
395
426
See [SPECS.md](SPECS.md) for guidelines on writing effective specs.
@@ -402,41 +433,33 @@ See [SPECS.md](SPECS.md) for guidelines on writing effective specs.
402
433
403
434
### Features
404
435
405
-
Adapters declare which features they support. Suites and individual specs can require specific features:
436
+
Adapters declare which features they do **not** support yet. Suites and individual specs can require capabilities; any required capability listed in `missing_features` is skipped so implementations can grow incrementally:
406
437
407
438
```ruby
408
439
LiquidSpec.configure do |config|
409
-
config.features = [:core, :shopify_tags]
440
+
# Empty means "try every spec". Add unsupported capabilities here.
441
+
config.missing_features = [:shopify_tags]
410
442
end
411
443
```
412
444
413
445
### Available Features
414
446
415
-
The `:core` feature is the recommended target for most implementations. It's an alias that automatically expands to include other essential features:
416
-
417
-
```ruby
418
-
# From lib/liquid/spec/cli/adapter_dsl.rb
419
-
FEATURE_EXPANSIONS = {
420
-
core: [:runtime_drops, :inline_errors],
421
-
}
422
-
```
423
-
424
-
**Core features (most implementations should declare `:core`):**
425
-
- `:core`- Full Liquid implementation. Expands to include `:runtime_drops` and `:inline_errors`
426
-
- `:runtime_drops`- Supports bidirectional communication for drop callbacks (test harness invokes adapter to access drop properties)
427
-
- `:inline_errors`- Errors are rendered inline in output rather than raised as exceptions
428
-
- `:strict_parsing` - Supports error_mode: :strict (default for most implementations)
447
+
Feature selection is denylist-based. Leave `missing_features` empty to try everything, or add unsupported capabilities while the implementation is still growing.
429
448
430
-
**Optional features:**
431
-
- `:lax_parsing` - Supports error_mode: :lax for lenient parsing
**JSON-RPC adapters** that can't support bidirectional communication for runtime drops should declare `features = []` to opt out of `:core` and `:runtime_drops`. They will still run all specs except those requiring `:runtime_drops`.
462
+
**JSON-RPC adapters** that can't support bidirectional communication for runtime drops should set `config.missing_features = [:runtime_drops, :ruby_types, :ruby_drops, :binary_data]` (plus any Shopify capabilities they lack).
440
463
441
464
## The Eval Tool
442
465
@@ -490,7 +513,7 @@ For complex tests, use YAML input via stdin or file:
A conformance test suite for [Liquid](https://github.com/Shopify/liquid) template implementations. Run **4,600+ test cases** extracted from Shopify's reference implementation to verify your Liquid parser/renderer produces correct output.
5
+
A conformance test suite for [Liquid](https://github.com/Shopify/liquid) template implementations. Run **7,000+ test cases** extracted from Shopify's reference implementation, curated basics, parser-error matrices, Dawn theme fixtures, and production recordings to verify your Liquid parser/renderer produces correct output.
6
6
7
7
## Why liquid-spec?
8
8
9
9
Building a Liquid implementation (compiler, interpreter, or transpiler)? liquid-spec helps you:
10
10
11
+
-**Build gradually** from an empty-template renderer into a production-ready Liquid implementation
11
12
-**Verify correctness** against the reference Shopify/liquid behavior
12
13
-**Catch regressions** when optimizing or refactoring
13
14
-**Discover edge cases** you might not have considered
@@ -152,30 +153,36 @@ regular runs.
152
153
153
154
| Suite | Tests | Description |
154
155
|-------|-------|-------------|
155
-
|**basics**| 183 | Essential Liquid features - start here! Ordered by complexity with implementation hints |
156
-
|**liquid_ruby**|~1,700 | Core Liquid specs from [Shopify/liquid](https://github.com/Shopify/liquid) integration tests |
157
-
|**shopify_production_recordings**|~3,000 | Recorded behavior from Shopify's production Liquid compiler |
156
+
|**basics**| 850 | Essential Liquid features - start here! Ordered by complexity with implementation hints |
|**shopify_production_recordings**| 2,260 | Recorded behavior from Shopify's production Liquid compiler |
158
162
|**shopify_theme_dawn**| 26 | Real-world templates from [Shopify Dawn](https://github.com/Shopify/dawn) theme |
159
163
160
164
### The Basics Suite
161
165
162
166
If you're building a new Liquid implementation, **start with the basics suite**. It runs first and covers all fundamental features from the [official Liquid documentation](https://shopify.github.io/liquid/).
163
167
164
-
Specs are ordered by complexity so you can implement features progressively:
168
+
Specs are ordered by complexity so you can implement features progressively. The goal is a smooth ramp: a toy renderer should pass only the trivial first specs, then fail on a small, actionable next behavior.
165
169
166
170
| Complexity | Features |
167
171
|------------|----------|
168
-
| 10-20 | Raw text output, string/number/boolean literals |
| 1000 | Production recordings and unscored specs |
182
+
183
+
Each non-trivial spec includes a detailed `hint` explaining how the feature should be implemented. If the first failure is surprising or unactionable, the spec probably needs a better hint or a higher complexity score.
184
+
185
+
**Read `Max complexity reached`, not just total passes.** A naive adapter that always returns `""` can accidentally pass many later specs whose expected output is empty, but its max reached complexity should remain at 0. The max-complexity line tells you how far the implementation progressed through the ordered curriculum.
179
186
180
187
### Feature-Based Suite Selection
181
188
@@ -215,6 +222,9 @@ Run Options:
215
222
--list-suites List available test suites
216
223
--max-failures N Stop after N failures (default: 10)
217
224
--no-max-failures Run all specs without stopping
225
+
--list-passed List specs that passed after the run (ramp/debug audits)
226
+
--json Output a single JSON summary (for tools)
227
+
--jsonl Output one JSON event per line (for benchmark streaming/tools)
218
228
-h, --help Show help
219
229
220
230
Examples:
@@ -228,6 +238,24 @@ Examples:
228
238
liquid-spec inspect my_adapter.rb -n "case"# Debug specific specs
229
239
```
230
240
241
+
242
+
### Auditing the Ramp with Dumb Adapters
243
+
244
+
When changing complexity scores or adding early specs, test the harness with intentionally bad adapters:
245
+
246
+
- an adapter that returns the template source unchanged
247
+
- an adapter that always returns `""`
248
+
- an adapter that raises during compile or render
249
+
250
+
Use `--list-passed` to see accidental passes and `--json` for machine-readable analysis:
A source-echo adapter should only pass raw-text specs before failing on first object output. An always-empty adapter may pass many empty-output specs, so judge progress by `max_complexity_reached`, not by total passes.
258
+
231
259
### Matrix Command
232
260
233
261
The `matrix` command runs specs across multiple adapters simultaneously and shows differences between implementations. This is useful for comparing behavior across different Liquid implementations or configurations.
@@ -442,15 +470,17 @@ Specs are automatically saved to `/tmp/liquid-spec-{date}.yml` for easy contribu
0 commit comments