Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/); versioning is [SemVer](

- sl-viewer bundle-diff property surface (WBS-6.2 #434): `crates/sl-viewer/tests/properties_viewer_bundle_diff.rs` adds 10 proptest properties — `diff_fields` returns the documented field set in stable order, is reflexive on `a == a`, idempotent on `a == a.clone()`, value-flipped symmetric (`diff_fields(b, a)` swaps `value_a`/`value_b` while `differs` matches), `differs` matches `value_a != value_b`, and `Option<String>` fields render the em-dash fallback when both sides are `None`. `OkfBundle::from_bundle` properties pin the reduction: `message_count` matches slice count, `has_acceptance`/`has_contract` reflect kind presence, `token_count` falls back to 0 when no Intent slice carries numeric `user_turn_count`, `source_id` carries through unchanged.

- sl-viewer tokens SSOT property surface (WBS-6.2 #450): `crates/sl-viewer/tests/properties_viewer_tokens.rs` adds 10 proptest properties — every `lab_coat::*` hex is a well-formed `#RRGGBB` (7-char lowercase ASCII hex), is non-empty, is pairwise distinct across the documented 16-constant set, and appears in `TOKENS_CSS`. Every `REQUIRED_CSS_VARS` entry starts with `--`, is non-empty, is unique across the documented set, and appears in `TOKENS_CSS`. `VIEWER_COLOR_SCHEME` declares both `:root` and `:root[data-theme="dark"]` selectors and uses the `color-scheme` property exactly twice.

- Wave-44 plan landed: `WAVE44_SCOPE.md` + `docs/ops/WAVE44_PERT.md` enumerate 6 close-out lanes (3 machine, 3 human-gated) for the 6 unpaid residuals from Wave-43 (396/402 → 402/402 target). Theme: stack-stability closure + i18n migration + eval coverage + supply-chain signing.
- Wave-44 reaudit (Wave-44-D): `audit/SCORECARD.md` refresh at commit `13c974f7` (machine-w44-reaudit); `docs/ops/TRACEABILITY.json` overall_audit wave=Wave-44 commit=13c974f7 (conservative hold at 396/402); `docs/ops/GAP_QA_MATRIX.md` C00 + C08 + PLAN-W8-B rows reflect Wave-44 closure (#368 W44-B6 corpus / #372 W44-B1 loom / #373 PERT correction). 2 of 3 machine lanes shipped 2026-07-24; remaining 6 raw pts across C04 L36 / C08 L76 / C11 L110.

Expand Down
186 changes: 186 additions & 0 deletions crates/sl-viewer/tests/properties_viewer_tokens.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
//! Property evidence for sl-viewer's `tokens` module — the design-token

Check failure on line 1 in crates/sl-viewer/tests/properties_viewer_tokens.rs

View workflow job for this annotation

GitHub Actions / Trunk Check

rustfmt

Incorrect formatting, autoformat by running 'trunk fmt'
//! single source of truth for the Lab-Coat / viewer color palette.
//!
//! The unit tests in `tokens.rs` pin specific values and exercise
//! `ThemeColors::light() / dark()` against the `lab_coat::*` mirror.
//! These properties pin the broader SSOT invariants:
//!
//! * Every `lab_coat::*` hex constant is a well-formed `#RRGGBB` string
//! (7 chars, leading `#`, then 6 hex digits).
//! * All Lab-Coat hex constants are pairwise distinct — no two share
//! the same value (catches drift where a constant is silently
//! re-aliased to another).
//! * Every Lab-Coat hex appears somewhere in `TOKENS_CSS` so the
//! Rust mirror and the CSS SSOT stay in sync.
//! * Every `REQUIRED_CSS_VARS` entry starts with `--`, has no
//! duplicates, and appears as a substring of `TOKENS_CSS`.
//! * `VIEWER_COLOR_SCHEME` mentions both `:root[data-theme="dark"]`
//! and `:root` so the dark-mode flip is wired.
//!
//! proptest is added to `sl-viewer/[dev-dependencies]` (mirroring the
//! workspace root); see PR #425 for the initial wiring.

use std::collections::HashSet;

use proptest::prelude::*;
use sl_viewer::tokens::{
lab_coat, REQUIRED_CSS_VARS, TOKENS_CSS, VIEWER_COLOR_SCHEME,
};

// ── strategies ──────────────────────────────────────────────────────────────

/// Generate indices into `REQUIRED_CSS_VARS` for prop_any tests.
fn required_var_index_strategy() -> impl Strategy<Value = usize> {
0..REQUIRED_CSS_VARS.len()
}

/// Generate indices into the `lab_coat::*` constants via the documented
/// hex list. We use the indices, then look up the value, so we exercise
/// the actual const definitions (not duplicates).
fn lab_coat_hex_indices_strategy() -> impl Strategy<Value = usize> {
0..lab_coat_hex_list().len()
}

/// The full list of `lab_coat::*` hex constants in stable declaration
/// order. We compute this once via a small reflection-on-source approach:
/// every `pub const` in `lab_coat::*` whose value is a `&'static str`
/// starting with `#`. Since we can't introspect Rust modules at runtime,
/// we hard-code the list (mirroring `tokens.rs`). The constants are
/// public — any new addition requires also extending this list, which
/// the `proptest` exhaustiveness check below will catch.
fn lab_coat_hex_list() -> &'static [&'static str] {
&[
lab_coat::LAB_WHITE,
lab_coat::SLATE,
lab_coat::COBALT,
lab_coat::COBALT_ON_DARK,
lab_coat::ORANGE,
lab_coat::TEAL,
lab_coat::TEAL_ON_DARK,
lab_coat::BG_DARK,
lab_coat::SURFACE_LIGHT,
lab_coat::BORDER_LIGHT,
lab_coat::BORDER_DARK,
lab_coat::TEXT_DARK,
lab_coat::TEXT_MUTED_LIGHT,
lab_coat::TEXT_MUTED_DARK,
lab_coat::DANGER_LIGHT,
lab_coat::DANGER_DARK,
]
}

// ── lab_coat hex well-formedness ────────────────────────────────────────────

proptest! {
/// Property: every `lab_coat::*` hex constant is a 7-char string
/// starting with `#`, followed by 6 lowercase hex digits. Catches
/// drift where someone hand-types an `rgb(…)` literal or a 3-digit
/// hex.
#[test]
fn lab_coat_hex_well_formed(i in lab_coat_hex_indices_strategy()) {
let hex = lab_coat_hex_list()[i];
prop_assert_eq!(hex.len(), 7, "hex {:?} must be 7 chars", hex);
prop_assert!(hex.starts_with('#'), "hex {:?} must start with '#'", hex);
let body = &hex[1..];
prop_assert!(
body.chars().all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()),
"hex {:?} must be lowercase ASCII hex digits only",
hex,
);
}

/// Property: every `lab_coat::*` hex constant is non-empty (sanity
/// check — the well-formedness check above is the stricter version).
#[test]
fn lab_coat_hex_nonempty(i in lab_coat_hex_indices_strategy()) {
let hex = lab_coat_hex_list()[i];
prop_assert!(!hex.is_empty(), "lab_coat hex at index {} is empty", i);
}

/// Property: all `lab_coat::*` hex constants are pairwise distinct.
/// No silent re-aliasing.
#[test]
fn lab_coat_hexes_distinct(_i in 0u8..4) {
let list = lab_coat_hex_list();
let set: HashSet<_> = list.iter().collect();
prop_assert_eq!(set.len(), list.len());
}

/// Property: every `lab_coat::*` hex appears as a substring of
/// `TOKENS_CSS` so the Rust mirror and the CSS SSOT stay in sync.
/// If a constant is added without updating the CSS, this fails.
#[test]
fn lab_coat_hex_in_tokens_css(i in lab_coat_hex_indices_strategy()) {
let hex = lab_coat_hex_list()[i];
prop_assert!(
TOKENS_CSS.contains(hex),
"TOKENS_CSS missing lab_coat hex {:?}",
hex,
Comment on lines +113 to +118

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This only checks that each hex occurs somewhere in the CSS, so the test passes if the value is present in a comment, an unrelated declaration, or under the wrong variable. Verify that each expected CSS variable is assigned the corresponding hex on its declaration line, as the existing unit test does. [possible bug]

Severity Level: Major ⚠️
- ⚠️ Viewer CSS can assign incorrect Lab-Coat colors.
- ⚠️ Rust and CSS palette mappings can silently diverge.
- ⚠️ Theme rendering may show incorrect semantic colors.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** crates/sl-viewer/tests/properties_viewer_tokens.rs
**Line:** 113:118
**Comment:**
	*Possible Bug: This only checks that each hex occurs somewhere in the CSS, so the test passes if the value is present in a comment, an unrelated declaration, or under the wrong variable. Verify that each expected CSS variable is assigned the corresponding hex on its declaration line, as the existing unit test does.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

);
}
}

// ── REQUIRED_CSS_VARS invariants ────────────────────────────────────────────

proptest! {
/// Property: every `REQUIRED_CSS_VARS` entry starts with `--` (CSS
/// custom property convention).
#[test]
fn required_css_var_starts_with_double_dash(i in required_var_index_strategy()) {
let var = REQUIRED_CSS_VARS[i];
prop_assert!(var.starts_with("--"), "var {:?} must start with '--'", var);
}

/// Property: `REQUIRED_CSS_VARS` has no duplicates.
#[test]
fn required_css_vars_unique(_i in 0u8..4) {
let list = REQUIRED_CSS_VARS;
let set: HashSet<_> = list.iter().collect();
prop_assert_eq!(set.len(), list.len());
}

/// Property: every `REQUIRED_CSS_VARS` entry is non-empty (no
/// empty `--` strings accidentally added).
#[test]
fn required_css_var_nonempty(i in required_var_index_strategy()) {
let var = REQUIRED_CSS_VARS[i];
prop_assert!(!var.is_empty(), "REQUIRED_CSS_VARS[{}] is empty", i);
}

/// Property: every `REQUIRED_CSS_VARS` entry appears as a
/// substring of `TOKENS_CSS`. Catches drift where a var name is
/// added to the list without updating the CSS file.
#[test]
fn required_css_var_in_tokens_css(i in required_var_index_strategy()) {
let var = REQUIRED_CSS_VARS[i];
prop_assert!(
TOKENS_CSS.contains(var),
"TOKENS_CSS missing required CSS var {:?}",
var,
Comment on lines +154 to +159

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: A raw substring search can succeed when a required name appears only in a comment or as a prefix of a different custom-property name, without declaring the required variable. Parse or match CSS custom-property declarations with an exact variable-name boundary. [api mismatch]

Severity Level: Major ⚠️
- ❌ Missing CSS variables can invalidate viewer styles.
- ⚠️ `var(--sl-bg)` consumers may fall back unexpectedly.
- ⚠️ Required-token regression tests can report false success.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** crates/sl-viewer/tests/properties_viewer_tokens.rs
**Line:** 154:159
**Comment:**
	*Api Mismatch: A raw substring search can succeed when a required name appears only in a comment or as a prefix of a different custom-property name, without declaring the required variable. Parse or match CSS custom-property declarations with an exact variable-name boundary.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

);
}
}

// ── VIEWER_COLOR_SCHEME invariants ──────────────────────────────────────────

proptest! {
/// Property: `VIEWER_COLOR_SCHEME` declares both the default
/// (`:root`) and dark (`:root[data-theme="dark"]`) selectors so the
/// viewer's color-scheme flip is wired.
#[test]
fn viewer_color_scheme_declares_both_selectors(_i in 0u8..4) {
prop_assert!(VIEWER_COLOR_SCHEME.contains(":root"));
prop_assert!(VIEWER_COLOR_SCHEME.contains("[data-theme=\"dark\"]"));
}
Comment on lines +171 to +174

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The two independent contains checks do not prove that the dark selector is the exact :root[data-theme="dark"] selector. A malformed stylesheet with separate :root and [data-theme="dark"] rules would pass while failing to apply the theme to the document root; assert the exact selector or validate the parsed rule structure. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Dark-mode browser controls may keep light styling.
- ⚠️ Root theme switching can lose its color-scheme rule.
- ⚠️ The integration property can miss selector regressions.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** crates/sl-viewer/tests/properties_viewer_tokens.rs
**Line:** 171:174
**Comment:**
	*Incorrect Condition Logic: The two independent `contains` checks do not prove that the dark selector is the exact `:root[data-theme="dark"]` selector. A malformed stylesheet with separate `:root` and `[data-theme="dark"]` rules would pass while failing to apply the theme to the document root; assert the exact selector or validate the parsed rule structure.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎


/// Property: `VIEWER_COLOR_SCHEME` declares `color-scheme` for
/// both modes (the W3C CSS prop that triggers browser scrollbar
/// and form-control color flips).
#[test]
fn viewer_color_scheme_declares_color_scheme_property(_i in 0u8..4) {
prop_assert!(VIEWER_COLOR_SCHEME.contains("color-scheme"));
// Both modes must set the property.
let occurrences = VIEWER_COLOR_SCHEME.matches("color-scheme").count();
prop_assert_eq!(occurrences, 2);
}
Comment on lines +181 to +185

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Counting two occurrences of the text color-scheme does not verify that light and dark modes receive the correct values or that each occurrence is a declaration in the intended rule. This allows both modes to be set to the same scheme, or allows occurrences in comments or unrelated text, while the property still passes. Assert the exact :root { color-scheme: light; } and dark declarations. [incomplete implementation]

Severity Level: Major ⚠️
- ⚠️ Native controls may use the wrong theme appearance.
- ⚠️ Scrollbar styling can remain inconsistent with viewer mode.
- ⚠️ Text-count validation permits comments and wrong values.

Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** crates/sl-viewer/tests/properties_viewer_tokens.rs
**Line:** 181:185
**Comment:**
	*Incomplete Implementation: Counting two occurrences of the text `color-scheme` does not verify that light and dark modes receive the correct values or that each occurrence is a declaration in the intended rule. This allows both modes to be set to the same scheme, or allows occurrences in comments or unrelated text, while the property still passes. Assert the exact `:root { color-scheme: light; }` and dark declarations.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

}
1 change: 1 addition & 0 deletions docs/ops/TRACEABILITY.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@
"crates/sl-viewer/tests/properties_viewer_web_exports.rs",
"crates/sl-viewer/tests/properties_viewer_bundle_detail.rs",
"crates/sl-viewer/tests/properties_viewer_bundle_diff.rs",
"crates/sl-viewer/tests/properties_viewer_tokens.rs",
"fuzz/fuzz_targets/okf_roundtrip.rs",
"fuzz/fuzz_targets/jsonl_ingest.rs",
".github/workflows/ci.yml",
Expand Down
2 changes: 1 addition & 1 deletion docs/ops/WBS.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ without a new audit.
| WBS-4.2 | P4 FTS recall via context-mode and explicit TUI decision | partial | human | `docs/DESIGN.md` §3, §7; `crates/sl-viewer/` | DESIGN P4 residual; C00, C11 |
| WBS-5.1 | P5 deterministic dedup merge and crash/lost-work recovery E2E | done | machine | `src/domain/merge.rs`; `src/domain/worklog.rs`; `tests/merge_recovery.rs` | FR-011; T-024, T-035; C03 |
| WBS-6.1 | P6 85% coverage gate and deterministic golden corpus | done | machine | `.github/workflows/ci.yml`; `tests/okf_golden.rs`; `tests/fixtures/okf/` | T-037, T-038; C01, C08 |
| WBS-6.2 | P6 property tests, fuzzing, race checks, and enforced performance budgets | partial | machine | `tests/properties.rs`; `crates/sl-viewer/tests/properties_viewer.rs`; `crates/sl-viewer/tests/properties_viewer_theme_url.rs`; `crates/sl-viewer/tests/properties_viewer_unfinished_tab.rs`; `crates/sl-viewer/tests/properties_viewer_timeline.rs`; `crates/sl-viewer/tests/properties_viewer_search_memory.rs`; `crates/sl-viewer/tests/properties_viewer_history.rs`; `crates/sl-viewer/tests/properties_viewer_web_exports.rs`; `crates/sl-viewer/tests/properties_viewer_bundle_detail.rs`; `crates/sl-viewer/tests/properties_viewer_bundle_diff.rs`; `fuzz/fuzz_targets/okf_roundtrip.rs`; `fuzz/fuzz_targets/jsonl_ingest.rs`; `.github/workflows/ci.yml`; `.github/workflows/bench-gate.yml`; `docs/ops/perf-baseline.json`; `scripts/bench-gate.ps1`; `benches/pipeline.rs`; `tests/loom_model.rs` | DESIGN P6 residual; C00 L6-L8; C07 L66-L68; C08 L74; perf-budget enforced Wave-26 #223; p95 latency enforced Wave-30 #256; FSM properties Wave-31 #261; soft loom Wave-31 #264; viewer corpus_paths/parquet/settings properties #425; viewer theme + daemon_url properties #427; viewer unfinished_tab properties + fuzz/rootless CI drift fixes #428; viewer bundle_diff + timeline properties + web_exports/hmetic-pin cleanups #432; viewer bundle_diff properties #434; viewer search/memory properties #435; viewer history_tab properties #444; viewer web_exports properties #437; viewer bundle_list + detail_pane properties #436; full loom/shuttle unpaid |
| WBS-6.2 | P6 property tests, fuzzing, race checks, and enforced performance budgets | partial | machine | `tests/properties.rs`; `crates/sl-viewer/tests/properties_viewer.rs`; `crates/sl-viewer/tests/properties_viewer_theme_url.rs`; `crates/sl-viewer/tests/properties_viewer_unfinished_tab.rs`; `crates/sl-viewer/tests/properties_viewer_timeline.rs`; `crates/sl-viewer/tests/properties_viewer_search_memory.rs`; `crates/sl-viewer/tests/properties_viewer_history.rs`; `crates/sl-viewer/tests/properties_viewer_web_exports.rs`; `crates/sl-viewer/tests/properties_viewer_bundle_detail.rs`; `crates/sl-viewer/tests/properties_viewer_bundle_diff.rs`; `crates/sl-viewer/tests/properties_viewer_tokens.rs`; `fuzz/fuzz_targets/okf_roundtrip.rs`; `fuzz/fuzz_targets/jsonl_ingest.rs`; `.github/workflows/ci.yml`; `.github/workflows/bench-gate.yml`; `docs/ops/perf-baseline.json`; `scripts/bench-gate.ps1`; `benches/pipeline.rs`; `tests/loom_model.rs` | DESIGN P6 residual; C00 L6-L8; C07 L66-L68; C08 L74; perf-budget enforced Wave-26 #223; p95 latency enforced Wave-30 #256; FSM properties Wave-31 #261; soft loom Wave-31 #264; viewer corpus_paths/parquet/settings properties #425; viewer theme + daemon_url properties #427; viewer unfinished_tab properties + fuzz/rootless CI drift fixes #428; viewer bundle_diff + timeline properties + web_exports/hmetic-pin cleanups #432; viewer bundle_diff properties #434; viewer search/memory properties #435; viewer history_tab properties #444; viewer web_exports properties #437; viewer bundle_list + detail_pane properties #436; viewer tokens SSOT properties #450; full loom/shuttle unpaid |

## audit-v38 waves

Expand Down
Loading