Skip to content

fix(smart-forms): allow visibility placeholders - #95

Merged
bazyk merged 3 commits into
corezoid:developfrom
yevhen-porechnyi:fix/smart-form-visibility-placeholder
Aug 24, 2026
Merged

fix(smart-forms): allow visibility placeholders#95
bazyk merged 3 commits into
corezoid:developfrom
yevhen-porechnyi:fix/smart-form-visibility-placeholder

Conversation

@yevhen-porechnyi

Copy link
Copy Markdown
Contributor

What & why

pushSmartForm rejected page configs that used a pure {{viewModelKey}} placeholder for visibility, although pong-server resolves that field before the page reaches the CDU renderer.

This change accepts pure visibility placeholders for forms, sections, and rendered items in header, modalHeader, and content, including nested and contentLoop items. Malformed, embedded, and multiple placeholders remain invalid. The unsupported section footer slot is deliberately not broadened.

The Smart Form skill and CDU protocol reference now distinguish server-side visibility binding from client-side reactive visibility.

Type of change

  • Bug fix
  • New MCP tool / capability
  • Skill behaviour
  • Docs
  • Chore / refactor

Checklist

  • make build && make vet && make test pass locally
  • No tool was added or renamed
  • Skill frontmatter is unchanged; make discovery produces no diff
  • Reference docs remain under plugins/simulator/docs/
  • No tokens or .env committed; TLS behaviour is unchanged
  • Added an entry under CHANGELOG.mdUnreleased

Notes for reviewers

Verified against pong-server template rendering: form and section visibility are injected explicitly, while rendered item properties are injected recursively. Placeholder values must resolve to visible, disabled, or hidden.

@gh-corezoid

Copy link
Copy Markdown
Contributor

AI Review

Relaxes pushSmartForm validation to accept pure {{viewModelKey}} placeholders for visibility on forms, sections, and rendered items in header, modalHeader, and content slots; footer is intentionally kept to resolved values only. Includes Go unit tests, CHANGELOG entry, CDU-protocol docs update, and skill documentation.

Checklist

Check Result
U1 — Conventional commit format ✅ pass
U2 — No leaked credentials ✅ pass
U3 — No merge commits ✅ pass
U4 — PR targets correct base branch (develop) ✅ pass
U5 — Build & tests (Go) ✅ pass
U6 — Architectural & design consequences ⚠️ warning (see below)
S1 — No manual edits to public/ ✅ pass
S2 — API path parameter names ⏭ skip (no internal/tools/ changes)
S3 — New tools have eval scenarios ⏭ skip (no new tools)
S4 — Discovery artifacts committed ✅ pass
S5 — All six manifest files version-synced ⏭ skip (no manifest files changed)
S6 — README / ARCHITECTURE updated for new tools ⏭ skip (no new tools)

U5 detail: go build ./... and go test ./... both clean on the PR branch (all 14 packages); base branch (develop) is equally clean — no regressions introduced.

S4 detail: Ran go run ./cmd/gendiscovery on the PR branch; public/ produced zero diff — the cduschema change does not affect discovered output.

Issues found

[warning — U6] isViewModelPlaceholder silently accepts {{ key }} with internal whitespace

key := value[2 : len(value)-2]
return strings.TrimSpace(key) != "" && !strings.ContainsAny(key, "{}")

strings.TrimSpace is used only to test emptiness, not to normalise the key. A value like "{{ myKey }}" (spaces inside braces) passes validation because TrimSpace(" myKey ") != "" and the key contains no {}. If pong-server's template engine does not strip whitespace from placeholder identifiers, {{ myKey }} will pass our validator but silently fail to resolve server-side (yielding an unresolved string instead of visible|disabled|hidden, which the CDU renderer may treat as invalid).

Suggested fix — tighten the guard to reject keys that contain any whitespace:

return key == strings.TrimSpace(key) && key != "" && !strings.ContainsAny(key, "{}")

None of the test cases cover this variant, so the current test suite cannot catch the regression if pong-server behaviour ever diverges.

[warning — U6] PR description claims contentLoop item coverage; no test confirms it

The PR body states the change covers "nested and contentLoop items." However, contentLoop is not in the validated slot list (["header", "content", "modalHeader"]) and has no dedicated test case in schema_test.go. If contentLoop is a distinct section slot (per SKILL.md: "contentLoop | section array expansion | one template → N rows"), its items are skipped by the validator entirely — both before and after this PR (pre-existing gap, not introduced here).

This is low-risk for the bug being fixed, but the phrasing in the PR description overstates coverage. Consider either:

  • adding a contentLoop test that documents the current behaviour (items not validated), or
  • adding "contentLoop" to the validated slot list with allowVisibilityPlaceholder: true if pong-server does resolve it.

