Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 94 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -235,28 +235,65 @@ jobs:
text=True,
))
packages = {package["name"]: package for package in metadata["packages"]}
consumers = (
"labcolors-wasm",
"labcolors-ffi",
"labcolors-conformance",
)
core = packages["labcolors-core"]
if core["features"].get("default") != ["wcag22-feasibility"]:
raise SystemExit("labcolors-core default capability set drifted")

dependencies = [
protocol_core = [
dependency
for dependency in packages["labcolors-wasm"]["dependencies"]
for dependency in packages["labcolors-protocol"]["dependencies"]
if dependency["name"] == "labcolors-core" and dependency["kind"] is None
]
if len(dependencies) != 1:
raise SystemExit("labcolors-wasm must have one normal labcolors-core edge")
dependency = dependencies[0]
if dependency["uses_default_features"] or dependency["features"]:
raise SystemExit("labcolors-wasm enabled an unprojected core capability")
PY
if len(protocol_core) != 1:
raise SystemExit("labcolors-protocol must have one normal labcolors-core edge")
protocol_core = protocol_core[0]
if protocol_core["uses_default_features"]:
raise SystemExit("labcolors-protocol must disable Core default capabilities")
if protocol_core["features"] != ["wcag22-feasibility"]:
raise SystemExit(
"labcolors-protocol must own exactly wcag22-feasibility, got "
f"{protocol_core['features']}"
)

feature_tree="$(cargo tree -p labcolors-wasm --edges normal -e features)"
if grep -Fq 'labcolors-core feature "wcag22-feasibility"' <<<"$feature_tree"; then
echo "labcolors-wasm resolved an unprojected core capability" >&2
exit 1
fi
echo "core capability projection: PASS"
for consumer in consumers:
core_dependencies = [
dependency
for dependency in packages[consumer]["dependencies"]
if dependency["name"] == "labcolors-core" and dependency["kind"] is None
]
if len(core_dependencies) != 1:
raise SystemExit(f"{consumer} must have one normal labcolors-core edge")
core_dependency = core_dependencies[0]
if core_dependency["uses_default_features"]:
raise SystemExit(f"{consumer} must disable Core default capabilities")
if core_dependency["features"]:
raise SystemExit(
f"{consumer} direct Core edge must own no optional capability, got "
f"{core_dependency['features']}"
)
protocol_dependencies = [
dependency
for dependency in packages[consumer]["dependencies"]
if dependency["name"] == "labcolors-protocol" and dependency["kind"] is None
]
if len(protocol_dependencies) != 1:
raise SystemExit(f"{consumer} must have one normal labcolors-protocol edge")
feature_tree = subprocess.check_output(
["cargo", "tree", "-p", consumer, "--edges", "normal", "-e", "features"],
text=True,
)
if 'labcolors-core feature "wcag22-feasibility"' not in feature_tree:
raise SystemExit(
f"{consumer} did not resolve protocol-owned wcag22-feasibility"
)

print("core capability projection: PASS")
PY
- name: reject WCAG22 source-route redirects and proof vacuums
run: python3 scripts/test_wcag22_source_binding.py
- name: prove WCAG22 sRGB8 Q55 artifact over the full finite domain
Expand All @@ -278,13 +315,25 @@ jobs:
--admit-package-version 0.2.0
--admit-sample-count 5
)
if test "$GITHUB_EVENT_NAME" = pull_request; then
historical_snapshot=6001cf41e0a8364f25543e7955ceaf64d50129b4
historical_root="$RUNNER_TEMP/wcag22-feasibility-slice-a-$GITHUB_RUN_ID-$GITHUB_RUN_ATTEMPT"
git worktree add --detach "$historical_root" "$historical_snapshot"
cleanup_history() {
git worktree remove --force "$historical_root"
}
trap cleanup_history EXIT
(
cd "$historical_root"
python3 scripts/check_wcag22_feasibility_benchmark.py \
"$artifact" "${protocol[@]}" --self-test
fi
python3 scripts/check_wcag22_feasibility_benchmark.py \
"$artifact" "${protocol[@]}" \
--verify-current-subjects \
"$artifact" "${protocol[@]}" \
--verify-current-subjects \
--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e \
--self-test
)
cleanup_history
trap - EXIT
python3 scripts/check_wcag22_feasibility_applicability.py \
"$artifact" \
--artifact-sha256 7e9ffcbdd9d5d50fe681f511c34fc5c5dd270e9c475ce23ae56e9776922a3c5e \
--self-test

