You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5.9 KiB
5.9 KiB
Audit: month-grid
audit-version: 1 audited-at: 2026-06-26 scope: soma,sema (SCOPE-DRIFT → SYS-1) method: adversarially-verified workflow (analyze → refute); HIGH/CRITICAL personally re-verified by the lead. Batch-3 ground-truth: each picker fires trigger(close) (close NOT inert), but open/commit-reset ARE inert; calendar/range-calendar are MID-REFACTOR (uncommitted view-switch work). provider: src/uix/soma/components/month-grid/month-grid-provider.svelte.ts
Summary
Counts (post-verification): CRITICAL 0 · HIGH 0 · MEDIUM 1 · LOW 1. systemic hits: SYS-1 (scope-drift: eidos exists but not in morfo scope). composition (A27): N/A (not a picker)
Findings
MEDIUM: SCOPE-DRIFT (SYS-1): An eidos recipe dir exists but morfo 'scope' omits 'eidos' — month-grid-002
- dimension: A
- rule: SCOPE-DRIFT (SYS-1): An eidos recipe dir exists but morfo 'scope' omits 'eidos'
- location: src/uix/morfo/components/month-grid.ts:7 (morfo scope declaration) vs src/uix/eidos/components/month-grid/ (eidos directory exists)
- evidence: Morfo declares
scope: ['soma', 'sema']but eidos components and CSS exist at src/uix/eidos/components/month-grid/ - impact: Systemic inconsistency. Consumers may not expect eidos layer. Type safety and documentation alignment degraded.
- repro: Observe the eidos directory structure.
- proposed-fix: Update morfo scope to
scope: ['soma', 'sema', 'eidos'] - verify: [confirmed] Confirmed SYS-1 scope-drift. month-grid.ts:7 declares
scope: ['soma', 'sema']while a full eidos layer exists: src/uix/eidos/components/month-grid/ contains month-grid.css (recipe-consuming[data-month-grid]block at lines 3-7, e.g.--_month-grid-cell-size: var(--calendar-day-size-md)), plus 8 .svelte wrappers, types.ts, index.ts and README.md. The morfo omits 'eidos' from scope. This is the known systemic MEDIUM baseline — matches the SYS-1 pattern seen across batch-1/2. - fix-status: fixed (
212624e0)
LOW: DOM-selector: querySelector/querySelectorAll must not interpolate consumer/state-derived v — month-grid-001
- dimension: C
- rule: DOM-selector: querySelector/querySelectorAll must not interpolate consumer/state-derived values without CSS.escape()
- location: src/uix/soma/components/month-grid/month-grid-provider.svelte.ts:247-248
- evidence: const el = root.querySelector(
[data-month-grid-cell][data-value="${this.placeholderYear}-${nextMonth!}"]); - impact: If placeholderYear or nextMonth contained CSS special characters (e.g., backslash, quote, bracket), the selector would break or match unintended elements. While current numeric values are safe, future state mutations or indirect consumer control could introduce injection risk.
- repro: Not exploitable with current numeric-only values, but violates the defensive selector rule.
- proposed-fix: Use CSS.escape():
const el = root.querySelector<HTMLElement>([data-month-grid-cell][data-value="${CSS.escape(this.placeholderYear + '-' + nextMonth!)}"]); - verify: [downgraded] Selector at month-grid-provider.svelte.ts:247-248 reads:
root.querySelector<HTMLElement>([data-month-grid-cell][data-value="{this.placeholderYear}-{nextMonth!}"]). The interpolated parts are strictly numeric:placeholderYear = this.opts.placeholder.current.year(line 113,MonthPlaceholder.year: number) andnextMonthis a month clamped to 1..12 by the overflow logic at lines 224-233. Neither can carry CSS metacharacters, so this is NOT user-hittable and NOT CRITICAL — the candidate's own repro field admits 'Not exploitable with current numeric-only values'. It IS a real deviation from the project's own consistent discipline: radio-group:93, tabs:109, listbox:196, grid-list:184, command:394, tree-view:110, tree-grid:162/214 all wrap data-value interpolation in CSS.escape(). But the closest sibling year-grid-provider.svelte.ts:219 does the identical unescaped numeric interpolation ([data-year-grid-cell][data-value="${nextYear!}"]), confirming this is an intentional grid-family pattern for numeric coordinates, not an oversight unique to month-grid. Downgrade CRITICAL -> LOW: cosmetic consistency nit, zero behavioral/security impact. - fix-status: open
No-findings dimensions
B, D, E, E-bis, F, G
Theming facts (E-bis)
- magic z-index: none
- magic literals: none
- undeclared parts: none
- roles clean: true · variants clean: true
- conformance: All CSS tokens use var(--*) references; no hardcoded px/rem/em/%. All 8 of 9 canonical color roles used (primary, secondary, neutral, affirm, fulfill, risk, threat, loss). Sizes xs/sm/md/lg via responsive prop. Variants use ControlVariant. No bare z-index, opacity, or letter-spacing literals. data-size/variant/color are eidos-owned visual attrs, not violations.
Tests (F)
- exists: false · env: N/A
- covers:
- untested: keyboard navigation (all 10 keys: Arrow×4, Home/End, PageUp/PageDown, Enter/Space); year wrap-around on month overflow; roving focus with tabindex toggle; placeholder year changes; min/max value bounds enforcement; readonly mode; disabled state
Style observations (non-blocking)
- Excellent keyboard handling: all 10 morfo-declared keys implemented with correct overflow wrapping (months 0→12 of prev year; months 13→1 of next year)
- Roving focus implemented correctly: single tabindex=0 on focused cell, -1 on others
- Event emit sequence correct: state mutation pre-emits (commit-set on value.current change, shift-navigate-step on placeholder change)
- Heading id registration via direct assignment in constructor (A30 compliant, not $effect)
- No composition issues (N/A for month-grid; it's not a picker)
- CSS selector at line 248 uses numeric-safe values but lacks defensive CSS.escape() per rule C
- All 7 declared morfo parts registered via runtime.part(); no orphans
- 2-of-3 rule satisfied: all data-* attrs consumed by both soma and eidos
- No data-soma-, data-eidos-, or data-air-* naming violations
- No setTimeout/setInterval/listeners/observers → no A6 leak risk