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 @@ -26,6 +26,8 @@ Follows [Keep a Changelog](https://keepachangelog.com/); versioning is [SemVer](

- sl-viewer bundle_list + detail_pane property surface (WBS-6.2 #436): `crates/sl-viewer/tests/properties_viewer_bundle_detail.rs` adds 11 proptest properties — `bundle_list::summarize` carries `source_id` through unchanged, matches input `bundle_count`, reflects kind presence (`has_acceptance`/`has_contract`), falls back to `"(no goal)"` when no Intent slice carries a string `goal`, and is deterministic. `detail_pane::extract_detail` carries `source_id` through unchanged, always emits `IntentState::Extracted`, matches `bundle.total_token_estimate()` for the token total, mirrors `Option<String>` fields (`intent_goal`, `context_cwd`, `context_title`) exactly, and is deterministic.

- 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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: The lab_coat_hex_list() doc promises an 'exhaustiveness check' that does not exist.

The doc comment (lines 45–50) claims the list is derived via a 'reflection-on-source approach' over every pub const in lab_coat, and that any new constant addition 'will [be] catch[ed] by the proptest exhaustiveness check below.' Neither is true: the list is hand-maintained (no reflection), and none of the 10 properties cross-checks lab_coat_hex_list() against the full set of lab_coat constants. A new lab_coat::* constant added without extending this list would silently escape coverage — precisely the drift this SSOT test surface is meant to prevent.

Suggested fix: either (a) add a property asserting lab_coat_hex_list().len() equals the documented count (16) and that every lab_coat::* value is present, or (b) reword the doc to state the list must be updated manually and the suite does not assert completeness.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

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 +114 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 string occurs somewhere in the entire stylesheet, not that it is assigned to the corresponding token. A token declaration can be changed or removed while the same color remains in another declaration, a comment, or an unrelated selector, and this property will still pass. Validate the expected variable-to-hex assignment on the same declaration line, as the existing unit test does. [api mismatch]

Severity Level: Major ⚠️
- ❌ Rust palette and CSS token assignments can silently diverge.
- ⚠️ Viewer colors can change for affected theme controls.
- ⚠️ `ThemeColors` users can receive mismatched CSS/Rust 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:** 114:118
**Comment:**
	*Api Mismatch: This only checks that each hex string occurs somewhere in the entire stylesheet, not that it is assigned to the corresponding token. A token declaration can be changed or removed while the same color remains in another declaration, a comment, or an unrelated selector, and this property will still pass. Validate the expected variable-to-hex assignment on the same 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 +155 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: This accepts a required variable when its name appears only in a comment, a var(...) reference, or another non-declaration context. Consequently, removal of the actual custom-property definition can go undetected even though REQUIRED_CSS_VARS documents variables that must exist. Check for a CSS declaration of the form var: rather than an arbitrary substring. [api mismatch]

Severity Level: Major ⚠️
- ❌ Missing viewer variables can invalidate theme styles.
- ⚠️ CSS fallbacks may alter backgrounds, text, or accents.
- ⚠️ Required-token regression can pass CI unnoticed.

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:** 155:159
**Comment:**
	*Api Mismatch: This accepts a required variable when its name appears only in a comment, a `var(...)` reference, or another non-declaration context. Consequently, removal of the actual custom-property definition can go undetected even though `REQUIRED_CSS_VARS` documents variables that must exist. Check for a CSS declaration of the form `var:` rather than an arbitrary substring.

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 +172 to +173

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 substring checks do not require the dark selector to be the exact :root[data-theme="dark"] selector. The fragments could occur in separate or malformed selectors and the property would still pass, allowing the selector consumed by the viewer to be missing. Assert the complete selector string or parse the selector block. [incorrect condition logic]

Severity Level: Major ⚠️
- ❌ Dark-mode selector regressions can escape the token test.
- ⚠️ Viewer dark-theme switching may stop applying color-scheme.
- ⚠️ Browser controls may retain the wrong light/dark styling.

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:** 172:173
**Comment:**
	*Incorrect Condition Logic: The two independent substring checks do not require the dark selector to be the exact `:root[data-theme="dark"]` selector. The fragments could occur in separate or malformed selectors and the property would still pass, allowing the selector consumed by the viewer to be missing. Assert the complete selector string or parse the selector block.

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 +184

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 textual occurrences does not prove that light and dark selectors each contain one color-scheme declaration. Occurrences in comments, duplicate declarations within one rule, or two declarations using the same value all satisfy this assertion while one theme mode remains incorrect. Validate the declarations associated with each selector and their expected values. [incorrect condition logic]

Severity Level: Major ⚠️
- ❌ Browser controls can use the wrong theme color scheme.
- ⚠️ Dark-mode scrollbar and form-control styling may regress.
- ⚠️ Viewer theme switching can pass CI with incomplete CSS.

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:184
**Comment:**
	*Incorrect Condition Logic: Counting textual occurrences does not prove that light and dark selectors each contain one `color-scheme` declaration. Occurrences in comments, duplicate declarations within one rule, or two declarations using the same value all satisfy this assertion while one theme mode remains incorrect. Validate the declarations associated with each selector and their expected values.

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 @@ -315,6 +315,7 @@
"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_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`; `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_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