Expand Down Expand Up @@ -392,10 +441,32 @@ jobs:
node-version: ${{ env.NODE_TOOLCHAIN }}
cache: npm
cache-dependency-path: packages/colors/package-lock.json
- name: "verify committed #295 canonical whole-call WASM boundary evidence"
# The admitted JSON binds exact deterministic bytes/shapes and the
# reviewed Uint8Array package root to this built Linux x64 WASM. Fresh
# latency/maxRSS/pages remain observations only.
working-directory: packages/colors
run: >-
node bench/wcag22-feasibility-boundary.bench.mjs
--verify
- name: independently fingerprint the exact #295 WASM
run: |
bytes="$(wc -c < packages/colors/pkg/labcolors_bg.wasm | tr -d '[:space:]')"
sha256="$(sha256sum packages/colors/pkg/labcolors_bg.wasm | cut -d ' ' -f1)"
echo "canonical candidate raw=${bytes}B sha256=${sha256}"
- name: "upload exact #295 verified whole-call evidence"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: wcag22-feasibility-wasm-boundary-${{ github.sha }}-attempt-${{ github.run_attempt }}
path: |
packages/colors/bench/wcag22-feasibility-wasm-boundary-v1.json
packages/colors/pkg/labcolors_bg.wasm
if-no-files-found: error
retention-days: 30
- name: enforce measured WASM raw-byte budget
# Issue #284 owns the immutable measured ceiling baseline. The checker
# separately binds this current Linux x64 artifact's exact SHA-256 and
# rejects growth above that ceiling. gzip is transport diagnostics only.
# Issue #295 owns the current exact Linux x64 measurement and zero-headroom
# ceiling. Issue #284 remains the immutable V1 build-recipe/baseline input.
# gzip is transport diagnostics only.
run: node scripts/check-wasm-size-budget.mjs
- name: "@labpics/colors: typecheck + runtime tests"
# Now that pkg/ exists (built above), the package's public types resolve
Expand Down
18 changes: 18 additions & 0 deletions .github/workflows/native-conformance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,29 @@ jobs:
# $GITHUB_WORKSPACE монтируется read-only; сборка идёт в /work в контейнере.
- name: swift conformance in swift:6.1.3 container
run: |
evidence_dir="$RUNNER_TEMP/labcolors-swift-evidence"
rm -rf "$evidence_dir"
mkdir -p "$evidence_dir"
subject_sha=$(git rev-parse HEAD)
docker run --rm \
-v "$GITHUB_WORKSPACE":/src:ro \
-v "$evidence_dir":/evidence \
-e LABCOLORS_SUBJECT_SHA="$subject_sha" \
-e LABCOLORS_SWIFT_CONTAINER="$SWIFT_CONTAINER" \
-e LABCOLORS_SWIFT_EVIDENCE_DIR=/evidence \
-e LABCOLORS_EVIDENCE_MODE=canonical-ci \
"$SWIFT_CONTAINER" \
bash /src/bindings/swift/ci/run-conformance.sh

- name: upload versioned UniFFI/Swift observation
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: uniffi-swift-observation-${{ github.run_id }}-${{ github.run_attempt }}
path: ${{ runner.temp }}/labcolors-swift-evidence
if-no-files-found: ignore
retention-days: 14

# ССЫЛОЧНАЯ джоба (НЕ активный гейт): нативный macOS/arm64. Требует ПЛАТНЫХ
# GitHub-hosted macOS-минут, которые владелец исключил навсегда — поэтому
# гейтирована `workflow_dispatch` и НЕ запускается на PR/push (не блокирует).
Expand Down
36 changes: 24 additions & 12 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ Rust различаются, потому что это разные delivery su

## [Unreleased]

