@3fn/core 13.0.0 → 14.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.kiro/agents/ada-prompt.md +80 -132
- package/.kiro/agents/ada-prompt.md.attribution.json +45 -0
- package/.kiro/agents/ada.json +44 -59
- package/.kiro/agents/ada.json.attribution.json +13 -0
- package/.kiro/agents/data-prompt.md +83 -74
- package/.kiro/agents/data-prompt.md.attribution.json +53 -0
- package/.kiro/agents/data.json +31 -38
- package/.kiro/agents/data.json.attribution.json +13 -0
- package/.kiro/agents/kenya-prompt.md +83 -72
- package/.kiro/agents/kenya-prompt.md.attribution.json +53 -0
- package/.kiro/agents/kenya.json +27 -35
- package/.kiro/agents/kenya.json.attribution.json +13 -0
- package/.kiro/agents/leonardo-prompt.md +176 -234
- package/.kiro/agents/leonardo-prompt.md.attribution.json +45 -0
- package/.kiro/agents/leonardo.json +28 -36
- package/.kiro/agents/leonardo.json.attribution.json +13 -0
- package/.kiro/agents/lina-prompt.md +110 -151
- package/.kiro/agents/lina-prompt.md.attribution.json +53 -0
- package/.kiro/agents/lina.json +46 -59
- package/.kiro/agents/lina.json.attribution.json +13 -0
- package/.kiro/agents/sparky-prompt.md +89 -72
- package/.kiro/agents/sparky-prompt.md.attribution.json +53 -0
- package/.kiro/agents/sparky.json +37 -37
- package/.kiro/agents/sparky.json.attribution.json +13 -0
- package/.kiro/agents/stacy-prompt.md +74 -48
- package/.kiro/agents/stacy-prompt.md.attribution.json +45 -0
- package/.kiro/agents/stacy.json +28 -30
- package/.kiro/agents/stacy.json.attribution.json +13 -0
- package/.kiro/agents/thurgood-prompt.md +94 -138
- package/.kiro/agents/thurgood-prompt.md.attribution.json +45 -0
- package/.kiro/agents/thurgood.json +31 -35
- package/.kiro/agents/thurgood.json.attribution.json +13 -0
- package/.kiro/steering/AI-Collaboration-Principles.md +3 -3
- package/.kiro/steering/Civitas-System-Overview.md +7 -16
- package/.kiro/steering/DesignerPunk-Systems-Overview.md +6 -6
- package/.kiro/steering/Spec-Feedback-Protocol.md +2 -11
- package/.kiro/steering/Task-Completion-Protocol.md +98 -17
- package/.kiro/steering/core-goals.md +3 -3
- package/.kiro/steering/personal-note.md +1 -1
- package/.kiro/steering/start-up-tasks.md +17 -6
- package/application-mcp-server/src/index.ts +26 -0
- package/dist/ComponentTokens.android.kt +12 -12
- package/dist/ComponentTokens.ios.swift +12 -12
- package/dist/ComponentTokens.web.css +3 -3
- package/dist/DesignTokens.android.kt +1 -1
- package/dist/DesignTokens.dtcg.json +8 -5
- package/dist/DesignTokens.figma.json +2 -2
- package/dist/DesignTokens.ios.swift +1 -1
- package/dist/DesignTokens.web.css +1 -1
- package/dist/android/DesignTokens.android.kt +1 -1
- package/dist/blend/OklchBlendCalculator.js +1 -0
- package/dist/blend/ThemeAwareBlendUtilities.web.d.ts +13 -2
- package/dist/blend/ThemeAwareBlendUtilities.web.js +6 -1
- package/dist/browser/designerpunk.esm.js +36 -90
- package/dist/browser/designerpunk.esm.min.js +33 -36
- package/dist/browser/designerpunk.umd.js +36 -90
- package/dist/browser/designerpunk.umd.min.js +47 -50
- package/dist/browser/tokens.css +3 -3
- package/dist/build/tokens/defineComponentTokens.d.ts +10 -0
- package/dist/build/tokens/defineComponentTokens.js +26 -0
- package/dist/components/core/Avatar-Base/avatar.tokens.d.ts +21 -26
- package/dist/components/core/Avatar-Base/avatar.tokens.js +31 -34
- package/dist/components/core/Avatar-Base/index.d.ts +1 -1
- package/dist/components/core/Avatar-Base/index.js +2 -2
- package/dist/components/core/Avatar-Base/platforms/web/Avatar.web.js +24 -5
- package/dist/components/core/Button-CTA/examples/BasicUsage.d.ts +16 -28
- package/dist/components/core/Button-CTA/examples/BasicUsage.js +18 -43
- package/dist/components/core/Button-CTA/platforms/web/ButtonCTA.web.d.ts +3 -15
- package/dist/components/core/Button-CTA/platforms/web/ButtonCTA.web.js +9 -58
- package/dist/components/core/Button-CTA/types.d.ts +0 -24
- package/dist/components/core/Button-CTA/types.js +6 -0
- package/dist/components/core/Button-Icon/buttonIcon.tokens.d.ts +28 -14
- package/dist/components/core/Button-Icon/buttonIcon.tokens.js +35 -20
- package/dist/components/core/Input-Text-Base/types.d.ts +13 -1
- package/dist/components/core/Input-Text-Password/platforms/web/InputTextPassword.web.js +11 -2
- package/dist/generators/DTCGFormatGenerator.js +8 -0
- package/dist/generators/TokenFileGenerator.js +7 -2
- package/dist/integration/BuildErrorHandler.js +2 -2
- package/dist/ios/DesignTokens.ios.swift +1 -1
- package/dist/mcp/application-mcp.js +24 -0
- package/dist/mcp/docs-mcp.js +130 -15
- package/dist/mcp/product-mcp.js +25 -0
- package/dist/tokens/OpacityTokens.js +1 -1
- package/dist/tokens/component/progress.d.ts +65 -5
- package/dist/tokens/component/progress.js +79 -18
- package/dist/tokens/semantic/BlendTokens.d.ts +10 -3
- package/dist/tokens/semantic/BlendTokens.js +17 -5
- package/dist/tokens/semantic/OpacityTokens.d.ts +4 -4
- package/dist/tokens/semantic/OpacityTokens.js +4 -4
- package/dist/types/ComponentTypes.d.ts +1 -1
- package/dist/types/generated/TokenTypes.d.ts +1 -1
- package/dist/types/generated/TokenTypes.js +1 -1
- package/dist/validators/StemmaTokenUsageValidator.js +3 -2
- package/dist/web/DesignTokens.web.css +1 -1
- package/governance/BUILD-SYSTEM-SETUP.md +1 -2
- package/governance/Component-Development-Guide.md +23 -13
- package/governance/Component-Development-Standards.md +21 -20
- package/governance/Component-Family-Avatar.md +6 -7
- package/governance/Component-Family-Badge.md +19 -20
- package/governance/Component-Family-Button.md +30 -43
- package/governance/Component-Family-Chip.md +14 -15
- package/governance/Component-Family-Container.md +12 -13
- package/governance/Component-Family-Data-Display.md +1 -2
- package/governance/Component-Family-Divider.md +1 -2
- package/governance/Component-Family-Form-Inputs.md +65 -65
- package/governance/Component-Family-Icon.md +9 -10
- package/governance/Component-Family-Loading.md +1 -2
- package/governance/Component-Family-Modal.md +1 -2
- package/governance/Component-Family-Navigation.md +1 -2
- package/governance/Component-Family-Progress.md +0 -1
- package/governance/Component-Inheritance-Structures.md +219 -99
- package/governance/Component-MCP-Document-Template.md +6 -5
- package/governance/Component-Primitive-vs-Semantic-Philosophy.md +1 -1
- package/governance/Component-Quick-Reference.md +33 -33
- package/governance/Component-Readiness-Status.md +59 -44
- package/governance/Component-Templates.md +57 -61
- package/governance/Contract-System-Reference.md +7 -7
- package/governance/MCP-Integration-Guide.md +1 -1
- package/governance/Process-Cross-Reference-Standards.md +31 -13
- package/governance/Process-Development-Workflow.md +49 -59
- package/governance/Process-File-Organization.md +25 -28
- package/governance/Process-Hook-Operations.md +22 -11
- package/governance/Process-Orchestration-Model-Selection.md +92 -0
- package/governance/Process-Spec-Planning.md +98 -52
- package/governance/Process-Task-Type-Definitions.md +80 -4
- package/governance/Product-Handoff-Protocol.md +2 -0
- package/governance/Rosetta-System-Architecture.md +13 -11
- package/governance/Test-Behavioral-Contract-Validation.md +38 -31
- package/governance/Test-Failure-Audit-Methodology.md +1 -1
- package/governance/Token-Family-Accessibility.md +1 -2
- package/governance/Token-Family-Blend.md +18 -16
- package/governance/Token-Family-Blur.md +0 -1
- package/governance/Token-Family-Border.md +1 -2
- package/governance/Token-Family-Color.md +0 -1
- package/governance/Token-Family-Glow.md +1 -2
- package/governance/Token-Family-Layering.md +0 -1
- package/governance/Token-Family-Motion.md +1 -2
- package/governance/Token-Family-Opacity.md +0 -1
- package/governance/Token-Family-Radius.md +1 -2
- package/governance/Token-Family-Responsive.md +1 -2
- package/governance/Token-Family-Shadow.md +1 -2
- package/governance/Token-Family-Sizing.md +0 -1
- package/governance/Token-Family-Spacing.md +1 -2
- package/governance/Token-Family-Typography.md +1 -2
- package/governance/Token-Governance.md +8 -8
- package/governance/Token-Quick-Reference.md +47 -34
- package/governance/Token-Resolution-Patterns.md +1 -1
- package/governance/Token-Semantic-Structure.md +1 -1
- package/governance/Web-Authoring-Standards.md +5 -5
- package/governance/browser-distribution-guide.md +1 -4
- package/governance/classification-map.md +474 -0
- package/governance/completion-documentation-guide.md +23 -37
- package/governance/component-meta-authoring-guide.md +1 -1
- package/governance/cross-platform-vs-platform-specific-decision-framework.md +1 -1
- package/governance/platform-implementation-guidelines.md +2 -3
- package/governance/release-management-system.md +28 -63
- package/governance/rosetta-system-principles.md +8 -6
- package/governance/stemma-system-principles.md +18 -17
- package/mcp-server/src/index.ts +24 -6
- package/mcp-server/src/indexer/DocumentIndexer.ts +119 -9
- package/mcp-server/src/indexer/__tests__/bare-id-crossrefs.test.ts +250 -0
- package/mcp-server/src/indexer/cross-ref-parser.ts +29 -1
- package/mcp-server/src/indexer/index-health.ts +27 -2
- package/mcp-server/src/query/__tests__/find-docs-calibration.test.ts +11 -26
- package/mcp-server/src/relocation-integrity-gate/__tests__/relocation-integrity-gate.test.ts +72 -5
- package/mcp-server/src/relocation-integrity-gate/relocation-integrity-gate.ts +81 -24
- package/mcp-server/src/tools/list-cross-references.ts +2 -2
- package/package.json +24 -26
- package/src/__tests__/browser-distribution/css-bundling.test.ts +6 -4
- package/src/__tests__/console-allowlist.json +14 -0
- package/src/__tests__/console-fail-setup.ts +169 -0
- package/src/__tests__/integration/Spec107-DesignLanguageContext.test.ts +16 -0
- package/src/__tests__/stemma-system/behavioral-contract-validation.test.ts +70 -17
- package/src/__tests__/stemma-system/contract-catalog-name-validation.test.ts +28 -0
- package/src/__tests__/stemma-system/form-inputs-contracts.test.ts +223 -16
- package/src/__tests__/stemma-system/input-text-native-base-call-alignment.test.ts +298 -0
- package/src/blend/OklchBlendCalculator.ts +3 -0
- package/src/blend/ThemeAwareBlendUtilities.android.kt +3 -0
- package/src/blend/ThemeAwareBlendUtilities.ios.swift +3 -0
- package/src/blend/ThemeAwareBlendUtilities.web.ts +9 -1
- package/src/blend/__tests__/InteractionStateAudit.test.ts +12 -9
- package/src/build/errors/__tests__/ErrorHandler.integration.test.ts +8 -0
- package/src/build/errors/__tests__/ErrorHandler.test.ts +5 -0
- package/src/build/tokens/__tests__/defineComponentTokens.test.ts +113 -0
- package/src/build/tokens/defineComponentTokens.ts +43 -1
- package/src/build/workflow/__tests__/CICDIntegration.test.ts +12 -1
- package/src/cli/__tests__/init.test.ts +45 -11
- package/src/components/core/Avatar-Base/Avatar-Base.schema.yaml +1 -1
- package/src/components/core/Avatar-Base/__tests__/Avatar.accessibility.test.ts +121 -7
- package/src/components/core/Avatar-Base/__tests__/Avatar.image.test.ts +3 -0
- package/src/components/core/Avatar-Base/__tests__/Avatar.test.ts +15 -6
- package/src/components/core/Avatar-Base/avatar.tokens.ts +31 -34
- package/src/components/core/Avatar-Base/contracts.yaml +11 -1
- package/src/components/core/Avatar-Base/index.ts +1 -1
- package/src/components/core/Avatar-Base/platforms/web/Avatar.web.ts +24 -5
- package/src/components/core/Badge-Count-Base/contracts.yaml +1 -1
- package/src/components/core/Badge-Label-Base/contracts.yaml +1 -1
- package/src/components/core/Button-CTA/Button-CTA.schema.yaml +2 -12
- package/src/components/core/Button-CTA/README.md +3 -6
- package/src/components/core/Button-CTA/__tests__/ButtonCTA.test.ts +35 -89
- package/src/components/core/Button-CTA/__tests__/setup.test.ts +0 -2
- package/src/components/core/Button-CTA/__tests__/test-utils.ts +0 -2
- package/src/components/core/Button-CTA/contracts.yaml +6 -29
- package/src/components/core/Button-CTA/examples/BasicUsage.html +2 -14
- package/src/components/core/Button-CTA/examples/BasicUsage.tsx +17 -44
- package/src/components/core/Button-CTA/platforms/android/ButtonCTA.android.kt +12 -20
- package/src/components/core/Button-CTA/platforms/ios/ButtonCTA.ios.swift +12 -51
- package/src/components/core/Button-CTA/platforms/web/ButtonCTA.web.css +2 -26
- package/src/components/core/Button-CTA/platforms/web/ButtonCTA.web.ts +18 -71
- package/src/components/core/Button-CTA/types.ts +10 -28
- package/src/components/core/Button-Icon/buttonIcon.tokens.ts +43 -27
- package/src/components/core/Chip-Base/__tests__/ChipBase.test.ts +13 -0
- package/src/components/core/Chip-Filter/__tests__/ChipFilter.test.ts +13 -0
- package/src/components/core/Chip-Input/__tests__/ChipInput.test.ts +13 -0
- package/src/components/core/Input-Text-Base/Input-Text-Base.schema.yaml +30 -2
- package/src/components/core/Input-Text-Base/README.md +25 -2
- package/src/components/core/Input-Text-Base/__tests__/focusIndicators.test.ts +16 -15
- package/src/components/core/Input-Text-Base/contracts.yaml +90 -0
- package/src/components/core/Input-Text-Base/platforms/android/InputTextBase.android.kt +26 -12
- package/src/components/core/Input-Text-Base/platforms/ios/InputTextBase.ios.swift +195 -59
- package/src/components/core/Input-Text-Base/types.ts +13 -1
- package/src/components/core/Input-Text-Email/Input-Text-Email.schema.yaml +5 -1
- package/src/components/core/Input-Text-Email/README.md +8 -7
- package/src/components/core/Input-Text-Email/platforms/android/InputTextEmail.android.kt +1 -4
- package/src/components/core/Input-Text-Email/platforms/ios/InputTextEmail.ios.swift +2 -16
- package/src/components/core/Input-Text-Password/Input-Text-Password.schema.yaml +10 -3
- package/src/components/core/Input-Text-Password/README.md +9 -8
- package/src/components/core/Input-Text-Password/contracts.yaml +5 -0
- package/src/components/core/Input-Text-Password/platforms/android/InputTextPassword.android.kt +17 -7
- package/src/components/core/Input-Text-Password/platforms/ios/InputTextPassword.ios.swift +22 -20
- package/src/components/core/Input-Text-Password/platforms/web/InputTextPassword.web.ts +11 -2
- package/src/components/core/Input-Text-PhoneNumber/Input-Text-PhoneNumber.schema.yaml +5 -1
- package/src/components/core/Input-Text-PhoneNumber/README.md +9 -8
- package/src/components/core/Input-Text-PhoneNumber/platforms/android/InputTextPhoneNumber.android.kt +2 -5
- package/src/components/core/Input-Text-PhoneNumber/platforms/ios/InputTextPhoneNumber.ios.swift +3 -17
- package/src/components/core/Nav-Header-App/contracts.yaml +1 -1
- package/src/components/core/Nav-SegmentedChoice-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Connector-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Label-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Indicator-Node-Base/contracts.yaml +1 -1
- package/src/components/core/Progress-Stepper-Base/__tests__/StepperBase.test.ts +5 -2
- package/src/components/core/Progress-Stepper-Detailed/__tests__/StepperDetailed.test.ts +5 -2
- package/src/generators/DTCGFormatGenerator.ts +6 -0
- package/src/generators/TokenFileGenerator.ts +7 -2
- package/src/generators/__tests__/DTCGConfigOptions.test.ts +14 -5
- package/src/integration/BuildErrorHandler.ts +2 -2
- package/src/tokens/OpacityTokens.ts +1 -1
- package/src/tokens/__tests__/OpacityTokens.test.ts +3 -1
- package/src/tokens/__tests__/ProgressTokenCompliance.test.ts +5 -3
- package/src/tokens/__tests__/ProgressTokenFormulas.test.ts +11 -11
- package/src/tokens/__tests__/ProgressTokenTranslation.test.ts +22 -20
- package/src/tokens/component/progress.ts +83 -21
- package/src/tokens/semantic/BlendTokens.ts +26 -5
- package/src/tokens/semantic/OpacityTokens.ts +4 -4
- package/src/types/ComponentTypes.ts +1 -1
- package/src/types/generated/TokenTypes.ts +1 -1
- package/src/validators/StemmaTokenUsageValidator.ts +3 -2
- package/token-index/components.yaml +8 -8
- package/token-index/semantics.yaml +1 -2
- package/src/tools/release/__tests__/ChangeClassifier.test.ts +0 -133
- package/src/tools/release/__tests__/ChangeExtractor.test.ts +0 -222
- package/src/tools/release/__tests__/GitHubPublisher.test.ts +0 -240
- package/src/tools/release/__tests__/NotesRenderer.test.ts +0 -142
- package/src/tools/release/__tests__/NpmPublisher.test.ts +0 -289
- package/src/tools/release/__tests__/PipelineIntegration.test.ts +0 -188
- package/src/tools/release/__tests__/ReleasePipeline.test.ts +0 -192
- package/src/tools/release/__tests__/SemanticVersionValidator.test.ts +0 -49
- package/src/tools/release/__tests__/SummaryScanner.test.ts +0 -141
- package/src/tools/release/__tests__/TagResolver.test.ts +0 -91
- package/src/tools/release/__tests__/VersionCalculator.test.ts +0 -270
- package/src/tools/release/__tests__/helpers/NpmMockHelper.ts +0 -80
- package/src/tools/release/cli/ReleasePipeline.ts +0 -165
- package/src/tools/release/cli/release-tool.ts +0 -107
- package/src/tools/release/pipeline/ChangeClassifier.ts +0 -61
- package/src/tools/release/pipeline/ChangeExtractor.ts +0 -87
- package/src/tools/release/pipeline/NotesRenderer.ts +0 -66
- package/src/tools/release/pipeline/SummaryScanner.ts +0 -70
- package/src/tools/release/pipeline/TagResolver.ts +0 -40
- package/src/tools/release/pipeline/VersionCalculator.ts +0 -375
- package/src/tools/release/publishers/GitHubPublisher.ts +0 -228
- package/src/tools/release/publishers/NpmPublisher.ts +0 -196
- package/src/tools/release/release-config.json +0 -5
- package/src/tools/release/types/index.ts +0 -282
- package/src/tools/release/validators/SemanticVersionValidator.ts +0 -67
|
@@ -323,33 +323,83 @@ describe('Behavioral Contract Validation Suite', () => {
|
|
|
323
323
|
});
|
|
324
324
|
|
|
325
325
|
describe('WCAG Reference Validation', () => {
|
|
326
|
+
/**
|
|
327
|
+
* NORMATIVE WCAG-REQUIRED MATCHER — Spec 125-B U2, Task 4.2 (audit) → 4.3 (arm).
|
|
328
|
+
*
|
|
329
|
+
* Copied VERBATIM from the Task 4.2 audit matcher:
|
|
330
|
+
* .kiro/specs/125-B-classification-map/completion/u2/wcag-required-matcher.ts
|
|
331
|
+
*
|
|
332
|
+
* CONTINUITY CONTRACT (Req 12.3 / LINA tasks-R1): Task 4.2's pre-arm WCAG audit
|
|
333
|
+
* enumerated the corpus THROUGH this exact function (and the same COMPONENTS
|
|
334
|
+
* loader below), so "audit-clean ⇒ arm-green by construction" holds ONLY while
|
|
335
|
+
* this predicate stays unmodified. ANY change to it RE-OPENS the 4.2 audit —
|
|
336
|
+
* do not "improve" it here.
|
|
337
|
+
*
|
|
338
|
+
* Replaces the legacy DORMANT six-name trigger (armed-but-aimed-at-6-retired
|
|
339
|
+
* legacy contract names — Spec 063 renamed them to canonical
|
|
340
|
+
* `{category}_{concept}` form; this check never matched the renamed names).
|
|
341
|
+
*/
|
|
342
|
+
const WCAG_REQUIRED_EXACT: ReadonlySet<string> = new Set([
|
|
343
|
+
'interaction_focusable',
|
|
344
|
+
'interaction_focus_ring',
|
|
345
|
+
'state_disabled',
|
|
346
|
+
'state_error',
|
|
347
|
+
]);
|
|
348
|
+
|
|
349
|
+
function isWcagRequiredContract(contractName: string): boolean {
|
|
350
|
+
if (WCAG_REQUIRED_EXACT.has(contractName)) return true;
|
|
351
|
+
if (contractName.startsWith('accessibility_')) return true;
|
|
352
|
+
if (contractName.startsWith('content_') && contractName.endsWith('_label')) return true;
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
326
356
|
it('accessibility-related contracts should have WCAG references', () => {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
357
|
+
// Match-count floor (Req 12.4 / DD3): the DORMANT lesson made structural —
|
|
358
|
+
// an empty (or stale) selection fails the check itself.
|
|
359
|
+
let totalSelected = 0;
|
|
360
|
+
const perLiteralCounts: Record<string, number> = {
|
|
361
|
+
interaction_focusable: 0,
|
|
362
|
+
interaction_focus_ring: 0,
|
|
363
|
+
state_error: 0,
|
|
364
|
+
};
|
|
365
|
+
|
|
336
366
|
for (const component of COMPONENTS) {
|
|
337
367
|
const contracts = loadComponentContracts(component);
|
|
338
|
-
|
|
368
|
+
|
|
339
369
|
if (!contracts) continue;
|
|
340
|
-
|
|
370
|
+
|
|
341
371
|
for (const [contractName, contract] of Object.entries(contracts)) {
|
|
342
|
-
if (
|
|
372
|
+
if (isWcagRequiredContract(contractName)) {
|
|
373
|
+
totalSelected++;
|
|
374
|
+
if (contractName in perLiteralCounts) {
|
|
375
|
+
perLiteralCounts[contractName]++;
|
|
376
|
+
}
|
|
377
|
+
|
|
343
378
|
const hasWcag = contract.wcag && contract.wcag.length > 0;
|
|
344
|
-
|
|
379
|
+
|
|
345
380
|
if (!hasWcag) {
|
|
346
381
|
console.log(`${component}.${contractName}: Missing WCAG reference`);
|
|
347
382
|
}
|
|
348
|
-
|
|
383
|
+
|
|
349
384
|
expect(hasWcag).toBe(true);
|
|
350
385
|
}
|
|
351
386
|
}
|
|
352
387
|
}
|
|
388
|
+
|
|
389
|
+
// Aggregate floor (DD3): non-empty selection (audited at 69 on the current
|
|
390
|
+
// corpus — Task 4.2 adjudication table).
|
|
391
|
+
expect(totalSelected).toBeGreaterThan(0);
|
|
392
|
+
|
|
393
|
+
// Per-literal presence floor (DD3), THREE literals per Peter's 2026-07-14
|
|
394
|
+
// amendment — NOT the four DD3 originally recorded. `state_disabled` is
|
|
395
|
+
// EXCLUDED from this floor pending the Button-CTA disabled-state
|
|
396
|
+
// adjudication (the matcher itself is unchanged: state_disabled contracts
|
|
397
|
+
// are still selected above and still must carry a valid wcag ref; only the
|
|
398
|
+
// per-literal PRESENCE floor omits it). See:
|
|
399
|
+
// .kiro/specs/125-B-classification-map/completion/u2/stemma-pre-arm-adjudication.md § 7
|
|
400
|
+
expect(perLiteralCounts.interaction_focusable).toBeGreaterThan(0);
|
|
401
|
+
expect(perLiteralCounts.interaction_focus_ring).toBeGreaterThan(0);
|
|
402
|
+
expect(perLiteralCounts.state_error).toBeGreaterThan(0);
|
|
353
403
|
});
|
|
354
404
|
|
|
355
405
|
it('WCAG references should follow standard format', () => {
|
|
@@ -430,9 +480,12 @@ describe('Behavioral Contract Validation Suite', () => {
|
|
|
430
480
|
console.log(`\nValidation Criteria Summary:`);
|
|
431
481
|
console.log(` With validation: ${contractsWithValidation}`);
|
|
432
482
|
console.log(` Without validation: ${contractsWithoutValidation}`);
|
|
433
|
-
|
|
434
|
-
//
|
|
435
|
-
|
|
483
|
+
|
|
484
|
+
// Promoted (Req 12.6 / DD4, audit-first — Task 4.2's inventory found the
|
|
485
|
+
// corpus already clean: 234 non-inherited contracts, 0 without validation;
|
|
486
|
+
// zero fixes, zero escalations needed). Inherited-contract skip preserved
|
|
487
|
+
// above. DD4: no exemption mechanism — fix-all, escalate-if-candidate.
|
|
488
|
+
expect(contractsWithoutValidation).toBe(0);
|
|
436
489
|
});
|
|
437
490
|
|
|
438
491
|
it('validation criteria should include testable assertions', () => {
|
|
@@ -26,6 +26,19 @@ const CATALOG_PATH = path.join(process.cwd(), 'governance/Contract-System-Refere
|
|
|
26
26
|
const BASELINE_CONCEPT_COUNT = 116;
|
|
27
27
|
const EXPECTED_CATEGORY_COUNT = 10;
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Contracts authored ahead of Concept Catalog ratification (ballot pending).
|
|
31
|
+
*
|
|
32
|
+
* Each entry MUST reference the ruling that authorized authoring the contract
|
|
33
|
+
* before the concept ballot ratified. This allowlist is SELF-CLEANING: once
|
|
34
|
+
* the concept lands in the catalog (ballot RATIFIED), the validation below
|
|
35
|
+
* fails with a removal instruction — a stale entry cannot linger.
|
|
36
|
+
*/
|
|
37
|
+
const PENDING_BALLOT_CONCEPTS: Record<string, string> = {
|
|
38
|
+
// (empty — state_readonly ratified and landed in the catalog, 2026-07-15;
|
|
39
|
+
// add entries here only for contracts whose concept ballot is still pending)
|
|
40
|
+
};
|
|
41
|
+
|
|
29
42
|
/**
|
|
30
43
|
* Parse the Concept Catalog from Contract-System-Reference.md.
|
|
31
44
|
*
|
|
@@ -145,6 +158,21 @@ describe('Contract Catalog Name Validation', () => {
|
|
|
145
158
|
);
|
|
146
159
|
}
|
|
147
160
|
|
|
161
|
+
// Pending-ballot carve-out (self-cleaning): a contract authored ahead
|
|
162
|
+
// of catalog ratification passes only while the concept is absent
|
|
163
|
+
// from the catalog. The moment the ballot ratifies and the concept
|
|
164
|
+
// lands in the catalog, the stale allowlist entry fails the test.
|
|
165
|
+
if (contractKey in PENDING_BALLOT_CONCEPTS) {
|
|
166
|
+
if (catalogCategory.has(concept)) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Contract '${contractKey}' is now in the Concept Catalog — its ballot has ` +
|
|
169
|
+
`ratified. Remove it from PENDING_BALLOT_CONCEPTS. ` +
|
|
170
|
+
`(Entry: ${PENDING_BALLOT_CONCEPTS[contractKey]})`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
return; // valid category + documented pending ballot
|
|
174
|
+
}
|
|
175
|
+
|
|
148
176
|
if (!catalogCategory.has(concept)) {
|
|
149
177
|
throw new Error(
|
|
150
178
|
`Component ${componentName}: contract '${contractKey}' has unrecognized concept '${concept}' in category '${category}'`
|
|
@@ -93,22 +93,8 @@ const CONTRACT_PATTERNS: Record<string, Record<string, RegExp[]>> = {
|
|
|
93
93
|
/errorMessage/g,
|
|
94
94
|
],
|
|
95
95
|
},
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
/disabled/gi,
|
|
99
|
-
/aria-disabled/g,
|
|
100
|
-
/cursor:\s*not-allowed/g,
|
|
101
|
-
],
|
|
102
|
-
ios: [
|
|
103
|
-
/disabled/gi,
|
|
104
|
-
/isDisabled/g,
|
|
105
|
-
],
|
|
106
|
-
android: [
|
|
107
|
-
/enabled/gi,
|
|
108
|
-
/isEnabled/g,
|
|
109
|
-
/disabled/gi,
|
|
110
|
-
],
|
|
111
|
-
},
|
|
96
|
+
// NOTE: disabled_state is a standardized EXCLUSION (no-disabled-states philosophy).
|
|
97
|
+
// Implementation-level guard patterns live in DISABLED_EXCLUSION_GUARD_PATTERNS below.
|
|
112
98
|
focus_ring: {
|
|
113
99
|
web: [
|
|
114
100
|
/:focus-visible/g,
|
|
@@ -141,6 +127,22 @@ const CONTRACT_PATTERNS: Record<string, Record<string, RegExp[]>> = {
|
|
|
141
127
|
},
|
|
142
128
|
};
|
|
143
129
|
|
|
130
|
+
// Disabled-state exclusion guard (no-disabled-states philosophy, Spec 066 / Button-CTA pattern).
|
|
131
|
+
// These patterns must NOT appear in any Form Inputs platform implementation.
|
|
132
|
+
// The iOS `.disabled(` pattern was added per the iOS readOnly adjudication
|
|
133
|
+
// (.kiro/issues/input-text-base-ios-readonly-adjudication.md — RULED B-prime,
|
|
134
|
+
// Peter 2026-07-15, condition 5): iOS previously implemented readOnly via
|
|
135
|
+
// SwiftUI .disabled(readOnly), which the original patterns missed — that is
|
|
136
|
+
// how a "dimmed" readOnly field survived two disabled-cleanup passes. There
|
|
137
|
+
// are zero legitimate `.disabled(` uses in Form Inputs platform sources.
|
|
138
|
+
const DISABLED_EXCLUSION_GUARD_PATTERNS: RegExp[] = [
|
|
139
|
+
/isDisabled/,
|
|
140
|
+
/disabledBlend/,
|
|
141
|
+
/aria-disabled/,
|
|
142
|
+
/cursor:\s*not-allowed/,
|
|
143
|
+
/\.disabled\(/, // SwiftUI disabled modifier — disabled semantics regardless of intent
|
|
144
|
+
];
|
|
145
|
+
|
|
144
146
|
/**
|
|
145
147
|
* Load component schema
|
|
146
148
|
*/
|
|
@@ -371,6 +373,211 @@ describe('Form Inputs Family Behavioral Contracts', () => {
|
|
|
371
373
|
expect(content).toContain('state_disabled');
|
|
372
374
|
expect(content).toContain('excludes');
|
|
373
375
|
});
|
|
376
|
+
|
|
377
|
+
it('no platform implementation should contain disabled-state handling', () => {
|
|
378
|
+
// Exclusion guard (Spec 066 / Button-CTA pattern): the contracts.yaml
|
|
379
|
+
// exclusion must hold in code. readOnly is the documented alternative
|
|
380
|
+
// and must never be implemented via disabled semantics — iOS renders
|
|
381
|
+
// readOnly as selectable Text in the field chrome (B-prime ruling,
|
|
382
|
+
// 2026-07-15), never via .disabled(readOnly).
|
|
383
|
+
for (const formComponent of FORM_INPUT_COMPONENTS) {
|
|
384
|
+
for (const platform of ['web', 'ios', 'android']) {
|
|
385
|
+
const content = loadPlatformImpl(formComponent, platform);
|
|
386
|
+
if (!content) continue;
|
|
387
|
+
|
|
388
|
+
// Raw-source matching (strict): banned patterns may not appear
|
|
389
|
+
// anywhere in a platform implementation, comments included —
|
|
390
|
+
// reference the mechanism descriptively in docs, never literally.
|
|
391
|
+
const violations = DISABLED_EXCLUSION_GUARD_PATTERNS
|
|
392
|
+
.filter(pattern => pattern.test(content))
|
|
393
|
+
.map(pattern => pattern.source);
|
|
394
|
+
|
|
395
|
+
if (violations.length > 0) {
|
|
396
|
+
console.log(`${formComponent} (${platform}) disabled-state handling found: ${violations.join(', ')}`);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
expect(violations).toEqual([]);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
describe('state_readonly contract (iOS readOnly adjudication, RULED B-prime 2026-07-15)', () => {
|
|
406
|
+
// @see .kiro/issues/input-text-base-ios-readonly-adjudication.md
|
|
407
|
+
// These are the first executable readOnly assertions anywhere in the
|
|
408
|
+
// component (pre-ruling coverage was zero on all platforms).
|
|
409
|
+
|
|
410
|
+
it('Input-Text-Base contracts.yaml declares state_readonly', () => {
|
|
411
|
+
const contracts = loadContracts('Input-Text-Base');
|
|
412
|
+
expect(contracts).not.toBeNull();
|
|
413
|
+
expect(contracts!.state_readonly).toBeDefined();
|
|
414
|
+
expect(contracts!.state_readonly.category).toBe('state');
|
|
415
|
+
expect(contracts!.state_readonly.required).toBe(true);
|
|
416
|
+
expect(contracts!.state_readonly.platforms).toEqual(['web', 'ios', 'android']);
|
|
417
|
+
// The contract must pin "never disabled semantics" — the invariant
|
|
418
|
+
// the original implementation violated.
|
|
419
|
+
expect(contracts!.state_readonly.behavior).toMatch(/disabled/i);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('Input-Text-Base interaction_focusable declares the iOS readOnly carve-out', () => {
|
|
423
|
+
const contractsPath = path.join(COMPONENTS_DIR, 'Input-Text-Base', 'contracts.yaml');
|
|
424
|
+
const content = fs.readFileSync(contractsPath, 'utf-8');
|
|
425
|
+
const parsed = yaml.load(content) as Record<string, any>;
|
|
426
|
+
const focusable = parsed.contracts.interaction_focusable;
|
|
427
|
+
|
|
428
|
+
// Declared, never silent: iOS carve-out + affirmative web/Android
|
|
429
|
+
// focus-order participation (adjudication condition 1).
|
|
430
|
+
expect(focusable.behavior).toMatch(/iOS readOnly carve-out/i);
|
|
431
|
+
expect(focusable.behavior).toMatch(/mitigable declared exception/i);
|
|
432
|
+
expect(focusable.behavior).toMatch(/REMAIN in\s+focus order/);
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
it('Input-Text-Password contracts.yaml excludes state_readonly (security fork deleted)', () => {
|
|
436
|
+
const contractsPath = path.join(COMPONENTS_DIR, 'Input-Text-Password', 'contracts.yaml');
|
|
437
|
+
const content = fs.readFileSync(contractsPath, 'utf-8');
|
|
438
|
+
const parsed = yaml.load(content) as Record<string, any>;
|
|
439
|
+
|
|
440
|
+
expect(parsed.excludes).toBeDefined();
|
|
441
|
+
expect(parsed.excludes.state_readonly).toBeDefined();
|
|
442
|
+
expect(parsed.excludes.state_readonly.reason).toMatch(/security/i);
|
|
443
|
+
expect(parsed.excludes.state_readonly.reference).toMatch(
|
|
444
|
+
/input-text-base-ios-readonly-adjudication/
|
|
445
|
+
);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
it('no Input-Text iOS implementation contains .disabled( anywhere', () => {
|
|
449
|
+
for (const formComponent of FORM_INPUT_COMPONENTS) {
|
|
450
|
+
const content = loadPlatformImpl(formComponent, 'ios');
|
|
451
|
+
if (!content) continue;
|
|
452
|
+
|
|
453
|
+
// Raw-source assertion, comments included: this is how
|
|
454
|
+
// .disabled(readOnly) survived two disabled-cleanup passes.
|
|
455
|
+
expect(content).not.toMatch(/\.disabled\(/);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('iOS readOnly path renders selectable Text inside the shared field chrome', () => {
|
|
460
|
+
const content = loadPlatformImpl('Input-Text-Base', 'ios');
|
|
461
|
+
expect(content).not.toBeNull();
|
|
462
|
+
|
|
463
|
+
// B-prime: Text(value).textSelection(.enabled) wrapped by the
|
|
464
|
+
// extracted chrome modifier, shared with the editable path.
|
|
465
|
+
expect(content).toMatch(/\.textSelection\(\.enabled\)/);
|
|
466
|
+
expect(content).toMatch(/InputTextBaseFieldChrome/);
|
|
467
|
+
// The chrome must reserve min-height from the typographyInput
|
|
468
|
+
// line-height token unconditionally on both paths (Kenya hardening).
|
|
469
|
+
expect(content).toMatch(/typographyInput\.lineHeight/);
|
|
470
|
+
expect(content).toMatch(/minHeight/);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('iOS readOnly Text path is unreachable for secure fields (SECURITY gate)', () => {
|
|
474
|
+
const content = loadPlatformImpl('Input-Text-Base', 'ios');
|
|
475
|
+
expect(content).not.toBeNull();
|
|
476
|
+
|
|
477
|
+
// The gate: the read-only display path must exclude .password so a
|
|
478
|
+
// secure value can never render as selectable plaintext Text.
|
|
479
|
+
expect(content).toMatch(/readOnly\s*&&\s*type\s*!=\s*\.password/);
|
|
480
|
+
// And the rendering branch must key off the gated property, not the
|
|
481
|
+
// raw readOnly prop.
|
|
482
|
+
expect(content).toMatch(/if\s+isReadOnlyDisplay/);
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
it('iOS Input-Text-Password never forwards readOnly to the base', () => {
|
|
486
|
+
const content = loadPlatformImpl('Input-Text-Password', 'ios');
|
|
487
|
+
expect(content).not.toBeNull();
|
|
488
|
+
|
|
489
|
+
// Both branches (masked SecureField and revealed .text) must strip
|
|
490
|
+
// readOnly — the revealed branch would otherwise route a secret to
|
|
491
|
+
// the base's plaintext Text path.
|
|
492
|
+
const forwarded = content!.match(/readOnly:\s*readOnly/g);
|
|
493
|
+
expect(forwarded).toBeNull();
|
|
494
|
+
|
|
495
|
+
const stripped = content!.match(/readOnly:\s*false/g);
|
|
496
|
+
expect(stripped).not.toBeNull();
|
|
497
|
+
expect(stripped!.length).toBeGreaterThanOrEqual(2);
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('iOS composes a read-only accessibility indication (never a dimmed trait)', () => {
|
|
501
|
+
const content = loadPlatformImpl('Input-Text-Base', 'ios');
|
|
502
|
+
expect(content).not.toBeNull();
|
|
503
|
+
|
|
504
|
+
// Composed label + value + read-only hint (Kenya R1: iOS has no
|
|
505
|
+
// read-only trait; the hint is the announcement route).
|
|
506
|
+
expect(content).toMatch(/Read only/);
|
|
507
|
+
expect(content).toMatch(/accessibilityHint/);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('web implements readOnly via the native readonly attribute', () => {
|
|
511
|
+
const content = loadPlatformImpl('Input-Text-Base', 'web');
|
|
512
|
+
expect(content).not.toBeNull();
|
|
513
|
+
|
|
514
|
+
// Native readonly attribute = native semantics (implicit
|
|
515
|
+
// aria-readonly), focus-order participation, selectable content.
|
|
516
|
+
expect(content).toMatch(/readOnly\s*\?\s*'readonly'/);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
it('Android passes readOnly through to the text field without disabled semantics', () => {
|
|
520
|
+
const content = loadPlatformImpl('Input-Text-Base', 'android');
|
|
521
|
+
expect(content).not.toBeNull();
|
|
522
|
+
|
|
523
|
+
// Compose readOnly keeps the field focusable/selectable/copyable.
|
|
524
|
+
expect(content).toMatch(/readOnly\s*=\s*readOnly/);
|
|
525
|
+
// Never enabled=false — the only Compose mechanism that would remove
|
|
526
|
+
// a readOnly field from focus order is banned disabled semantics.
|
|
527
|
+
expect(content).not.toMatch(/enabled\s*=\s*false/);
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
it('Android sets TalkBack stateDescription("read only") conditionally on readOnly', () => {
|
|
531
|
+
const content = loadPlatformImpl('Input-Text-Base', 'android');
|
|
532
|
+
expect(content).not.toBeNull();
|
|
533
|
+
|
|
534
|
+
// Announcement parity (adjudication condition 3): explicit
|
|
535
|
+
// stateDescription in the semantics block, gated on readOnly —
|
|
536
|
+
// never announced on editable fields, never a disabled/dimmed state.
|
|
537
|
+
expect(content).toMatch(
|
|
538
|
+
/if\s*\(readOnly\)\s*\{[^}]*stateDescription\s*=\s*"read only"/
|
|
539
|
+
);
|
|
540
|
+
// And the semantics property must actually be wired up (import), not
|
|
541
|
+
// just mentioned in a comment.
|
|
542
|
+
expect(content).toMatch(/import\s+androidx\.compose\.ui\.semantics\.stateDescription/);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('Android Input-Text-Password never forwards readOnly to the base', () => {
|
|
546
|
+
const content = loadPlatformImpl('Input-Text-Password', 'android');
|
|
547
|
+
expect(content).not.toBeNull();
|
|
548
|
+
|
|
549
|
+
// readOnly is contracted out of the Password variant (adjudication
|
|
550
|
+
// condition 4). On Android this is contract conformance, not a
|
|
551
|
+
// security fix — the masked PasswordVisualTransformation already
|
|
552
|
+
// suppresses copy/cut ([DATA R2] §3) — but the exclusion must hold
|
|
553
|
+
// uniformly: the prop is accepted for API stability and stripped.
|
|
554
|
+
const forwarded = content!.match(/readOnly\s*=\s*readOnly/g);
|
|
555
|
+
expect(forwarded).toBeNull();
|
|
556
|
+
|
|
557
|
+
const stripped = content!.match(/readOnly\s*=\s*false/g);
|
|
558
|
+
expect(stripped).not.toBeNull();
|
|
559
|
+
expect(stripped!.length).toBeGreaterThanOrEqual(1);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it('web Input-Text-Password never forwards read-only to the base', () => {
|
|
563
|
+
const content = loadPlatformImpl('Input-Text-Password', 'web');
|
|
564
|
+
expect(content).not.toBeNull();
|
|
565
|
+
|
|
566
|
+
// readOnly is contracted out of the Password variant (adjudication
|
|
567
|
+
// condition 4). On web this is contract conformance, not a security
|
|
568
|
+
// fix — a readonly password input stays masked and browsers block
|
|
569
|
+
// copy from type=password — but the exclusion holds uniformly on all
|
|
570
|
+
// three platforms.
|
|
571
|
+
// The render template must not interpolate read-only onto the inner
|
|
572
|
+
// input-text-base, and no code path may read the attribute.
|
|
573
|
+
expect(content).not.toMatch(/\$\{readOnly/);
|
|
574
|
+
expect(content).not.toMatch(/hasAttribute\(\s*['"]read-only['"]\s*\)/);
|
|
575
|
+
|
|
576
|
+
// The attribute stays accepted (observedAttributes) for API
|
|
577
|
+
// stability, documented as ignored with the adjudication reference.
|
|
578
|
+
expect(content).toMatch(/'read-only',/);
|
|
579
|
+
expect(content).toMatch(/input-text-base-ios-readonly-adjudication/);
|
|
580
|
+
});
|
|
374
581
|
});
|
|
375
582
|
});
|
|
376
583
|
|