Skip to content

feat(#343): adopt Lume icon + state-as-glyph specs (additive, gate-re… - #725

Merged
Weegy merged 3 commits into
byte5ai:mainfrom
sneumannb5:feat/review-lume-spec-update
Aug 20, 2026
Merged

feat(#343): adopt Lume icon + state-as-glyph specs (additive, gate-re…#725
Weegy merged 3 commits into
byte5ai:mainfrom
sneumannb5:feat/review-lume-spec-update

Conversation

@sneumannb5

@sneumannb5 sneumannb5 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

What

Adopt the Lume icon + state-as-glyph specs (#343) into core as an additive, gate-ready change. Adds the icon/iconState trait and chart variant:"spark" to the canvas-tree whitelist, plus the "show state, don't narrate it" authoring rule. ADR-0010 records the decision.

Why

The upstream Lume specs (omadia-ui#41/#42) proposed an additive canvas-protocol §12 icon affordance and a spark chart variant; none had landed in core. This lands the vocabulary + validator contract so the desktop/mobile renderers have one thing to build against — without turning emission on. Nothing in core emits the new props yet; emission stays behind the iconResolver@1 handshake gate, because the Tier-1 renderer rejects unknown props wholesale (strict unevaluatedProperties:false whitelist), so a half-shipped prop would brick the whole surface rather than degrade.

Test plan

  • npm run gen && npx vitest run test/validator.test.ts test/fixtures.test.ts in canvas-core → 41 passed
  • Accept cases: app:/lib: icon + iconState, chart variant:"spark", _gallery.json fixture read-back
  • Reject cases: non-namespaced icon, deferred gen: prefix, iconState without icon (dependentRequired), unknown iconState/chart variant
  • Cross-repo: Tier-1 renderer iconResolver@1 support (byte5ai/omadia-ui) — out of scope, tracked as follow-up

Risk / blast radius

  • Schema change (additive only): new optional props on commonTraits + p_chart; no migration, no existing tree invalidated. $id stays protocol/1.0/… (additive vocabulary in the 1.0 whitelist — see ADR-0010 version story).
  • Security: gen: prefix is rejected by the schema (the last gate before a renderer) until the upstream §6 sanitised-SVG + consent review lands; pattern widens to include it in that same PR. app:/lib: only today.
  • a11y: state-as-glyph rule keeps a text/glyph label alongside tone — state is never signalled by colour alone.
  • No public API bump, no new env-var, no CI/release tooling touched.

Naming-decisions still pending

  • iconGenerator@1 pinned to generateAsset scope {kind:"icon"} (not a new capability enum) — decided in ADR-0010, no churn.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

@Weegy

Weegy commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Prepped for merge — main merged in at 4ae09f74, no conflicts, and the claims in the diff verified against the built validator rather than by reading the JSON.

Why it needed a refresh

The 9/9 green on this branch was measured against f1914c97, three commits stale (#730, #748, #747 have landed since). Green against an old base is not green — so I merged current main in and re-ran everything.

Verified, not assumed

The ADR says the gen: deferral is enforced in the schema ("not merely left un-emitted"). That is a security claim, so I drove it through the built validator instead of reading the pattern:

case result
icon: "app:play" accept
icon: "lib:cloud-check" accept
icon: "gen:a-sunset" reject
icon: "data:image/svg+xml;base64,…" reject
icon: "app:../../etc/passwd" reject
icon: "play" (no namespace) reject
iconState without icon reject (dependentRequired)
iconState: "active" with icon accept
iconState: "hover" reject
variant: "spark" on chart accept
variant: "sparkline" reject
tone: "success" on status accept
tone: "chartreuse" reject

13/13 as documented. The data: URI and traversal cases are not in the PR's own tests and are worth adding — they are the shapes an attacker reaches for once icon is a string that a renderer resolves.

Test state

main and this branch, measured back to back in the same environment: 6830 tests, 6818 pass, 0 fail, 0 cancelled on both. Identical — which is what we want, since the 32 added test lines are vitest in canvas-core (15/15 green) and run under a different runner than middleware/test. Test-file sets are byte-identical between the two branches (674 files, empty diff), so nothing was lost in the merge.

Two notes for the reviewer

1. The iconResolver@1 gate does not exist yet. Both schema descriptions say "emission is gated on the renderer advertising iconResolver@1 in the handshake". Repo-wide, iconResolver occurs only in those description strings, the generated validator that embeds them, and the ADR — there is no handshake check in any .ts. That is consistent with the PR being titled gate-ready, and it is safe today only because nothing emits icons: composition.ts gained the state-as-glyph rule and status.tone, but no icon instruction.

The risk is the next PR. Whoever adds icon emission must build the handshake gate in the same change — otherwise a Tier-1 renderer without icon support rejects the surface wholesale, because the whitelist is unevaluatedProperties: false. Worth stating in the ADR's consequences section so the sequencing is not carried only in a schema comment.

2. Unrelated repair worth naming. docs/adr/README.md gains rows for 0009 and 0010. 0009-default-ttl-for-mcp-tool-list-caching.md is on main but was never indexed in the README — this PR quietly fixes that omission from #702. Good catch; just noting it so it is not read as a merge artefact.

No blockers from my side.

@Weegy

Weegy commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Refreshed again — main merged in at e0f6434a. Three PRs landed since the last refresh (#752, #751, #750), which put this branch back to mergeable_state: behind, i.e. green against a base that no longer exists.

Re-verified on the new head:

  • canvas-core validator suite 15/15
  • the 13-case schema probe from my earlier comment still holds — app:/lib: accepted; gen:, data:image/svg+xml;…, app:../../etc/passwd and namespace-less play rejected; iconState without icon rejected; variant: spark accepted, sparkline rejected
  • full middleware suite 6860 tests, 0 fail, 0 cancelled

One thing that is genuinely better than last time: #752 fixed the core decoupling ratchet job, which had been timing out inside its apt-get step and reporting cancelled while the step that does the checking was skipped. That check now completes in ~50s, so this PR's run should give a real verdict on it rather than an absent one.

Still no blockers from me. The two review notes from before stand — the iconResolver@1 handshake gate does not exist in any .ts yet (safe only because nothing emits icons; whoever adds emission must build the gate in the same change), and the ADR-0009 README row this PR adds is an unrelated repair from #702 worth keeping.

@Weegy
Weegy merged commit caa5af0 into byte5ai:main Aug 20, 2026
9 checks passed
Weegy added a commit that referenced this pull request Aug 21, 2026
…lders audit row (#820)

Two findings from the same review wave, one branch because both are small
and sharp.

#764 — CI never ran the workspace packages' own test suites. `npm run test`
globs only `middleware/test/**`; canvas-core (vitest), conductor-core
(vitest) and plugin-api (node:test) ran nowhere. Not hypothetical twice
over: #759 broke two conductor-core tests while every required check stayed
green, and #725's 15 canvas-core validator tests were only ever run by hand
during review. New CI step runs all three. `@omadia/plugin-ui-helpers` is
deliberately absent: it declares a test script but contains zero test files,
and papering over that with --passWithNoTests would create a permanently
green no-op — the exact failure family this repo keeps finding (#640, #752).
If it gains tests, add it to the step.

#775 — the `conductor.role_holders_change` audit entry never landed: the
index.ts closure passed the session sub (an EMAIL under local auth) as
`actor.id`, and `admin_audit.actor_id` is a uuid column, so every insert
threw. Loud in the log, empty in the audit trail — the entire point of #759.

Fix: the conductor entry additionally threads the session's
`omadia_user_id` (a real uuid), and the mapping lives in an exported
`roleChangeAuditEntry` (adminAuditLog.ts): uuid to `actor_id` only when the
session carries one, the sub always to the free-text `actor_email` — the
same treatment the adminUsers routes give it, and for the `'operator'`
fallback the only place the actor survives at all.

Tests: 3 mapper units; the existing route test now proves the uuid threading
end-to-end (harness session carries omadia_user_id, deep-equal asserts it);
and a pg-gated suite runs the mapper output through the real
`AdminAuditLog.record` against the REAL migration DDL (read from the
migration file, not copied — a hand-duplicated schema could drift and
green-light what production rejects). Its second case pins the regression
permanently: the OLD mapping must keep failing on the real column with
`invalid input syntax for type uuid`.

Mutation checks: removing the uuid threading turns the route test red;
reverting the closure to the old inline mapping reproduces exactly the
insert the pg test asserts the database refuses. Verified against an
ephemeral postgres:16-alpine (2/2).

Full suite: 7048 tests, 1 pre-existing-shape failure fixed by extending the
existing expectation (the new field), then green; typecheck:test ratchet
held with no regressions; lint clean.
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.

2 participants