Атомарная numerical-decision граница (#292) и exact WCAG 2.2 evaluator для
финальной sRGB8-пары (#284). Существующая цветовая эмиссия, config fingerprint
и adaptive runtime не меняются, но Rust/npm capability API и conformance pack
изменены; следующий release обязан получить согласованный 0.x version bump.
Атомарная numerical-decision граница (#292), exact WCAG 2.2 evaluator для
финальной sRGB8-пары (#284) и bounded complete-feasibility compiler (#295).
Существующая цветовая эмиссия, config fingerprint и adaptive runtime не
меняются, но Rust/npm/Swift transport API и conformance pack изменены;
следующий release обязан получить согласованный 0.x version bump.
Migration-note: [exact alpha / typed Glow](docs/migrations/exact-alpha-glow.md),
дополнение ADR-0004 от 2026-07-12.

Expand All @@ -22,6 +23,15 @@ Migration-note: [exact alpha / typed Glow](docs/migrations/exact-alpha-glow.md),
source bindings и SHA-256 live typed registry admission-row.
- npm package несёт byte-exact profile/table/proof в `evidence/`; release
verifier и clean-install gate повторно проверяют их хэши и содержимое.
- `labcolors-protocol` задаёт единственную versioned bytes→Core→wire границу
complete-feasibility. npm принимает только `Uint8Array`, Swift — `Data` или
`[UInt8]`;
обе поверхности сохраняют `Success(Feasible | Infeasible | NotEvaluated)`
либо typed `Failure` и не воспроизводят математику Core.
- Conformance pack 5.0.0 добавляет ровно одно семейство
`wcag22-feasibility.json`: exact 7/2/0/92/59, mixed/all NotApplicable,
typed conflict/resource failures и opaque-ID law. Шесть прежних family
остаются byte-identical.

### Breaking (Rust API)

Expand Down Expand Up @@ -60,14 +70,16 @@ Migration-note: [exact alpha / typed Glow](docs/migrations/exact-alpha-glow.md),
capsules. Profile V1, proof ID `wcag22-srgb8-full-domain-q55-v1` и package path
`evidence/wcag22-srgb8-q55-proof-v1.json` не меняются: это отдельные version
domains, а доказанная математика и finite artifact прежние.
- WASM size contract сохраняет immutable измерение #284 как происхождение
точного raw-byte ceiling и отдельно аттестует SHA текущего воспроизводимого
артефакта. Proof-only recertification больше не переписывает baseline задним
числом; ceiling остаётся `454385 B` без запаса.
- Conformance pack 4.0.0 добавляет `wcag22.json`; `packDigest` закономерно
изменён. `manifest.numericalCapabilities` зеркалит single public V2 core
manifest (coverage `migrated-sites-only-v1`, FNV-1a-32 drift-checksum над
canonical length-prefixed preimage).
- WASM size history стала append-only: immutable V1 сохраняет допуск #284
(`454385 B`), а V2 допускает полный transport #295 как точные `521240 B` /
`d37841…9ca0` с нулевым headroom и ссылкой на неизменяемый V1 build recipe.
Canonical whole-call artifact фиксирует 10 крайних форм × 5 свежих процессов;
latency, process maxRSS и WASM pages остаются наблюдениями, не SLO.
- Conformance pack 4.0.0 добавил `wcag22.json`; pack 5.0.0 добавляет только
versioned complete-feasibility transport family, поэтому `packDigest`
закономерно изменён. `manifest.numericalCapabilities` зеркалит single public
V2 core manifest (coverage `migrated-sites-only-v1`, FNV-1a-32
drift-checksum над canonical length-prefixed preimage).
- Release manifest schema v2: секция `numericalSites` заменена на
`numericalCapabilities`; release verifier и Swift conformance-тесты
пересчитывают capability checksum независимо от Rust-кода.
Expand Down
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ NamedRoleTable
source-over/screen операции несут проверяемый профиль и `bit-exact`
сертификат; Glow требует явный decision profile и может завершиться
типизированным `Indeterminate` без CSS fallback.
- **Полная проверка конечного домена.** Клиент может объявить opaque relations,
точные соседние sRGB8-цвета и критерии WCAG 2.2; Core полностью перечислит
зарегистрированную neutral-axis и вернёт packed feasible partition с
доказательством. Это проверка выполнимости, а не скрытая политика выбора.
- **Непрерывные семейства.** `ColorCurve` и реализации `NeutralCurve`/`AccentCurve` доступны как низкоуровневые вычислительные примитивы.
- **Браузерное применение.** `applyTheme`, `watchTheme`, `adaptTheme` и `effectiveBackground` связывают результат WASM с локальной областью DOM.

Expand Down Expand Up @@ -308,6 +312,7 @@ Roadmap, текущий SHA и активный PR хранятся только
```text
crates/
├── labcolors-core — математика, конфиг, resolve и результаты
├── labcolors-protocol — единая versioned bytes→Core→wire граница
├── labcolors-wasm — WASM-граница
├── labcolors-ffi — нативная FFI-граница
└── labcolors-conformance — общие тест-векторы
Expand Down
4 changes: 3 additions & 1 deletion bindings/swift/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Сгенерированные UniFFI-биндинги — производный артефакт, генерируется в CI
# (uniffi-bindgen) ПЕРЕД `swift test`. В репозитории каталоги несут лишь .gitkeep.
# (uniffi-bindgen) ПЕРЕД `swift test`. Единственный отслеживаемый Swift-source
# ниже — hand-written protocol consumer.
Sources/LabColors/*.swift
!Sources/LabColors/Wcag22FeasibilityProtocol.swift
Sources/labcolorsFFI/*.h
Sources/labcolorsFFI/module.modulemap
Sources/labcolorsFFI/*.modulemap
Expand Down
7 changes: 4 additions & 3 deletions bindings/swift/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
// Swift-контейнере на Linux x86_64; ручной macOS/arm64 path не является
// достигнутой аттестацией. Файлы в `Sources/LabColors` и
// `Sources/labcolorsFFI` генерирует uniffi-bindgen ПЕРЕД `swift test` (см.
// .github/workflows/native-conformance.yml). В репозитории эти каталоги несут
// лишь .gitkeep — сгенерированное не коммитится.
// .github/workflows/native-conformance.yml). Единственный hand-written source
// в `Sources/LabColors` — exhaustive protocol wrapper
// `Wcag22FeasibilityProtocol.swift`; сгенерированное не коммитится.
import PackageDescription

let package = Package(
Expand All @@ -19,7 +20,7 @@ let package = Package(
// labcolorsFFI.h + labcolorsFFI.modulemap → переименовывается в
// module.modulemap в CI). Экспортирует extern-C FFI-символы ядра.
.systemLibrary(name: "labcolorsFFI", path: "Sources/labcolorsFFI"),
// Swift-обёртка (сгенерированный labcolors.swift) поверх системного модуля.
// Сгенерированный labcolors.swift + hand-written typed byte wrapper.
.target(
name: "LabColors",
dependencies: ["labcolorsFFI"],
Expand Down
Loading
Loading