Commit Graph
8 Commits
Author SHA1 Message Date
DumiFlex 0b36ce892b feat(engine): multi-pick prefix gains N-M range + ~ independent flag
The {N$$sep$$} brace prefix now also accepts a count range ({N-M$$}) and a
~ independent flag ({N~$$} / {N-M~$$}, repeats allowed; absent = unique).
Both tokenizers (Python engine + TS richTokenize) parse it identically
into dp-multi meta {min, max, independent}; legacy `count` is kept (== max)
so existing readers + the resolver behave unchanged for a fixed {N$$}.
Shared syntax-corpus.json locks Py≡TS parity (existing multi_pick entries
gain the new keys; new range/lone-ref cases added).

Resolution of the range/independent semantics lands next (engine resolver);
this commit is tokenizer + parity only — a fixed {N$$} is byte-for-byte
unchanged.
2026-06-09 17:54:48 +03:00
DumiFlex 3ac0afa979 feat(engine): tokenize var.K list accessor (py+ts parity) 2026-06-08 14:12:42 +03:00
DumiFlex 13de622c6b feat(engine): multi-tag options + boolean sub-category filters (v2)
Option sub_category -> sub_categories[] (default []). Both pool filters (instance category_filter, nested @{} ref) become boolean expressions (and/or/not/parens; comma=or) matched against an option's tag set via the shared engine/syntax/subcat_filter matcher, applied BEFORE constraints. Exclude-null is a separate flag: instance exclude_null + the trailing !null ref marker, replacing the old 'null' keyword.

4-segment ref grammar @{uuid#name:expr!null} single-sourced across tokenize.py/scan.py/pipeline.py/_constraints.py (! excluded from name/expr captures); tokenizer REF meta now carries filter_expr + exclude_null instead of a comma-split list. scan.py findall unpack sites updated for the 4th group.

validate_payload: per-option sub_categories[] must be in the registry; optional tag_groups (UI-only axes, members in-registry, <=1 group); registry names validated via subcat_filter.validate_subcat_name (rejects whitespace, boolean/ref chars, reserved words incl. null). wp_api/_validators delegates to the same function.

Constraints stay alive via a primary-tag stopgap: _apply_constraint_to_options + _record_pick use sub_categories[0] as the matrix key; true multi-tag matrix matching is SP3. Cascade fix_subcat_delete/rename + orchestrator scrub the per-option list; instance_schemas category_filter -> string. Existing engine tests migrated to the v2 shape; added tests for the ref regex, payload validation, and multi-tag pool filtering.
2026-06-07 00:43:55 +03:00
DumiFlex 85cf827d56 chore: pre-pair-badge polish (bundle, cascade, debug, light-theme, cleaner)
Accumulated branch work that was uncommitted alongside the pair-badge feature in the prior commit. Captured as a single sweep commit so the branch can merge cleanly; future work should commit per-feature to avoid this.
2026-05-26 11:24:43 +03:00
DumiFlex b950b8fd82 feat(engine,scanner): nested-ref constraints + @{uuid:subcat} filter + smarter target classification
Three improvements aimed at making constraints actually useful in
composed pipelines, with the conflict scanner keeping up.

## Phase A — nested constraints (engine)

Pre-2026-05 constraints only fired when the target wildcard ran as a
top-level chain module. Wildcards reached through `@{uuid}` refs from
inside another wildcard's option value silently bypassed every rule.
Composition (outfit-set wildcards, palette reuse) was a constraint
blind spot.

- New `engine/modules/_constraints.py` holds the shared application
  logic (loop constraints by target uuid, look up source pick, apply
  matrix/exception cells, emit unknown_constraint_source +
  constraint_excludes_all_options warnings)
- `wildcard_handler.resolve` now delegates to the helper instead of
  inlining the loop
- `engine/syntax/resolve._resolve_ref` calls the same helper after
  fetching the catalog wildcard but before rolling — nested target
  wildcards now get constrained identically to chain-level ones
- `ResolveContext` exposes `get_constraints()` + `get_picks()` so the
  resolver has the back-channel it needs without depending on the
  full pipeline ctx dict

## Phase B — `@{uuid:subcat[,subcat]}` filter

New optional ref syntax. `@{color:warm}` resolves the catalog wildcard
`color` but restricts its option pool to entries whose `sub_category`
is in the filter list. Same semantics as chain-level
`instance.category_filter`, but scoped per call site so one library
wildcard stays reusable.

- Python tokenizer regex extended to capture optional `:subcat,subcat`
  group; `_resolve_ref` filters options pre-pick + pre-constraint
- TS `richTokenize.ts` mirrors the grammar so previews + RichTextInput
  recognise the new form
- `extension/graph.ts:WC_REF_RE` + `preview-tokens.ts:REF_RE` updated
  to skip the filter when extracting uuids (uuid is still group 1)
- Empty pool after filter → empty string output + new
  `ref_subcategory_empty_pool` warning so the user sees an
  unsatisfiable filter instead of falling back silently

## Phase C — scanner reach-set + classification refinement

Constraint scanner pre-fix:
- flagged `constraint_target_in_upstream` whenever target had ANY
  upstream instance, even if a downstream / local-after / nested-after
  instance was also present
- flagged `constraint_target_missing` for targets reachable only
  through nested `@{}` refs

Both false-positives. Post-fix scanner computes
`localNestedReachAfter[i]` — transitive `@{}` walk through the local
catalog starting from modules at positions ≥ i — and uses it to:

- skip `target_in_upstream` when a bindable instance exists elsewhere
  (downstream, local-after-self, nested-after)
- skip `target_missing` when target is reachable via nested ref from
  any later chain wildcard
- still flag both when no bindable instance exists (sole-upstream,
  truly-missing both remain caught)

## Tests

- 8 new Python tests under `TestNestedConstraints` +
  `TestNestedSubcategoryFilter` (constraint applies through nested,
  excludes-all warning, source-not-yet-picked warning, subcat narrows,
  multi-subcat union, empty pool warning, empty filter no-op,
  subcat+constraint composition)
- 7 new TS tests under `scanConflicts — constraint nested-reach
  classification` (nested reachable doesn't flag missing, transitive
  deep reach, upstream+downstream silent, upstream+local-after silent,
  sole-upstream still flags, nested-from-before-constraint still
  flags, nested with subcat filter still reaches)

## Numbers

- 696 pytest pass (+8)
- 1672 vitest pass (+7)
- ruff clean
- bundle 306899/307200 (301 bytes headroom)
2026-05-19 21:40:08 +03:00
DumiFlex df0ff9441b feat(syntax): tokenize dp_brace + dp_multi with nesting support 2026-04-28 04:17:54 +03:00
DumiFlex 4717865061 feat(syntax): tokenize $var and @{uuid} kinds + corpus cases 2026-04-28 04:05:17 +03:00
DumiFlex 761b45c8b2 feat(syntax): tokenize_text — text + escape kinds with lossless invariant 2026-04-28 03:35:16 +03:00