This review was generated automatically. A human maintainer should still make the merge decision.

@gh-corezoid

Copy link
Copy Markdown
Contributor

AI Review

Re-review after synchronize: relaxes pushSmartForm validation to accept pure {{viewModelKey}} placeholders for visibility on forms, sections, and rendered items in header, modalHeader, and content; spaced-key placeholders ({{ key }}) are now correctly rejected; footer intentionally left to resolved values only.

Checklist

Check Result
U1 — Conventional commit format ✅ pass
U2 — No leaked credentials ✅ pass
U3 — No merge commits ✅ pass
U4 — PR targets correct base branch (develop) ✅ pass
U5 — Build & tests (Go) ✅ pass
U6 — Architectural & design consequences ⚠️ warning (see below)
S1 — No manual edits to public/ ✅ pass
S2 — API path parameter names ⏭ skip (no internal/tools/ changes)
S3 — New tools have eval scenarios ⏭ skip (no new tools)
S4 — Discovery artifacts committed ✅ pass
S5 — All six manifest files version-synced ⏭ skip (no manifest files changed)
S6 — README / ARCHITECTURE updated for new tools ⏭ skip (no new tools)

U5 detail: go build ./... and go test ./... both clean on the PR branch (all 14 packages, including 3 new test functions in cduschema); develop base is equally clean — no regressions introduced.

S4 detail: internal/cduschema/validate.go change does not affect discovery output — public/ correctly untouched.

Issues found

[warning — U6] contentLoop-expanded item visibility: coverage is implicit, not explicit

The PR description and both docs files state that the fix "applies to … items expanded from contentLoop." Architecturally this is correct — contentLoop is a section-level expansion directive, not a separate slot, and the items it expands come from section.content, which is already in the validated slot list with allowVisibilityPlaceholder: true. So content template items with a visibility placeholder do pass the validator.

However, no test exercises a section that uses both contentLoop and a visibility placeholder in its content template items. This leaves a documentation gap: a reader of schema_test.go cannot confirm from the test suite alone that contentLoop-expanded items are covered. The risk is low (the coverage path is real), but a one-line test documenting the behavior would close the gap and guard against future refactors that could accidentally break the implicit path.


Fixed since last review ✅

  • [U6] isViewModelPlaceholder accepted {{ key }} with internal whitespace. Resolved by commit 8d072c37 (fix(smart-forms): reject spaced visibility placeholders). The guard now uses key == strings.TrimSpace(key) — values like "{{ myKey }}" are correctly rejected. A dedicated test case ("placeholder with surrounding whitespace") confirms the behaviour.

This review was generated automatically. A human maintainer should still make the merge decision.

@gh-corezoid

Copy link
Copy Markdown
Contributor

AI Review

Re-review after synchronize: relaxes pushSmartForm validation to accept pure {{viewModelKey}} placeholders for visibility on forms, sections, and rendered items in header, modalHeader, and content; footer intentionally left to resolved values only; contentLoop-expanded item coverage is now exercised by an explicit test.

Checklist

Check Result
U1 — Conventional commit format ✅ pass
U2 — No leaked credentials ✅ pass
U3 — No merge commits ✅ pass
U4 — PR targets correct base branch (develop) ✅ pass
U5 — Build & tests (Go) ✅ pass
U6 — Architectural & design consequences ✅ pass
S1 — No manual edits to public/ ✅ pass
S2 — API path parameter names ⏭ skip (no internal/tools/ changes)
S3 — New tools have eval scenarios ⏭ skip (no new tools)
S4 — Discovery artifacts committed ✅ pass
S5 — All six manifest files version-synced ⏭ skip (no manifest files changed)
S6 — README / ARCHITECTURE updated for new tools ⏭ skip (no new tools)

U5 detail: go build ./... and go test ./... both clean on the PR branch (all 14 packages, including 5 new test functions in cduschema); develop base is equally clean — no regressions introduced by this PR.

S4 detail: internal/cduschema/ changes do not affect discovery output — public/ correctly untouched. Confirmed by the PR author's own make discovery check and the absence of public/ in the diff.

Issues found

No issues found.


Fixed since last review ✅

  • [U6] contentLoop-expanded item visibility: coverage was implicit, not explicit. Resolved by commit 701eb1f (test(smart-forms): cover contentLoop visibility placeholders). The new TestValidatePageConfig_ContentLoopVisibilityPlaceholder test exercises a section with a contentLoop directive and a {{fieldVisibility}} placeholder on a content item, confirming that the content-slot path with allowVisibilityPlaceholder: true is reachable and correct. The previously documented coverage gap is now closed.

This review was generated automatically. A human maintainer should still make the merge decision.

