fuaran-py 0.0.1__tar.gz
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.
- fuaran_py-0.0.1/.gitattributes +31 -0
- fuaran_py-0.0.1/.github/workflows/ci.yml +150 -0
- fuaran_py-0.0.1/.github/workflows/publish-pypi.yml +64 -0
- fuaran_py-0.0.1/.gitignore +32 -0
- fuaran_py-0.0.1/CLAUDE.md +307 -0
- fuaran_py-0.0.1/CODE_OF_CONDUCT.md +77 -0
- fuaran_py-0.0.1/CONTRIBUTING.md +32 -0
- fuaran_py-0.0.1/LICENSE +215 -0
- fuaran_py-0.0.1/NOTICE +16 -0
- fuaran_py-0.0.1/PKG-INFO +508 -0
- fuaran_py-0.0.1/README.md +483 -0
- fuaran_py-0.0.1/SECURITY.md +29 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-accept-full.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-accept-int-for-float.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-accept-minimal.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-enum.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-float-range.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-int-range.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-missing-required.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-string-len.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-type-mismatch.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-ans-reject-undeclared.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-full.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-minimal.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-answered.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-declined.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-reject-empty-id.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-reject-missing-answer.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-reject-stray-key.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-reject-unknown-type.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-superseded-anon.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-superseded-by.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-out-timedout.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-bad-space.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-bad-timeout.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-bad-version.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-default-nonconformant.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-duplicate-field.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-empty-contract.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-empty-id.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-missing-version.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-stray-key.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-tree-decode.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/elicitation/elc-reject-unknown-node.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-foreign-major.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-foreign-namespace.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-known-current.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-unknown-behind.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-unknown-malformed-required-profile.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/envelope/envelope-unknown-required-profile.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-022-lvr-emphasis-loud.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-022-lvr-emphasis-loud.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-022-lvr-emphasis-normal.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-022-lvr-emphasis-normal.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-emphasis-muted.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-emphasis-muted.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-emphasis-strong.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-emphasis-strong.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-tone-danger.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-tone-danger.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-tone-positive.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-alias-tone-positive.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-column.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-column.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-metric.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-metric.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-style.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-460-explicit-default-style.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-596-form-explicit-auto-state.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-596-form-explicit-auto-state.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-665-rows-opaque-sentinel.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-665-rows-opaque-sentinel.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-call-url.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-call-url.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-card-title-metric-value.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-card-title-metric-value.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-datagrid-data-column-type.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-datagrid-data-column-type.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-form-field-name.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-form-field-name.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-grid-columns-row.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-grid-columns-row.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-navigate-href.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-navigate-href.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-select-options-query-deps.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-alias-select-options-query-deps.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-button-label.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-button-label.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-callout.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-callout.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-heading.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-heading.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-markdown.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-bare-text-markdown.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-binding-bound-wrapper.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-binding-bound-wrapper.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-daterange-bare-array.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-daterange-bare-array.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-daterange-static-envelope.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-daterange-static-envelope.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-emphasis-cross-vocab.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-emphasis-cross-vocab.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-fact-explicit-defaults.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-fact-explicit-defaults.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-filterable-static-dashboard-compact.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-filterable-static-dashboard-compact.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-grid-field-named-compact.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-grid-field-named-compact.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-grid-transform-param-compact.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-grid-transform-param-compact.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-caption-envelope.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-caption-envelope.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-empty-srcset.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-empty-srcset.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-explicit-defaults.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-explicit-defaults.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-explicit-expandable-false.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-image-explicit-expandable-false.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-master-detail-preselected-compact.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-master-detail-preselected-compact.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-null-static-options.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-null-static-options.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-markers.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-markers.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-options.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-options.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-series.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-series.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-values.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-opaque-static-values.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-scalar-transform-composition-compact.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-scalar-transform-composition-compact.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-a11y-label-bare-scalar.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-a11y-label-bare-scalar.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-binding-scalar-fraction.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-binding-scalar-fraction.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-grid-no-cols.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-grid-no-cols.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-grid-template-no-cols.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-grid-template-no-cols.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-options-bare-strings.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-options-bare-strings.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-params-map.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-params-map.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-segmented-orientation-omitted.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-segmented-orientation-omitted.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-static-envelope-plain-scalars.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-shape-static-envelope-plain-scalars.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-pill-tag.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-pill-tag.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-tone-aliases.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-tone-aliases.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-tonemap-alias.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-tonedpill-tonemap-alias.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-bare-columns.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-bare-columns.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-epoch-timestamps.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-epoch-timestamps.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-expr-spellings.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-expr-spellings.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-contains.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-contains.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-filter.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-filter.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-or.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-or.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-scalar-fn.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-flat-scalar-fn.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-schemaless.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-schemaless.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-source-rowmajor.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-source-rowmajor.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-source-state-rows.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-source-state-rows.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-step-aliases.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-step-aliases.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-values-only-columns.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-transform-values-only-columns.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-window-cumsum-legacy.expected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/lenient/lenient-window-cumsum-legacy.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/manifest.json +3253 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-alert-assertive.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-button-named.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-image-decorative.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-link-labelled.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-wrapper-all-slots.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/a11y-wrapper-state-bound.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/badge-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/badge-transform-live.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/boundary-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/btn-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/btn-copy-link.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/btn-invoke.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/btn-json-payloads.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/btn-read-workbook.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/button-setstate-valuefrom.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/call-into.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/callout-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/card-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-axis-titles.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-data-labels.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-legend-position.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-state-rows.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-temporal-x.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/chart-value-format.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/code-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/composite-root.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/composite-tabs-panels.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/controls-closure.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/controls-declarative.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/custom-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/custom-bounded-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/custom-bounded-advisory.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/dash-empty.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/discl-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/drawing-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/drawing-empty.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/drawing-nonfinite-sentinels.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/drawing-rotated-labels.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/drawing-tipped-shapes.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/empty-state-card.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/fact-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/filterable-static-dashboard.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/filters-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/filters-date-range.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/filters-declarative.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/filters-segmented.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-date-range.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-date.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-declarative-minimal.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-declarative.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-field-rules.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-local-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-local-debounce.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-ranged.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-segmented.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/form-toggle.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/format-bindings.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-decl-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-decl-param.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-ref-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-ref-args.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-confirm-action-pair-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-confirm-action-pair.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-empty-state-panel-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-empty-state-panel.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-filter-bar-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-filter-bar.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-kpi-strip-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-kpi-strip.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-labelled-metric-row-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-labelled-metric-row.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-loading-placeholder-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-loading-placeholder.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-metric-card-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-metric-card.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-section-header-ref.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/frag-stdlib-section-header.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/glayout-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/glayout-tpl-autofit.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/glayout-tpl-fixed.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/glayout-tpl-ratio.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-bound-sort.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-declared-edit.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-editable-state.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-field-named.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-paged-sorted.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-paged.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-reorderable.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-sort-state-key.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-toned-pill.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-transform-param.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/grid-transform.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/heading-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/icon-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-caption-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-caption-i18n-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-expandable-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-expandable-figure-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-presentation-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/image-srcset-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/limit-node-depth-at-max.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/link-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/link-protected-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/list-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/lvr-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/map-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/markdown-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/masonry-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/masonry-gap.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/master-detail-multi-field.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/master-detail-preselected-second-row.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/master-detail-preselected.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/math-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/media-audio-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/media-video-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/media-video-autoplay-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/media-video-poster-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-duration-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-float-17sig.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-float-bigint.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-float-exp-neg.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-float-exp-pos.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-inverted-polarity.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-invoke.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/metric-nonfinite-sentinel.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/modal-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/mount-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/mount-2.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/multiselect-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/multiselect-chip-list-param.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/now-environment-binding.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/progress-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/query-dependson.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/scalar-transform-composition.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/scroll-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/select-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/shared-source-seeded-pair.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/skel-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/spark-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/spark-nonfinite-sentinel.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/split-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/stack-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/step-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/style-role-voice-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/summary-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/switch-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/switch-on-selection.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/table-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/table-sortable-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/tabs-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/tabs-explicit-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/toast-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/nodes/upload-1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-batch.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-editnode.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-insertchild.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-movenode.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-removenode.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-reorderchildren.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-replacebinding.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-replaceroot.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-badfield.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-badindex.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-column0-label.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-column1-label.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-field0-required.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-field1-required.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-malformed.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-object-value.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-yfield0.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop-nested-yfield1.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updateprop.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updatestate.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updatestyle (phase 147 role=eyebrow + voice=structural).json +1 -0
- fuaran_py-0.0.1/conformance/corpus/ops/op-updatestyle.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-hidden-nonbool.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-labelledby-nonstring.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-liveregion-nonstring.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-liveregion-unknown.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-not-object.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-a11y-role-nonstring.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-binding-float-bool.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-binding-float-sentinel-case.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-binding-float-string.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-binding-int-bool.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-binding-int-sentinel-string.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-box-masonry-nonpositive-cols.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-daterange-unordered.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-emptynodeid.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-fieldrule-empty.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-fieldrule-length-unordered.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-formfield-near-miss-validation.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-image-expandable-nonbool.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-image-srcset-nonpositive-width.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-image-srcset-null.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-invalid-empty.json +0 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-invalid-garbage.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-invalid-truncated.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-limit-json-depth-at-max.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-limit-json-depth.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-limit-node-depth.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-limit-op-depth.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-media-autoplay-nonbool.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-media-missing-label.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-binding-type.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-custom-moduleid.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-markdown-text.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-metric-value.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-mount-scopeid.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-node-id.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-node-kind.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-switch-cases.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-switch-default.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-missing-switch-statekey.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-a11y-aria-hidden.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-a11y-arialabel.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-a11y-live.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-a11y-liveregion-case.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-column-readonly.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-grid-behaviour-record.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-grid-current-page.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-nearmiss-grid-sortable.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-action-aitool-args.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-action-notify-payload.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-action-setstate-value.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-custom-prop.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-i18n-arg.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-null-updateprop-value.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-insertchild-empty-childid.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-insertchild-missing-parentid.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-insertchild-retired-position.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-missing-type.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-movenode-retired-newposition.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-reorderchildren-wrongtype.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-unknown-yeet.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-op-updateprop-missing-path.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-setstate-value-and-valuefrom.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-tonedpill-unknown-tone.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-binding.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-drawing-curve-command.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-drawing-shape.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-image-aspect.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-link-protection.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-media-kind.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-mount-direction.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-static-sort-direction.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-textsource.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-tone.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-unknown-trend-polarity.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongnodekind-sparkler.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongnodekind-widget.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-box-children.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-discriminator.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-grid-default-sort-column.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-grid-page-size-zero.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-heading-level.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-id.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-kind.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-static-sort-column.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/reject/reject-wrongtype-style-tone.json +1 -0
- fuaran_py-0.0.1/conformance/corpus/schema.json +8164 -0
- fuaran_py-0.0.1/conformance/sync_corpus.py +65 -0
- fuaran_py-0.0.1/docs/AUTHORING.md +104 -0
- fuaran_py-0.0.1/docs/fable-python-decision.md +127 -0
- fuaran_py-0.0.1/examples/quickstart_client.py +36 -0
- fuaran_py-0.0.1/examples/quickstart_reactive_data_app.py +119 -0
- fuaran_py-0.0.1/pyproject.toml +63 -0
- fuaran_py-0.0.1/run.ps1 +106 -0
- fuaran_py-0.0.1/samples/fastapi-host/README.md +116 -0
- fuaran_py-0.0.1/samples/fastapi-host/app.py +186 -0
- fuaran_py-0.0.1/samples/fastapi-host/requirements.txt +6 -0
- fuaran_py-0.0.1/src/fuaran_py/__init__.py +107 -0
- fuaran_py-0.0.1/src/fuaran_py/ai_tools/__init__.py +86 -0
- fuaran_py-0.0.1/src/fuaran_py/ai_tools/dispatch.py +91 -0
- fuaran_py-0.0.1/src/fuaran_py/ai_tools/introspect.py +190 -0
- fuaran_py-0.0.1/src/fuaran_py/ai_tools/surface.py +194 -0
- fuaran_py-0.0.1/src/fuaran_py/canonical.py +165 -0
- fuaran_py-0.0.1/src/fuaran_py/charts/__init__.py +2693 -0
- fuaran_py-0.0.1/src/fuaran_py/client/__init__.py +68 -0
- fuaran_py-0.0.1/src/fuaran_py/client/client.py +147 -0
- fuaran_py-0.0.1/src/fuaran_py/client/contract.py +136 -0
- fuaran_py-0.0.1/src/fuaran_py/client/session.py +64 -0
- fuaran_py-0.0.1/src/fuaran_py/client/wire.py +141 -0
- fuaran_py-0.0.1/src/fuaran_py/compute/__init__.py +38 -0
- fuaran_py-0.0.1/src/fuaran_py/compute/evaluate.py +437 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/__init__.py +7 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/bridge.py +84 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/decoder_fuzz.py +1036 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/fuzz_exchange.py +209 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/harness.py +111 -0
- fuaran_py-0.0.1/src/fuaran_py/conformance/refusal_report.py +123 -0
- fuaran_py-0.0.1/src/fuaran_py/dag.py +172 -0
- fuaran_py-0.0.1/src/fuaran_py/dataframe/__init__.py +143 -0
- fuaran_py-0.0.1/src/fuaran_py/dataframe/codec.py +1078 -0
- fuaran_py-0.0.1/src/fuaran_py/dataframe/evaluate.py +899 -0
- fuaran_py-0.0.1/src/fuaran_py/dataframe/model.py +424 -0
- fuaran_py-0.0.1/src/fuaran_py/elicitation.py +549 -0
- fuaran_py-0.0.1/src/fuaran_py/envelope.py +161 -0
- fuaran_py-0.0.1/src/fuaran_py/function.py +294 -0
- fuaran_py-0.0.1/src/fuaran_py/layout_observer/__init__.py +70 -0
- fuaran_py-0.0.1/src/fuaran_py/layout_observer/flags.py +260 -0
- fuaran_py-0.0.1/src/fuaran_py/layout_observer/observer.py +230 -0
- fuaran_py-0.0.1/src/fuaran_py/limits.py +62 -0
- fuaran_py-0.0.1/src/fuaran_py/merge.py +539 -0
- fuaran_py-0.0.1/src/fuaran_py/model.py +70 -0
- fuaran_py-0.0.1/src/fuaran_py/op_stream/__init__.py +114 -0
- fuaran_py-0.0.1/src/fuaran_py/op_stream/hash_chain.py +230 -0
- fuaran_py-0.0.1/src/fuaran_py/op_stream/in_memory_sink.py +76 -0
- fuaran_py-0.0.1/src/fuaran_py/op_stream/replay.py +180 -0
- fuaran_py-0.0.1/src/fuaran_py/op_stream/types.py +205 -0
- fuaran_py-0.0.1/src/fuaran_py/ops/__init__.py +26 -0
- fuaran_py-0.0.1/src/fuaran_py/ops/apply.py +1053 -0
- fuaran_py-0.0.1/src/fuaran_py/ops/decode.py +233 -0
- fuaran_py-0.0.1/src/fuaran_py/ops/diff.py +305 -0
- fuaran_py-0.0.1/src/fuaran_py/ops/encode.py +11 -0
- fuaran_py-0.0.1/src/fuaran_py/render_fidelity.py +439 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/__init__.py +84 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/bindings.py +348 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/content/fuaran-reference.css +2723 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/egress.py +562 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/html.py +67 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/markdown.py +1211 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/render.py +1945 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/sanitize.py +287 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/seeds.py +149 -0
- fuaran_py-0.0.1/src/fuaran_py/renderer/theme.py +208 -0
- fuaran_py-0.0.1/src/fuaran_py/result.py +71 -0
- fuaran_py-0.0.1/src/fuaran_py/runtime/__init__.py +22 -0
- fuaran_py-0.0.1/src/fuaran_py/runtime/runtime.py +256 -0
- fuaran_py-0.0.1/src/fuaran_py/runtime/sample.py +57 -0
- fuaran_py-0.0.1/src/fuaran_py/schema/__init__.py +15 -0
- fuaran_py-0.0.1/src/fuaran_py/schema/decode.py +2888 -0
- fuaran_py-0.0.1/src/fuaran_py/schema/encode.py +11 -0
- fuaran_py-0.0.1/src/fuaran_py/schema/types.py +2169 -0
- fuaran_py-0.0.1/src/fuaran_py/shapeguard.py +129 -0
- fuaran_py-0.0.1/src/fuaran_py/style_observer/__init__.py +129 -0
- fuaran_py-0.0.1/src/fuaran_py/style_observer/color.py +91 -0
- fuaran_py-0.0.1/src/fuaran_py/style_observer/flags.py +326 -0
- fuaran_py-0.0.1/src/fuaran_py/style_observer/manifest_flags.py +108 -0
- fuaran_py-0.0.1/src/fuaran_py/style_observer/observer.py +320 -0
- fuaran_py-0.0.1/src/fuaran_py/teleport.py +240 -0
- fuaran_py-0.0.1/src/fuaran_py/theme_manifest/__init__.py +91 -0
- fuaran_py-0.0.1/src/fuaran_py/theme_manifest/decode.py +162 -0
- fuaran_py-0.0.1/src/fuaran_py/theme_manifest/manifest.py +184 -0
- fuaran_py-0.0.1/src/fuaran_py/theme_manifest/project.py +173 -0
- fuaran_py-0.0.1/src/fuaran_py/ui/__init__.py +1025 -0
- fuaran_py-0.0.1/src/fuaran_py/ui/capability.py +238 -0
- fuaran_py-0.0.1/src/fuaran_py/ui/compute.py +546 -0
- fuaran_py-0.0.1/src/fuaran_py/validator/__init__.py +7 -0
- fuaran_py-0.0.1/src/fuaran_py/validator/validate.py +455 -0
- fuaran_py-0.0.1/tests/_corpus.py +105 -0
- fuaran_py-0.0.1/tests/_reference_host.py +95 -0
- fuaran_py-0.0.1/tests/fixtures/dataframe_parity.json +1 -0
- fuaran_py-0.0.1/tests/test_action_cases.py +62 -0
- fuaran_py-0.0.1/tests/test_ai_tools.py +179 -0
- fuaran_py-0.0.1/tests/test_authoring_toggle_now_selection.py +158 -0
- fuaran_py-0.0.1/tests/test_bare_state_source.py +75 -0
- fuaran_py-0.0.1/tests/test_bridge.py +91 -0
- fuaran_py-0.0.1/tests/test_browser_style_observer.py +80 -0
- fuaran_py-0.0.1/tests/test_canonical_numbers.py +48 -0
- fuaran_py-0.0.1/tests/test_capability_seam.py +118 -0
- fuaran_py-0.0.1/tests/test_chart_lowering.py +831 -0
- fuaran_py-0.0.1/tests/test_client.py +339 -0
- fuaran_py-0.0.1/tests/test_client_mock_endpoint.py +109 -0
- fuaran_py-0.0.1/tests/test_compute.py +361 -0
- fuaran_py-0.0.1/tests/test_compute_authoring.py +123 -0
- fuaran_py-0.0.1/tests/test_corpus_sync.py +75 -0
- fuaran_py-0.0.1/tests/test_dag_roundtrip.py +70 -0
- fuaran_py-0.0.1/tests/test_dataframe_parity.py +72 -0
- fuaran_py-0.0.1/tests/test_decoder_fuzz.py +202 -0
- fuaran_py-0.0.1/tests/test_diff.py +365 -0
- fuaran_py-0.0.1/tests/test_diff_golden.py +116 -0
- fuaran_py-0.0.1/tests/test_egress_policy.py +485 -0
- fuaran_py-0.0.1/tests/test_elicitation.py +115 -0
- fuaran_py-0.0.1/tests/test_envelope.py +93 -0
- fuaran_py-0.0.1/tests/test_fastapi_host.py +140 -0
- fuaran_py-0.0.1/tests/test_field_rule.py +316 -0
- fuaran_py-0.0.1/tests/test_function.py +159 -0
- fuaran_py-0.0.1/tests/test_fuzz_exchange.py +168 -0
- fuaran_py-0.0.1/tests/test_generative_parity.py +104 -0
- fuaran_py-0.0.1/tests/test_kind_set.py +125 -0
- fuaran_py-0.0.1/tests/test_layout_observer.py +162 -0
- fuaran_py-0.0.1/tests/test_lenient.py +32 -0
- fuaran_py-0.0.1/tests/test_limits.py +176 -0
- fuaran_py-0.0.1/tests/test_manifest_flags.py +135 -0
- fuaran_py-0.0.1/tests/test_markdown_corpus.py +176 -0
- fuaran_py-0.0.1/tests/test_masonry_layout.py +119 -0
- fuaran_py-0.0.1/tests/test_merge_conformance.py +149 -0
- fuaran_py-0.0.1/tests/test_merge_primacy.py +207 -0
- fuaran_py-0.0.1/tests/test_op_stream.py +268 -0
- fuaran_py-0.0.1/tests/test_ops_apply.py +451 -0
- fuaran_py-0.0.1/tests/test_reject.py +20 -0
- fuaran_py-0.0.1/tests/test_render_a11y_corpus.py +136 -0
- fuaran_py-0.0.1/tests/test_render_a11y_placement.py +88 -0
- fuaran_py-0.0.1/tests/test_render_a11y_projection.py +125 -0
- fuaran_py-0.0.1/tests/test_render_fidelity.py +143 -0
- fuaran_py-0.0.1/tests/test_render_obligations.py +479 -0
- fuaran_py-0.0.1/tests/test_render_parity.py +188 -0
- fuaran_py-0.0.1/tests/test_render_sanitize.py +228 -0
- fuaran_py-0.0.1/tests/test_renderer.py +395 -0
- fuaran_py-0.0.1/tests/test_retired_position.py +68 -0
- fuaran_py-0.0.1/tests/test_roundtrip.py +24 -0
- fuaran_py-0.0.1/tests/test_runtime.py +146 -0
- fuaran_py-0.0.1/tests/test_sanitization_corpus.py +137 -0
- fuaran_py-0.0.1/tests/test_scalar_compute_parity.py +174 -0
- fuaran_py-0.0.1/tests/test_schema_conformance.py +75 -0
- fuaran_py-0.0.1/tests/test_state_seeding.py +211 -0
- fuaran_py-0.0.1/tests/test_style_observer.py +205 -0
- fuaran_py-0.0.1/tests/test_teleport.py +131 -0
- fuaran_py-0.0.1/tests/test_theme_manifest.py +118 -0
- fuaran_py-0.0.1/tests/test_toned_pill.py +160 -0
- fuaran_py-0.0.1/tests/test_trend_polarity.py +248 -0
- fuaran_py-0.0.1/tests/test_ui_authoring.py +441 -0
- fuaran_py-0.0.1/tests/test_validator.py +252 -0
- fuaran_py-0.0.1/tests/test_validator_coverage.py +83 -0
- fuaran_py-0.0.1/validator-coverage.json +34 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Pin canonical text-file line endings to LF.
|
|
2
|
+
#
|
|
3
|
+
# `text=auto` lets git detect text vs. binary; `eol=lf` pins the
|
|
4
|
+
# canonical in-repo line ending. On a Windows checkout, files stay LF
|
|
5
|
+
# on disk (no autocrlf round-trip churn), eliminating the recurring
|
|
6
|
+
# "LF will be replaced by CRLF the next time Git touches it" warnings
|
|
7
|
+
# and phantom "modified" churn on tool-regenerated files.
|
|
8
|
+
|
|
9
|
+
* text=auto eol=lf
|
|
10
|
+
|
|
11
|
+
# Explicit binary types — git's auto-detection is usually correct
|
|
12
|
+
# but spell out the common ones to be safe.
|
|
13
|
+
*.png binary
|
|
14
|
+
*.jpg binary
|
|
15
|
+
*.jpeg binary
|
|
16
|
+
*.gif binary
|
|
17
|
+
*.ico binary
|
|
18
|
+
*.svg binary
|
|
19
|
+
*.dll binary
|
|
20
|
+
*.exe binary
|
|
21
|
+
*.pdb binary
|
|
22
|
+
*.nupkg binary
|
|
23
|
+
*.snupkg binary
|
|
24
|
+
*.zip binary
|
|
25
|
+
*.tar binary
|
|
26
|
+
*.gz binary
|
|
27
|
+
*.pdf binary
|
|
28
|
+
*.woff binary
|
|
29
|
+
*.woff2 binary
|
|
30
|
+
*.ttf binary
|
|
31
|
+
*.eot binary
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Build, test and wire-format conformance gate for the Python host.
|
|
2
|
+
#
|
|
3
|
+
# ── Layout ───────────────────────────────────────────────────────────────────
|
|
4
|
+
# The conformance corpus is its own public repository. The test suite resolves
|
|
5
|
+
# it as a SIBLING of this repo root (tests/_corpus.py walks to
|
|
6
|
+
# `<container>/wire-format-fixtures`), so this repo is checked out into a
|
|
7
|
+
# subdirectory and the corpus beside it:
|
|
8
|
+
#
|
|
9
|
+
# $GITHUB_WORKSPACE/
|
|
10
|
+
# ├── fuaran-py/ ← this repo
|
|
11
|
+
# └── wire-format-fixtures/ ← the corpus (fuaran-ui/fuaran-ui-specification)
|
|
12
|
+
#
|
|
13
|
+
# The corpus repository is PUBLIC, so the checkout is anonymous — no token.
|
|
14
|
+
#
|
|
15
|
+
# ── Why the corpus assertion below is not optional ───────────────────────────
|
|
16
|
+
# When the corpus is absent, tests/_corpus.py does NOT fail — it silently falls
|
|
17
|
+
# back to the committed offline snapshot under conformance/corpus/. A layout
|
|
18
|
+
# drift would therefore leave the suite certifying against a stale local copy of
|
|
19
|
+
# the spec while the job stayed green, which is the one failure mode a
|
|
20
|
+
# conformance gate must never have. The assertion turns that into a hard stop.
|
|
21
|
+
|
|
22
|
+
name: CI
|
|
23
|
+
|
|
24
|
+
on:
|
|
25
|
+
push:
|
|
26
|
+
branches: [main]
|
|
27
|
+
pull_request:
|
|
28
|
+
branches: [main]
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
|
|
33
|
+
env:
|
|
34
|
+
# ── QUARANTINE ──────────────────────────────────────────────────────────────────────────
|
|
35
|
+
# Cases that fail on a clean tree are excluded from the blocking gate here, so
|
|
36
|
+
# that a NEW regression is caught while known drift stays visible. An entry is
|
|
37
|
+
# not a judgement that a case is wrong — it is a record of known, understood,
|
|
38
|
+
# outstanding work, and the list SHRINKS TO EMPTY as the decoder catches up.
|
|
39
|
+
# Delete entries as they start passing; do not add to it to make a red build
|
|
40
|
+
# green without a root-cause note — a new failure here is the regression this
|
|
41
|
+
# gate exists to catch.
|
|
42
|
+
#
|
|
43
|
+
# EMPTY as of 2026-08-13: the Toggle/Now/Switch vocabulary landed
|
|
44
|
+
# (3ac3c87/8e9fac3), the offline corpus snapshot was re-synced (392abd6), and
|
|
45
|
+
# every formerly quarantined case passes against the current corpus —
|
|
46
|
+
# verified by the full suite at 1627 passed with zero deselections AND each
|
|
47
|
+
# former entry re-run by node ID (24 passed). The machinery stays armed; a
|
|
48
|
+
# future corpus advance repopulates the list the same way.
|
|
49
|
+
QUARANTINE: ""
|
|
50
|
+
|
|
51
|
+
jobs:
|
|
52
|
+
build:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
defaults:
|
|
55
|
+
run:
|
|
56
|
+
working-directory: fuaran-py
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
with:
|
|
60
|
+
path: fuaran-py
|
|
61
|
+
|
|
62
|
+
# Deliberately UNPINNED: the corpus is the shared conformance authority
|
|
63
|
+
# every host certifies against, and one current spec for all hosts is the
|
|
64
|
+
# point of giving it a single home. Pinning here would let this host drift
|
|
65
|
+
# from the spec without anything going red.
|
|
66
|
+
- name: Check out the wire-format conformance corpus
|
|
67
|
+
uses: actions/checkout@v4
|
|
68
|
+
with:
|
|
69
|
+
repository: fuaran-ui/fuaran-ui-specification
|
|
70
|
+
path: wire-format-fixtures
|
|
71
|
+
|
|
72
|
+
- name: Assert the corpus is where the tests resolve it
|
|
73
|
+
run: test -f ../wire-format-fixtures/manifest.json
|
|
74
|
+
|
|
75
|
+
- uses: actions/setup-python@v5
|
|
76
|
+
with:
|
|
77
|
+
python-version: '3.13'
|
|
78
|
+
|
|
79
|
+
- name: Install
|
|
80
|
+
run: |
|
|
81
|
+
python -m pip install --upgrade pip
|
|
82
|
+
python -m pip install ".[dev]"
|
|
83
|
+
|
|
84
|
+
- name: Lint (ruff check)
|
|
85
|
+
run: python -m ruff check .
|
|
86
|
+
|
|
87
|
+
- name: Format check (ruff format)
|
|
88
|
+
run: python -m ruff format --check .
|
|
89
|
+
|
|
90
|
+
- name: Type check (mypy)
|
|
91
|
+
run: python -m mypy
|
|
92
|
+
|
|
93
|
+
# Print what this gate does NOT cover, on every run. A cap nobody can see
|
|
94
|
+
# reads as "covered everything".
|
|
95
|
+
- name: Report the conformance quarantine
|
|
96
|
+
run: |
|
|
97
|
+
if [ -n "$QUARANTINE" ]; then
|
|
98
|
+
echo "The blocking gate below EXCLUDES these known-failing cases:"
|
|
99
|
+
echo "$QUARANTINE" | tr ' ' '\n' | grep -v '^--deselect$' | sed 's/^/ - /'
|
|
100
|
+
else
|
|
101
|
+
echo "Quarantine EMPTY — the blocking gate covers the full conformance suite."
|
|
102
|
+
fi
|
|
103
|
+
echo
|
|
104
|
+
echo "ALSO NOT GATED HERE — inert rather than quarantined (see the skip"
|
|
105
|
+
echo "summary that -rs prints under each test run below):"
|
|
106
|
+
echo " - the reference-renderer parity tests read the reference host's source"
|
|
107
|
+
echo " from a sibling checkout this workflow does not make, so they skip."
|
|
108
|
+
echo " A skip here does NOT mean the path they look in is right: it was"
|
|
109
|
+
echo " wrong for months (the host was renamed) and this note read as the"
|
|
110
|
+
echo " explanation. What proves the path now is"
|
|
111
|
+
echo " test_reference_host_resolves_in_a_cross_host_checkout, which never"
|
|
112
|
+
echo " skips and fails in any checkout that HAS sibling hosts but no"
|
|
113
|
+
echo " reference host. It passes here because this job is genuinely a"
|
|
114
|
+
echo " single-host checkout — the one shape in which skipping is honest."
|
|
115
|
+
echo " - the FastAPI sample-host tests skip because fastapi is a sample-only"
|
|
116
|
+
echo " dependency and is not in the [dev] extra this job installs."
|
|
117
|
+
{
|
|
118
|
+
echo "### Conformance quarantine (excluded from the blocking gate)"
|
|
119
|
+
echo
|
|
120
|
+
if [ -n "$QUARANTINE" ]; then
|
|
121
|
+
echo '```'
|
|
122
|
+
echo "$QUARANTINE" | tr ' ' '\n' | grep -v '^--deselect$'
|
|
123
|
+
echo '```'
|
|
124
|
+
else
|
|
125
|
+
echo "Empty — the blocking gate covers the full conformance suite."
|
|
126
|
+
fi
|
|
127
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
128
|
+
|
|
129
|
+
# Non-blocking: runs the FULL suite so the size of the existing drift is
|
|
130
|
+
# measured on every run rather than inferred from the quarantine list.
|
|
131
|
+
- name: Conformance drift (reported, non-blocking)
|
|
132
|
+
continue-on-error: true
|
|
133
|
+
run: |
|
|
134
|
+
set +e
|
|
135
|
+
python -m pytest -q -rs > drift.log 2>&1
|
|
136
|
+
status=$?
|
|
137
|
+
tail -40 drift.log
|
|
138
|
+
{
|
|
139
|
+
echo "### Full suite, including quarantined cases (non-blocking)"
|
|
140
|
+
echo
|
|
141
|
+
echo '```'
|
|
142
|
+
grep -E '^[0-9]+ (failed|passed)|failed,|passed,' drift.log | tail -3
|
|
143
|
+
echo '```'
|
|
144
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
145
|
+
exit 0
|
|
146
|
+
|
|
147
|
+
# THE GATE. Everything the suite currently passes, including every
|
|
148
|
+
# conformance case outside the quarantine above.
|
|
149
|
+
- name: Test (gated)
|
|
150
|
+
run: python -m pytest -q -rs $QUARANTINE
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Publish fuaran-py to PyPI on a version tag, via PyPI Trusted Publishing (OIDC — no stored token).
|
|
2
|
+
#
|
|
3
|
+
# Trigger: a `v*` tag push only. A branch push reaches no publish job.
|
|
4
|
+
# Prerequisites (one-time, on pypi.org): register this repository as the project's trusted
|
|
5
|
+
# publisher — owner `fuaran-ui`, repository `fuaran-py`, workflow `publish-pypi.yml`,
|
|
6
|
+
# environment `pypi` — then create the `pypi` environment on this repository.
|
|
7
|
+
# The tag must equal the version in pyproject.toml; the gate below refuses otherwise, so a
|
|
8
|
+
# mistyped tag never publishes a mismatched release.
|
|
9
|
+
name: publish-pypi
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
push:
|
|
13
|
+
tags: ["v*"]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: "3.13"
|
|
26
|
+
- name: Version gate — tag must equal pyproject version
|
|
27
|
+
run: |
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
30
|
+
ver="$(python - <<'PY'
|
|
31
|
+
import tomllib
|
|
32
|
+
print(tomllib.load(open("pyproject.toml","rb"))["project"]["version"])
|
|
33
|
+
PY
|
|
34
|
+
)"
|
|
35
|
+
if [ "$tag" != "$ver" ]; then
|
|
36
|
+
echo "tag v$tag does not match pyproject version $ver" >&2
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
- name: Build sdist + wheel
|
|
40
|
+
run: |
|
|
41
|
+
python -m pip install --upgrade pip build
|
|
42
|
+
python -m build
|
|
43
|
+
- uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: dist
|
|
46
|
+
path: dist/
|
|
47
|
+
if-no-files-found: error
|
|
48
|
+
|
|
49
|
+
publish:
|
|
50
|
+
needs: build
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
environment: pypi
|
|
53
|
+
permissions:
|
|
54
|
+
id-token: write
|
|
55
|
+
contents: read
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
62
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
63
|
+
with:
|
|
64
|
+
skip-existing: true
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv/
|
|
11
|
+
venv/
|
|
12
|
+
env/
|
|
13
|
+
|
|
14
|
+
# Tooling caches
|
|
15
|
+
.pytest_cache/
|
|
16
|
+
.mypy_cache/
|
|
17
|
+
.ruff_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
coverage.xml
|
|
21
|
+
|
|
22
|
+
# Editors / OS
|
|
23
|
+
.vscode/
|
|
24
|
+
.idea/
|
|
25
|
+
.DS_Store
|
|
26
|
+
Thumbs.db
|
|
27
|
+
|
|
28
|
+
# The synced corpus snapshot (conformance/corpus/) IS committed — it makes
|
|
29
|
+
# fuaran-py standalone-testable offline and gives the drift guard
|
|
30
|
+
# (tests/test_corpus_sync.py) a committed baseline to pin against the authority.
|
|
31
|
+
# Re-sync + re-commit it whenever the authoritative corpus regenerates
|
|
32
|
+
# (python conformance/sync_corpus.py), per the §11 forward-coupling rule.
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
# CLAUDE.md — fuaran-py (Python reference implementation)
|
|
2
|
+
|
|
3
|
+
This repo is the **Python host of the Fuaran UI language** — a **co-equal sibling
|
|
4
|
+
to the F# (`Fuaran.UI`) and TypeScript (`@fuaran-ui/*`) tiers**, not merely a
|
|
5
|
+
codec. The target identity is a full authoring + rendering host: an ergonomic
|
|
6
|
+
`fuaran_py.ui` smart-constructor authoring surface (roadmap Phase 278), a tree-op
|
|
7
|
+
apply engine (279), a server-HTML renderer (`fuaran_py.renderer`, Phase 239 —
|
|
8
|
+
shipped) plus an interactive Pyodide client runtime (280), all conformant to the
|
|
9
|
+
shared wire format. What ships **today** is the floor: the canonical-JSON codec
|
|
10
|
+
(`decode_node` / `encode_node` / `decode_op` / `encode_op`), a pre-emit validator,
|
|
11
|
+
the corpus conformance harness, and the headless renderer; the authoring +
|
|
12
|
+
interactive surfaces are the Wave 35 co-equality expansion (Phases 278–281).
|
|
13
|
+
|
|
14
|
+
**Framing — load-bearing, do not regress.** The LLM's emission surface is the
|
|
15
|
+
**canonical JSON wire format, for every host**. The three language tiers (F#, TS,
|
|
16
|
+
Python) are **human-developer authoring surfaces** that produce that JSON; the AI
|
|
17
|
+
never authors host-language code — a structure-free baseline exists only as the
|
|
18
|
+
*thing to beat*, not a direction. Python being
|
|
19
|
+
the language AI orchestrators are written in is a reason it is *valuable*, not a
|
|
20
|
+
reason to make it a lesser, codec-only kind of artefact than F#/TS.
|
|
21
|
+
|
|
22
|
+
This repo sits alongside the `fuaran` (F#) and `fuaran-ts` (TypeScript) tiers as a co-equal
|
|
23
|
+
conformant host. Cross-repo development conventions (port allocation, formatting, language-baseline pinning) live at the maintainers' workspace level and are not shipped here.
|
|
24
|
+
|
|
25
|
+
## Posture
|
|
26
|
+
|
|
27
|
+
- **Apache 2.0 from day one** — same posture as `fuaran-ts`, to make the
|
|
28
|
+
reference-implementation claim unambiguous.
|
|
29
|
+
- **Sibling reference implementation, not a transpile.** `fuaran-py` is built to
|
|
30
|
+
the language-neutral wire-format spec (`../fuaran-dotnet/docs/WIRE_FORMAT.md`) +
|
|
31
|
+
conformance corpus (`../wire-format-fixtures/`), not generated from the F# tier.
|
|
32
|
+
The Fable-Python build-vs-port evaluation that settled this is recorded in
|
|
33
|
+
[`docs/fable-python-decision.md`](docs/fable-python-decision.md).
|
|
34
|
+
- **Wire-format conformance is the stability contract.** The codec encodes /
|
|
35
|
+
decodes byte-identically against the shared corpus; it is certified the same
|
|
36
|
+
way the F# and TypeScript hosts are.
|
|
37
|
+
- **Dependency-light.** The runtime codec uses only the Python standard library.
|
|
38
|
+
Third-party packages appear only as dev tooling (`pytest` / `mypy` / `ruff`).
|
|
39
|
+
|
|
40
|
+
## Language baseline
|
|
41
|
+
|
|
42
|
+
CPython **3.13+** (the workspace-chosen floor for this sibling; uses PEP 695
|
|
43
|
+
type parameters / `type` aliases). The Python analogue of the workspace's
|
|
44
|
+
F#-10/.NET-10 pinning.
|
|
45
|
+
|
|
46
|
+
## Layout
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
fuaran-py/
|
|
50
|
+
├── src/fuaran_py/
|
|
51
|
+
│ ├── canonical.py # the canonical-JSON encoder (number form, key sort, escaping)
|
|
52
|
+
│ ├── model.py # the structural typed tree (Node / Obj / Arr)
|
|
53
|
+
│ ├── result.py # Ok / Err + the six DecodeError codes
|
|
54
|
+
│ ├── schema/ # decode_node / encode_node + the per-kind field schemas
|
|
55
|
+
│ ├── ops/ # decode_op / encode_op over the 11-op TreeOp algebra
|
|
56
|
+
│ ├── op_stream/ # hash-chained provenance log: StreamEntry envelope + SHA-256 chain + in-memory sink + replay
|
|
57
|
+
│ ├── validator/ # pre-emit, default-deny-by-shape structural validator
|
|
58
|
+
│ ├── ai_tools/ # AI-tools introspection: emittable-surface catalog + value-space + tool schemas + tree introspection + default-deny dispatch gate (Phase 237)
|
|
59
|
+
│ ├── conformance/ # corpus round-trip smoke harness + certification bridge + the cross-host fuzz-sample exchange (Phase 236)
|
|
60
|
+
│ ├── renderer/ # optional server-HTML renderer + sanitiser + reference CSS (Phase 239)
|
|
61
|
+
│ ├── style_observer/ # computed-style observer: pure flag tier + InMemory + Pyodide live read-back
|
|
62
|
+
│ └── theme_manifest/ # DTCG-compatible theme contract (tokens + role bindings + invariants)
|
|
63
|
+
├── tests/ # pytest: number form, full-corpus round-trip + reject, validator
|
|
64
|
+
├── docs/ # fable-python-decision.md (the build-vs-port decision record)
|
|
65
|
+
├── pyproject.toml # dependency-light; dev extras = pytest / mypy / ruff
|
|
66
|
+
├── LICENSE # Apache 2.0 + Diametrical Ltd copyright
|
|
67
|
+
└── run.ps1 # Stage-0 entry point — lint + format-check + type-check + test
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Build / verify pipeline
|
|
71
|
+
|
|
72
|
+
```powershell
|
|
73
|
+
.\run.ps1 # provision .venv (first run) + ruff + mypy + pytest
|
|
74
|
+
.\run.ps1 -SkipInstall # run the gate against an already-provisioned .venv
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Or drive the tools directly inside the venv: `ruff check .`, `ruff format .`,
|
|
78
|
+
`mypy`, `pytest`.
|
|
79
|
+
|
|
80
|
+
## Formatting mandate
|
|
81
|
+
|
|
82
|
+
The workspace formatting mandate (Fantomas for F#, Prettier for TS) maps here to
|
|
83
|
+
**ruff** — every commit is preceded by `ruff format` + `ruff check` over the
|
|
84
|
+
changed files. The CI gate is `ruff format --check` + `ruff check`.
|
|
85
|
+
|
|
86
|
+
## Wire format
|
|
87
|
+
|
|
88
|
+
The canonical wire format is owned by the F# `fuaran` tier
|
|
89
|
+
(`../fuaran-dotnet/docs/WIRE_FORMAT.md`) with the workspace-level
|
|
90
|
+
`../wire-format-fixtures/` corpus as the executable conformance suite. `fuaran-py`
|
|
91
|
+
is one conformant host: it round-trips the corpus byte-for-byte and surfaces the
|
|
92
|
+
canonical reject code + path for every malformed fixture. The **forward-coupling
|
|
93
|
+
rule** (`WIRE_FORMAT.md` §11) means a new `NodeKind` / `Spec` / `TreeOp` /
|
|
94
|
+
`Binding` / `Action` case must move every host in one change — `fuaran-py` is now
|
|
95
|
+
one of those hosts.
|
|
96
|
+
|
|
97
|
+
### Conformance coverage (v0 bootstrap)
|
|
98
|
+
|
|
99
|
+
The codec **runs the full four-family corpus green** — node round-trips, op
|
|
100
|
+
round-trips, rejects, and `lenient-accept` per WIRE_FORMAT §16's normative
|
|
101
|
+
block (fixture counts drift as the corpus grows —
|
|
102
|
+
`../wire-format-fixtures/manifest.json` is the authoritative enumeration).
|
|
103
|
+
The 2026-07-05 parity pass closed the measured divergences: the `Mount`
|
|
104
|
+
NodeKind decodes (typed scopeId/channel, structural inputs); the §16
|
|
105
|
+
bare-string TextSource shorthand is accepted and normalises to canonical
|
|
106
|
+
bytes; JSON null rejects in structured JVal positions at the null's exact
|
|
107
|
+
path (`_from_json_strict` — the §5 `Binding.Static` opaque seam deliberately
|
|
108
|
+
stays null-lenient); the op decoder gates the reserved `"<opaque>"` /
|
|
109
|
+
`"<closure>"` sentinels in `UpdateProp` values like the F# reference. Typed
|
|
110
|
+
field-level validation is implemented for the common kinds;
|
|
111
|
+
recognised-but-not-yet-typed kinds are accepted structurally (still
|
|
112
|
+
byte-exact on round-trip). The formal certification harness (offline corpus
|
|
113
|
+
snapshot + drift guard, schema validation, a language-agnostic certification
|
|
114
|
+
bridge, a CI leg, and generative parity) has since landed.
|
|
115
|
+
|
|
116
|
+
### Within-host parity vs the cross-host exchange (do not conflate these)
|
|
117
|
+
|
|
118
|
+
Two generative floors sit above the curated corpus, and each proves something the
|
|
119
|
+
other cannot:
|
|
120
|
+
|
|
121
|
+
- **Within-host** — `tests/test_generative_parity.py`, ≥1000 `hypothesis` trees,
|
|
122
|
+
`encode(decode(encode x)) == encode x`. This host's canonical form is a fixed
|
|
123
|
+
point. Self-consistency only: a misreading of the spec that this host applies
|
|
124
|
+
*consistently* passes it every time.
|
|
125
|
+
- **Cross-host** — `fuaran_py.conformance.fuzz_exchange`, the fuzz-sample
|
|
126
|
+
exchange. Another host emits its canonical bytes for generated trees into
|
|
127
|
+
`<dir>/fsharp/`; this module decodes + re-encodes each through the Python codec,
|
|
128
|
+
asserts byte-identity, and writes **this** host's canonical output to
|
|
129
|
+
`<dir>/python/` so the sibling's `--check-fuzz-samples <dir> python` can check
|
|
130
|
+
the converse direction. One host's output, a different host's codec — the only
|
|
131
|
+
arrangement in which a shared misreading is visible.
|
|
132
|
+
|
|
133
|
+
The exchange needs a sibling host's emitter and so is hand-driven (the module
|
|
134
|
+
docstring carries the full command sequence); the within-host floor runs under a
|
|
135
|
+
plain `pytest`. `tests/test_fuzz_exchange.py` pins the *runner* — that it detects
|
|
136
|
+
a divergence, emits this host's set, and refuses a missing input set — using real
|
|
137
|
+
corpus payloads, so it needs no other toolchain.
|
|
138
|
+
|
|
139
|
+
The exchange's directory names (`fsharp` / `python`) are a cross-repo contract
|
|
140
|
+
with the sibling's `--check-fuzz-samples <dir> <host>` argument; renaming either
|
|
141
|
+
side silently un-wires it.
|
|
142
|
+
|
|
143
|
+
### Decoder robustness fuzz (`fuaran_py.conformance.decoder_fuzz` + `tests/test_decoder_fuzz.py`)
|
|
144
|
+
|
|
145
|
+
A third generative floor, and the only one aimed at inputs a conformant emitter would never produce.
|
|
146
|
+
The refusal contract — decoding is TOTAL, so a malformed or hostile input yields a structured typed
|
|
147
|
+
error, never an exception and never a hang — is asserted against **generated** hostile input rather
|
|
148
|
+
than against the curated reject corpus alone. A curated corpus is evidence about the author's
|
|
149
|
+
imagination.
|
|
150
|
+
|
|
151
|
+
- **The bounded run IS the PR gate.** It landed in the suite `pytest` already runs, so no workflow
|
|
152
|
+
change was needed and none can silently switch it off.
|
|
153
|
+
- **The long run is a CLI**, and it writes its own machine-readable record:
|
|
154
|
+
`python -m fuaran_py.conformance.decoder_fuzz --long --iterations 250000 --evidence <file>`.
|
|
155
|
+
- **The go-red self-test is permanent, and so is its inverse.** Five mutants, one per invariant, plus
|
|
156
|
+
a pin that each is PARTIAL — a mutant that broke every input would make the harness look sensitive
|
|
157
|
+
while testing nothing.
|
|
158
|
+
- **The allocation invariant is split in two, deliberately.** The main stream carries a cheap
|
|
159
|
+
canonical-output amplification proxy; `test_pathological_inputs_stay_inside_an_allocation_budget`
|
|
160
|
+
spends `tracemalloc` on the pathological family, where an adversarial shape would actually
|
|
161
|
+
amplify. Neither half is the whole invariant, and the docstrings say which is which.
|
|
162
|
+
- **`RecursionError` is the escape this host most needs to see.** A recursive-descent decoder that
|
|
163
|
+
lets the interpreter's stack limit answer for it has no depth guard, whatever its §21 constant
|
|
164
|
+
says. `check` catches it and reports it as a counterexample rather than letting it propagate.
|
|
165
|
+
- **The generator is a SIBLING of the reference one, not a transpile.** Same five input families; a
|
|
166
|
+
different byte stream, necessarily — the reference alphabet carries lone surrogates a `str` cannot
|
|
167
|
+
encode to UTF-8 at all, and the module names that gap rather than closing it silently.
|
|
168
|
+
- **The `duplicate-key` mutator and the `NaN` / `Infinity` / `1e999` / `+1` tokens are generated
|
|
169
|
+
deliberately, and nothing here asserts which answer is right.** Those are §20 "Decode
|
|
170
|
+
determinism", landed PROPOSED and not yet ratified; crash-freedom on them is in scope, agreement
|
|
171
|
+
is not.
|
|
172
|
+
|
|
173
|
+
`fuaran_py.conformance.refusal_report` is the companion emitter: this host's refusal class for every
|
|
174
|
+
reject fixture, as JSON, for a cross-host runner that compares the hosts to each other rather than
|
|
175
|
+
each to the corpus in isolation.
|
|
176
|
+
|
|
177
|
+
## Renderer (Phase 239)
|
|
178
|
+
|
|
179
|
+
`fuaran_py.renderer.render_html` walks a decoded `Node` tree and emits a
|
|
180
|
+
**body-fragment HTML string** carrying the reference `fuaran-*` class vocabulary,
|
|
181
|
+
so the byte-copied stylesheet styles it exactly as the F#/TS hosts style their
|
|
182
|
+
output. Server semantics mirror the F# SSR precedent: no runtime, no dispatch
|
|
183
|
+
(`Button` inert, `Link` a real `<a href>`), `Static` bindings resolve and the
|
|
184
|
+
rest placeholder to an em-dash, and a visualisation this host cannot paint
|
|
185
|
+
renders a deterministic placeholder. A **data-bound `DataGrid` renders its
|
|
186
|
+
rows** — the completeness posture; the declared boundary (a closure-projected
|
|
187
|
+
column, which cannot survive the wire) is written up in the README's
|
|
188
|
+
"Bound-grid rendering" section.
|
|
189
|
+
|
|
190
|
+
Two disciplines keep it honest:
|
|
191
|
+
|
|
192
|
+
- **Reference-CSS byte-copy.** `src/fuaran_py/renderer/content/fuaran-reference.css`
|
|
193
|
+
is a byte-for-byte copy of the F# canonical
|
|
194
|
+
(`../fuaran-dotnet/src/Fuaran.UI.Renderer/content/fuaran-reference.css`). A test
|
|
195
|
+
(`test_render_parity` / the byte-identical check) fails if the copy drifts when
|
|
196
|
+
the F# sibling is checked out alongside. Re-copy it in the same change-set as
|
|
197
|
+
any F#-side CSS change (the §11 forward-coupling rule now spans this host too).
|
|
198
|
+
- **Class-name vocabulary parity.** `tests/test_render_parity.py` extracts the
|
|
199
|
+
class vocabulary straight from the F# reference renderer source (`Render.fs` +
|
|
200
|
+
`Theme.fs`, literals + `sprintf "...-%s"` prefixes) and asserts every class the
|
|
201
|
+
Python renderer emits over the node corpus is in it — a cross-host parity lock,
|
|
202
|
+
the rendering analogue of the wire corpus. It skips when the F# sibling is
|
|
203
|
+
absent. **A new `NodeKind` / variant that changes the emitted class vocabulary
|
|
204
|
+
updates the renderer here in the same move that updates the codec.**
|
|
205
|
+
|
|
206
|
+
Sanitisation matches the F#/TS posture (`fuaran_py.renderer.sanitize` ports
|
|
207
|
+
`Sanitize.fs`): URL-scheme default-deny, `data-*`/`aria-*` attribute allowlist,
|
|
208
|
+
markdown escaped-first then swept. The `Custom` host-renderer registry is a host
|
|
209
|
+
trust boundary — the baseline ships no registry seam, so `Custom` renders an
|
|
210
|
+
inert labelled placeholder.
|
|
211
|
+
|
|
212
|
+
**Destination policy is AMBIENT on the render context, and defaults to deny.**
|
|
213
|
+
`Renderer.egress_policy` (`fuaran_py.renderer.egress`, the port of the reference
|
|
214
|
+
host's destination-policy section) is consulted by every `href` / `src` the
|
|
215
|
+
renderer emits — the `Link` node's `href` under the `HYPERLINK` class, the
|
|
216
|
+
`Image` node's `src` under `MEDIA`, and the whole markdown body through
|
|
217
|
+
`markdown.to_html_with_egress`. `render_html(..., egress_policy=…)` and
|
|
218
|
+
`FuaranRuntime(..., egress_policy=…)` are the declaration points; both default to
|
|
219
|
+
`DENY_NON_LOCAL_EGRESS`, so a host that declares nothing gets deny, and
|
|
220
|
+
`PERMISSIVE_EGRESS` is reachable only by that name.
|
|
221
|
+
|
|
222
|
+
Three disciplines follow, and the third is the one that decays quietly:
|
|
223
|
+
|
|
224
|
+
- **A new emission that carries a URL owes a policy consultation in the same
|
|
225
|
+
change.** `sanitize_url_or_blank` is the *floor*, not the seam — reaching for
|
|
226
|
+
it directly at a render call site re-opens the hole the ambient default closed.
|
|
227
|
+
Use `egress.sanitize_url_for_egress(self.egress_policy, <class>, url)` and
|
|
228
|
+
splice the returned attribute pairs onto the emitted element, last.
|
|
229
|
+
- **Never widen the default to make a test pass.** The corpus fixtures and the
|
|
230
|
+
ambient tests in `tests/test_egress_policy.py` are the falsifiers in both
|
|
231
|
+
directions; a fixture that needs an off-origin host names a policy.
|
|
232
|
+
- **`tests/test_markdown_corpus.py` runs TWO legs.** The seam leg calls
|
|
233
|
+
`to_html_with_egress` directly; the ambient leg renders a `Markdown` node
|
|
234
|
+
through `render_html` with **no policy named** for the `denyNonLocal` fixtures.
|
|
235
|
+
Keep both — the seam leg cannot detect a renderer that stopped reaching the
|
|
236
|
+
seam, which is precisely the state this host was in while the policy was merely
|
|
237
|
+
available.
|
|
238
|
+
|
|
239
|
+
The README's "Destination policy" section carries the host-facing contract and
|
|
240
|
+
the four declared shape differences from the reference host.
|
|
241
|
+
|
|
242
|
+
## Op-stream (hash chain)
|
|
243
|
+
|
|
244
|
+
`fuaran_py.op_stream` is the Python host of the op-stream **hash-chained provenance
|
|
245
|
+
log** — the twin of the F# and TypeScript op-stream tiers. A stream's applied
|
|
246
|
+
`TreeOp` edits are an append-only sequence of `OpRecord` envelopes; a host-side
|
|
247
|
+
SHA-256 chain (`sha256(previousHash | payload)`) links them so the stream is
|
|
248
|
+
tamper-evident. The pre-image is byte-identical across hosts: the versioned
|
|
249
|
+
`StreamEntry` envelope leads with `{"v":2,…}` (the chain format version folded in
|
|
250
|
+
first), wrapped in the canonical delimited `{"seq":…,"actor":…,"op":…}` payload,
|
|
251
|
+
with the op encoded by the shared `fuaran_py.ops.encode_op` + `fuaran_py.canonical`
|
|
252
|
+
(never a re-implemented encoder). The module is **stdlib-only** — unlike the
|
|
253
|
+
Fable-constrained hosts it uses `hashlib.sha256` directly.
|
|
254
|
+
|
|
255
|
+
- **Conformance is the contract.** `tests/test_op_stream.py` loads the shared
|
|
256
|
+
`chain/chain-corpus.json` golden and asserts every computed chain hash equals the
|
|
257
|
+
committed value byte-for-byte — the same corpus the F# and TypeScript hosts
|
|
258
|
+
certify against. A mismatch is a bug in this host's encoder/chain, **never** the
|
|
259
|
+
corpus; do not regenerate the golden to make a test pass.
|
|
260
|
+
- **Version lock-step.** `CHAIN_FORMAT_VERSION` is pinned to the corpus `version`;
|
|
261
|
+
bump it in lock-step with the other hosts + the golden whenever the pre-image
|
|
262
|
+
formula, envelope shape, or hash function changes (the wire-format forward-coupling
|
|
263
|
+
rule now spans the chain envelope too).
|
|
264
|
+
|
|
265
|
+
## Computed-style observer + theme manifest
|
|
266
|
+
|
|
267
|
+
`fuaran_py.style_observer` is the Python twin of `Fuaran.UI.StyleObserver`: it
|
|
268
|
+
reads back a rendered tree's resolved computed styles and derives a fixed
|
|
269
|
+
vocabulary of resolved-style flags (contrast-below-AA, invisible-text,
|
|
270
|
+
accent-indistinct, plus the four manifest-aware checks) the semantic-state channel
|
|
271
|
+
is blind to — as small typed facts, deterministically. The flag + observation
|
|
272
|
+
JSON encode is **byte-identical** to the F# and TypeScript hosts.
|
|
273
|
+
|
|
274
|
+
- `InMemoryStyleObserver` — fixture-driven, substrate-free (tests / headless).
|
|
275
|
+
- `BrowserStyleObserver` — reads **live** `getComputedStyle` under **Pyodide**
|
|
276
|
+
(CPython compiled to WASM running client-side in the browser — the Python
|
|
277
|
+
analogue of the F# Fable / TS browser observers). Browser-API access is behind
|
|
278
|
+
an injectable `BrowserDeps` (default: the `js` interop module, imported lazily so
|
|
279
|
+
the package stays stdlib-only at install time and importable under plain CPython;
|
|
280
|
+
tests inject a fake DOM). `connect()` wires a live `MutationObserver`
|
|
281
|
+
(Pyodide-only) so a theme toggle re-derives automatically.
|
|
282
|
+
|
|
283
|
+
`fuaran_py.theme_manifest` is the Python twin of `Fuaran.UI.ThemeManifest`: a
|
|
284
|
+
DTCG-compatible token model + semantic role bindings + quantified invariants
|
|
285
|
+
(per-role contrast floors, 60-30-10 usage budgets, motion voice). It is the
|
|
286
|
+
contract the observer's manifest-aware tier (`per_node_flags` /
|
|
287
|
+
`verify_usage_budgets`) verifies resolved style against. Both modules are
|
|
288
|
+
stdlib-only; the Pyodide-only `js` / `pyodide.ffi` imports are lazy and
|
|
289
|
+
import-guarded (the `#if FABLE_COMPILER` analogue). Manifest JSON encode + the F#
|
|
290
|
+
`ThemeBridge` (typed-`Theme` projector) are not yet ported (follow-up).
|
|
291
|
+
|
|
292
|
+
## Cross-repo dependencies
|
|
293
|
+
|
|
294
|
+
No upstream dependency on any other sibling. At test time it reads the
|
|
295
|
+
workspace-relative corpus at `../wire-format-fixtures/` (skipped when absent, so
|
|
296
|
+
the repo is standalone-testable). It produces a Python package, not a NuGet pack
|
|
297
|
+
— the workspace `pack-all.ps1` treats it as a no-op.
|
|
298
|
+
|
|
299
|
+
## Public vocabulary discipline
|
|
300
|
+
|
|
301
|
+
`fuaran-py` is OSS-public (Apache 2.0). Per the workspace OSS publication
|
|
302
|
+
boundary, **shipped artefacts** (source, README, package metadata) reference only
|
|
303
|
+
"the Fuaran UI wire format" generically — never a private sibling/package name,
|
|
304
|
+
commercial product names, or the strategic-command names. The specific banned
|
|
305
|
+
list lives in the workspace OSS publication boundary doc, not here. This
|
|
306
|
+
`CLAUDE.md` lives in the public repo, so it observes the same boundary — it names
|
|
307
|
+
no private sibling, package, product, or command.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our
|
|
6
|
+
community a harassment-free experience for everyone, regardless of age, body
|
|
7
|
+
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
|
8
|
+
identity and expression, level of experience, education, socio-economic status,
|
|
9
|
+
nationality, personal appearance, race, caste, color, religion, or sexual
|
|
10
|
+
identity and orientation.
|
|
11
|
+
|
|
12
|
+
We pledge to act and interact in ways that contribute to an open, welcoming,
|
|
13
|
+
diverse, inclusive, and healthy community.
|
|
14
|
+
|
|
15
|
+
## Our Standards
|
|
16
|
+
|
|
17
|
+
Examples of behavior that contributes to a positive environment for our
|
|
18
|
+
community include:
|
|
19
|
+
|
|
20
|
+
- Demonstrating empathy and kindness toward other people
|
|
21
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
22
|
+
- Giving and gracefully accepting constructive feedback
|
|
23
|
+
- Accepting responsibility and apologizing to those affected by our mistakes,
|
|
24
|
+
and learning from the experience
|
|
25
|
+
- Focusing on what is best not just for us as individuals, but for the overall
|
|
26
|
+
community
|
|
27
|
+
|
|
28
|
+
Examples of unacceptable behavior include:
|
|
29
|
+
|
|
30
|
+
- The use of sexualized language or imagery, and sexual attention or advances
|
|
31
|
+
of any kind
|
|
32
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
33
|
+
- Public or private harassment
|
|
34
|
+
- Publishing others' private information, such as a physical or email address,
|
|
35
|
+
without their explicit permission
|
|
36
|
+
- Other conduct which could reasonably be considered inappropriate in a
|
|
37
|
+
professional setting
|
|
38
|
+
|
|
39
|
+
## Enforcement Responsibilities
|
|
40
|
+
|
|
41
|
+
Community leaders are responsible for clarifying and enforcing our standards of
|
|
42
|
+
acceptable behavior and will take appropriate and fair corrective action in
|
|
43
|
+
response to any behavior that they deem inappropriate, threatening, offensive,
|
|
44
|
+
or harmful.
|
|
45
|
+
|
|
46
|
+
Community leaders have the right and responsibility to remove, edit, or reject
|
|
47
|
+
comments, commits, code, wiki edits, issues, and other contributions that are
|
|
48
|
+
not aligned to this Code of Conduct, and will communicate reasons for
|
|
49
|
+
moderation decisions when appropriate.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
This Code of Conduct applies within all community spaces, and also applies when
|
|
54
|
+
an individual is officially representing the community in public spaces.
|
|
55
|
+
|
|
56
|
+
## Enforcement
|
|
57
|
+
|
|
58
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
|
59
|
+
reported to the community leaders responsible for enforcement at
|
|
60
|
+
andrew@fuaran.com. All complaints will be reviewed and investigated
|
|
61
|
+
promptly and fairly.
|
|
62
|
+
|
|
63
|
+
All community leaders are obligated to respect the privacy and security of the
|
|
64
|
+
reporter of any incident.
|
|
65
|
+
|
|
66
|
+
## Attribution
|
|
67
|
+
|
|
68
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
|
69
|
+
version 2.1, available at
|
|
70
|
+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
|
71
|
+
|
|
72
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
|
73
|
+
[https://www.contributor-covenant.org/faq][FAQ].
|
|
74
|
+
|
|
75
|
+
[homepage]: https://www.contributor-covenant.org
|
|
76
|
+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
|
77
|
+
[FAQ]: https://www.contributor-covenant.org/faq
|