salimovartem added a commit that referenced this pull request Aug 21, 2026
No schema in the bundled swagger sets `additionalProperties: false`, so the
save endpoint stores a typo'd `visibilty` or a `head[].id` verbatim and the
defect surfaces only as a console error in the browser: the item never hides,
or the column list renders empty.

`cduschema` now derives from the swagger the property surface of every item
`class`, plus the nested spots where the schema IS precise (`extra`,
`options[]`, a table's `head[]` / `body[]`), and `ValidateFile` rejects a key
no variant declares.

The allowlist is the UNION over every schema variant of a class. The swagger
splits one class across type variants that each redeclare only part of the
surface (`value` is on `Edit-int` but not on `Edit-default`), so checking a
single variant would reject valid config. `TestProbeDocumentedKeysPresentInUnion`
guards that assumption — a swagger update that drops a real key fails the tests
instead of blocking users' pushes — and a class with no derived rule is skipped
rather than rejected.

Also transcribes two renderer-only rules the swagger cannot express (it carries
no `minLength` anywhere):

- a `label` / `image` `value` may not be an empty string;
- an `image` `value` must be a URL the *server* can fetch — the renderer
  proxies it through `/api/1.0/image?src=`, which rejects a `data:` URI.

Documented in `cdu-page-protocol.md` §5.1 / §10.1.

Deliberately out of scope: whether a `visibility` placeholder is legal. #95
argues from pong-server that a pure `{{key}}` resolves server-side, and owns
that question — this commit leaves the existing enum check and its message
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bazyk pushed a commit that referenced this pull request Aug 24, 2026
* fix(cduschema): reject item keys and nested shapes the renderer rejects

No schema in the bundled swagger sets `additionalProperties: false`, so the
save endpoint stores a typo'd `visibilty` or a `head[].id` verbatim and the
defect surfaces only as a console error in the browser: the item never hides,
or the column list renders empty.

`cduschema` now derives from the swagger the property surface of every item
`class`, plus the nested spots where the schema IS precise (`extra`,
`options[]`, a table's `head[]` / `body[]`), and `ValidateFile` rejects a key
no variant declares.

The allowlist is the UNION over every schema variant of a class. The swagger
splits one class across type variants that each redeclare only part of the
surface (`value` is on `Edit-int` but not on `Edit-default`), so checking a
single variant would reject valid config. `TestProbeDocumentedKeysPresentInUnion`
guards that assumption — a swagger update that drops a real key fails the tests
instead of blocking users' pushes — and a class with no derived rule is skipped
rather than rejected.

Also transcribes two renderer-only rules the swagger cannot express (it carries
no `minLength` anywhere):

- a `label` / `image` `value` may not be an empty string;
- an `image` `value` must be a URL the *server* can fetch — the renderer
  proxies it through `/api/1.0/image?src=`, which rejects a `data:` URI.

Documented in `cdu-page-protocol.md` §5.1 / §10.1.

Deliberately out of scope: whether a `visibility` placeholder is legal. #95
argues from pong-server that a pure `{{key}}` resolves server-side, and owns
that question — this commit leaves the existing enum check and its message
untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(smartform): audit the whole env tree for cross-file token defects on push

`cduschema.ValidateFile` is per-file by signature — one relPath, one source —
so it can never tell whether a page's `[[key]]` resolves against the locale
files, or whether a `{{key}}` has a viewModel default. Those defects survive
every server-side check too: the app_content endpoint stores the source
opaquely and pong-server serves whatever it cannot substitute verbatim. The
user meets them as a literal `[[key]]` on the rendered page.

New `cduschema.ValidateTree` audits the whole env tree at once, and
`pushSmartForm` runs it alongside the per-file pass. It is handed the WHOLE
tree, not just the files being written: deleting a viewModel default breaks an
untouched page, which is exactly what a changed-files audit would miss.

The error/warning split follows what the runtime can still rescue:

- a missing LOCALE key is an ERROR. Locale resolves from the static files
  only, so an unresolved `[[key]]` always reaches the browser as text.
- a missing viewModel default is a WARNING. The bound process returns a
  per-request viewModel merged over the defaults, so it may well be filled at
  runtime — it renders literally only when the backend call fails.

Also reported as warnings: a `label`/`image` bound to a default of `""` (the
renderer rejects an empty value), a default no page references, and — for a
LITERAL `contentLoop` — the exact entries missing a key the template uses.
Placeholders inside a TEMPLATED `contentLoop` are backend-filled and never
reported, so list pages stay quiet.

Warnings are surfaced on a successful push under a new `warnings` field.

Drive-by: `sortedSet` here and `sortedKeys2` in validate.go were byte-identical
duplicates; both are now one generic `sortedKeys`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cduschema): stop the new push checks from rejecting valid config

Review follow-up on the two validation commits. Each item below was reproduced
against this branch before it was changed.

The swagger-derived key union was NARROWER than the protocol this repo
documents, so seven shapes straight out of `cdu-page-protocol.md` §5 aborted a
push: `mainMenu.options`, `carousel.items`/`extra`, `comments.title`,
`timer.extra.duration`, `file.extra.{downloadUrl,uploadUrl,auth}`,
`upload.extra.compression`, `attachment.extra.downloadUrl` — plus the §4 base
envelope (`value`, `required`, `error`, `errorMsg`, `submitOnChange`, `extra`),
of which the swagger's File.allOf[0] carries only half. `applySupplements` now
widens the union from that same table, never switching a check on where the
swagger produced no rule (`row`/`draggable` stay rule-less, `carousel.extra`
stays unchecked). The probe test walked ten hand-picked classes and missed every
failing one; it now mirrors §5 row for row.

`ValidateTree` findings were tree-wide errors, so one dangling `[[key]]` in a
page nobody touched — written through the web UI, or predating these checks —
blocked every future push, and `pushSmartForm` has no force flag to get an
unrelated fix out. `ValidateTreeScoped` keeps reading the whole tree but blocks
only when this push is what breaks it: the page config, the app `locale`, or
that page's `locale` is among the files being written. Everything else is a
warning tagged `pre-existing`.

Three narrower defects in the token scan:
  - `\[\[…\]\]` was matched in every string, so `"regexp": "^[[:alpha:]]+$"`
    aborted the push over a locale key nobody wrote. `regexp`/`mask` are pattern
    fields and are skipped.
  - `contentLoop` scoped the whole section map, so `"title": "{{section_title}}"`
    next to a loop was dropped and its default then reported as dead. Only
    `content` is loop-scoped now.
  - `walk` harvested strings from map values only, so tokens in a string array
    element were invisible.

And `image.value` as a `data:` URI is now rejected, which is what the changelog
entry and §4 already claimed — the renderer proxies through `/api/1.0/image?src=`
and that proxy answers `400 "URL is not allowed"`. The no-false-positives test
had enshrined the opposite.

make build, make vet, go test ./... green; make discovery produces no drift.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QeU5WiHehPjJReAfefezBo

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bazyk
bazyk force-pushed the fix/smart-form-visibility-placeholder branch from 701eb1f to 7eb3ad0 Compare August 24, 2026 08:25
@bazyk
bazyk merged commit 2754783 into corezoid:develop Aug 24, 2026
2 checks passed
@gh-corezoid

Copy link
Copy Markdown
Contributor

AI Review

Re-review after synchronize: relaxes pushSmartForm validation to accept pure {{viewModelKey}} placeholders for visibility on forms, sections, and rendered items in header, modalHeader, and content; spaced-key and malformed placeholders correctly rejected; footer intentionally left to resolved values only; contentLoop-expanded item coverage explicitly tested.

Note: PR was merged (2026-08-24T08:26:03Z) before this review completed. This comment is posted for audit-trail completeness — the synchronize event (force-push with rebased OIDs, committed 2026-08-24T08:18–08:22Z) occurred before the merge.

Checklist

Check Result
U1 — Conventional commit format ✅ pass
U2 — No leaked credentials ✅ pass
U3 — No merge commits ✅ pass
U4 — PR targets correct base branch (develop) ✅ pass
U5 — Build & tests (Go) ✅ pass
U6 — Architectural & design consequences ✅ pass
S1 — No manual edits to public/ ✅ pass
S2 — API path parameter names ⏭ skip (no internal/tools/ changes)
S3 — New tools have eval scenarios ⏭ skip (no new tools)
S4 — Discovery artifacts committed ✅ pass
S5 — All six manifest files version-synced ⏭ skip (no manifest files changed)
S6 — README / ARCHITECTURE updated for new tools ⏭ skip (no new tools)

U1 detail: All three commits follow the <type>(<scope>): <description> convention; subjects are lowercase, under 70 characters, no trailing period. No manifest touched, so no "bump to" suffix required.

U5 detail: go build ./... and go test ./... both clean on the PR branch (all 14 packages including 4 new test functions in cduschema). Base comparison run at 0f6f96d (develop state before merge) — all 14 packages also clean. No regressions introduced.

S4 detail: Ran go run ./cmd/gendiscovery on the PR branch; public/ diff is empty — the cduschema change does not affect discovered output. public/ correctly absent from the PR diff.

U6 detail: isViewModelPlaceholder tightened with key == strings.TrimSpace(key) guard; footer slot explicitly excluded from placeholder allowance; allowVisibilityPlaceholder propagated correctly through recursive row/draggable children. No new design concerns identified.

Issues found

No issues found.


This review was generated automatically. A human maintainer should still make the merge decision.

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.

3 participants