Skip to content

test(viewer): session-ledger worklog projector proptest surface (WBS-6.2 #460) - #476

Closed
KooshaPari wants to merge 3 commits into
mainfrom
fix/viewer-mock-data-properties-20260809
Closed

test(viewer): session-ledger worklog projector proptest surface (WBS-6.2 #460)#476
KooshaPari wants to merge 3 commits into
mainfrom
fix/viewer-mock-data-properties-20260809

Conversation

@KooshaPari

@KooshaPari KooshaPari commented Aug 10, 2026

Copy link
Copy Markdown
Owner

User description

Summary

Adds crates/sl-viewer/tests/properties_session_ledger_worklog.rs with 11 proptest properties pinning session_ledger::detect_unfinished, project_unfinished_work, and WorklogProjection::from_session — the crash-recovery / lost-work projector (WBS-6.2 #460).

detect_unfinished (8 properties)

  • Empty sessions → None.
  • Final Role::UserAwaitingAssistantResponse.
  • Final Role::Tool / Role::SubagentInterruptedExecution.
  • Final assistant turn with any of 9 documented completion markers → None.
  • Final assistant turn without marker → MissingCompletionMarker.
  • UnfinishedWorkItem carries originating session id, corpus, message count.
  • summary is bounded ≤ 241 chars, single-line.

project_unfinished_work (2 properties)

  • One item per unfinished session, in input order.
  • Deterministic.

WorklogProjection::from_session (1 property)

  • Carries message_count and matches detect_unfinished exactly.

Validation

  • cargo test -p sl-viewer --test properties_session_ledger_worklog --features "desktop parquet" --locked — 11 passed
  • cargo fmt --all --check — clean

WBS / TRACEABILITY

WBS-6.2 evidence list and TRACEABILITY.json gain crates/sl-viewer/tests/properties_session_ledger_worklog.rs. CHANGELOG Unreleased documents the new surface.


CodeAnt-AI Description

Add property coverage for viewer loading states and session-ledger validation

What Changed

  • Adds property tests that keep viewer skeleton layouts, labels, defaults, and row limits consistent.
  • Verifies OKF documents preserve source and corpus metadata, start valid, and report clear errors for invalid versions, mismatched sources, duplicate entities, and dangling relations.
  • Verifies unfinished-session detection identifies work awaiting a response, interrupted execution, and missing completion markers while preserving concise session summaries and metadata.
  • Verifies unfinished work is projected in input order, deterministically, and stays aligned with session message counts.
  • Records the new test coverage in the workplan, traceability data, and changelog.

Impact

✅ Consistent viewer loading states
✅ Clearer export validation errors
✅ More reliable unfinished-work recovery

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

SessionLedger Bot added 3 commits August 9, 2026 19:37
)

Adds crates/sl-viewer/tests/properties_viewer_async_states.rs with
7 proptest properties pinning the async_states SSOT:

* SkeletonLayout::default() is Bundles.
* SkeletonLayout exposes exactly three variants
  (Bundles, ListDetail, StreamFeed).
* Every variant's Debug label is non-empty, single-line, and
  matches one of the documented names.
* SkeletonLayout::default() matches the first arm in the match
  block in ContentSkeleton.
* list_rows.clamp(3, 6) lands in [3, 6] for every input.
* The clamp is monotonic non-decreasing.
* The clamp has the documented fixed points (0/2 -> 3, 6/MAX -> 6).

Updates WBS-6.2 evidence list, TRACEABILITY.json, and CHANGELOG.
…(WBS-6.2 #459)

Adds crates/sl-viewer/tests/properties_session_ledger_okf.rs with
12 proptest properties pinning the session-ledger OKF SSOT:

* OkfDocument::new(b, c) always produces okf = "1.0".
* OkfDocument::new(b, c) propagates bundle.source_id into
  source_id and provenance.source_id.
* OkfDocument::new(b, c) propagates c into provenance.corpus.
* OkfDocument::new(b, c) starts with empty entities, relations,
  tags.
* validate_okf_document reports exactly one unsupported_version
  error per non-"1.0" okf (with offending version in message).
* validate_okf_document reports exactly one source_id_mismatch
  error per provenance/source mismatch.
* Duplicate entity ids each surface a duplicate_entity_id error.
* Dangling relation source / target surface their respective
  errors.
* Every OkfValidationError carries non-empty field / code /
  message.

First property test to exercise session_ledger (the core domain
crate) from sl-viewer's test harness, pivoting the bounded lane
beyond the viewer-only surface.

Updates WBS-6.2 evidence list, TRACEABILITY.json, and CHANGELOG.
…6.2 #460)

Adds crates/sl-viewer/tests/properties_session_ledger_worklog.rs
with 11 proptest properties pinning the session-ledger worklog
projector (crash-recovery / lost-work pipeline):

* Empty sessions project None.
* Final Role::User turn -> AwaitingAssistantResponse.
* Final Role::Tool / Role::Subagent -> InterruptedExecution.
* Final assistant turn with one of the 9 documented completion
  markers (complete / completed / done / [completed] /
  <completed> / status: complete / status: completed /
  task complete / task completed) projects None.
* Final assistant turn without any marker projects as
  MissingCompletionMarker.
* UnfinishedWorkItem carries the originating session id, corpus,
  and message_count.
* summary never exceeds 241 chars and is single-line.
* project_unfinished_work returns one item per unfinished
  session in input order and is deterministic.
* WorklogProjection::from_session carries message_count and
  matches detect_unfinished exactly.

Updates WBS-6.2 evidence list, TRACEABILITY.json, and CHANGELOG.
Copilot AI lite review requested due to automatic review settings August 10, 2026 02:56
@codeant-ai

codeant-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR ee2ad52 Aug 10, 2026 · 02:56 02:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@codeant-ai

codeant-ai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@codeant-ai codeant-ai Bot added the size:XL This PR changes 500-999 lines, ignoring generated files label Aug 10, 2026
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary

Adds property-based tests for viewer async states, Session Ledger OKF validation, and worklog projection.

The tests cover:

  • SkeletonLayout defaults and invariants.
  • OkfDocument defaults, validation, duplicate entities, and dangling relations.
  • Unfinished-session detection and work projection.
  • Summary bounds, ordering, determinism, metadata propagation, completion markers, and projection consistency.

Updates WBS-6.2 evidence, TRACEABILITY.json, and the Unreleased changelog.

No public API changes are introduced.

Must Fix

None identified.

Should Fix

None identified.

Consider

The targeted test suite and formatting check passed. Run the full workspace checks before merge if they are not covered by CI:

cargo clippy --workspace -- -D warnings
cargo test --workspace
cargo fmt --all --check

Approve / Request Changes

Approve.

Walkthrough

This change adds property-based tests for viewer async states, OKF document validation, and session worklog projection. It also updates the changelog and WBS-6.2 traceability evidence.

Changes

Property-Test Coverage

Layer / File(s) Summary
OKF construction and validation properties
crates/sl-viewer/tests/properties_session_ledger_okf.rs
Tests cover document defaults, metadata propagation, valid documents, validation errors, duplicate entities, and dangling relation endpoints.
Worklog projection properties
crates/sl-viewer/tests/properties_session_ledger_worklog.rs
Tests cover unfinished sessions, completion markers, summary constraints, metadata, ordering, determinism, and projection consistency.
Viewer async-state properties
crates/sl-viewer/tests/properties_viewer_async_states.rs
Tests cover SkeletonLayout invariants and list_rows clamping behavior.
Changelog and WBS evidence updates
CHANGELOG.md, docs/ops/TRACEABILITY.json, docs/ops/WBS.md
The new test suites are documented and listed as WBS-6.2 evidence.
Estimated code review effort: 3 (Moderate) ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main change: property-test coverage for the session-ledger worklog projector.
Description check ✅ Passed The description accurately summarizes the new property tests, validation results, and related documentation updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/viewer-mock-data-properties-20260809
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/viewer-mock-data-properties-20260809

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/sl-viewer/tests/properties_session_ledger_okf.rs`:
- Around line 240-260: Update every_error_has_nonempty_components to add a
relation whose source and target IDs are absent from document.entities, ensuring
validation produces both dangling_relation_source and dangling_relation_target
alongside the existing error classes. Keep the fixture’s all-error-shape intent
and existing entity setup unchanged.

In `@crates/sl-viewer/tests/properties_session_ledger_worklog.rs`:
- Around line 11-12: Extend the role-to-reason assertions in the session ledger
worklog tests with a final Role::System case, asserting that it maps to
UnfinishedReason::MissingCompletionMarker. Keep the existing User, Assistant,
Tool, and Subagent cases unchanged.
- Around line 225-231: Update the property test around
WorklogProjection::from_session and detect_unfinished so that when
detect_unfinished returns Some(item), it asserts projection.unfinished equals a
single-item vector containing that exact item, rather than comparing only
lengths. Preserve the existing empty assertion for the None case.
- Around line 99-107: Update the
assistant_without_completion_marker_is_unfinished property so its generated body
cannot equal or contain documented completion markers such as “complete” or
“completed”; use a fixed non-completion prefix with a generated numeric suffix,
while preserving the existing detect_unfinished assertion.
- Around line 140-150: Update the summary_is_single_line property to construct
body from generated prefix and suffix strings with a forced embedded newline
between them, rather than relying on ".{1,200}". Preserve a forced tab in the
generated input as well if tab normalization remains part of the assertions,
while retaining the existing summary checks.

In `@crates/sl-viewer/tests/properties_viewer_async_states.rs`:
- Around line 31-60: Update skeleton_layout_labels_documented and related
coverage to use a production-owned source of SkeletonLayout variants or labels
instead of test-local arrays and select values. Add or reuse a production API or
derive-based metadata that enumerates every variant, then assert each
production-provided variant has the expected stable, non-empty, single-line
label; ensure newly added variants are automatically covered.
- Around line 74-102: Update the tests around list_rows_clamp_in_range,
list_rows_clamp_monotonic, and list_rows_clamp_fixed_points to exercise
ContentSkeleton’s production rendering path rather than usize::clamp directly.
Cover list_rows values below 3, within 3–6, and above 6, asserting the rendered
layout uses the clamped row count; alternatively extract a shared clamp_rows
helper from ContentSkeleton into async_states.rs and test that helper while
ensuring production code calls it.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: b1355c5c-71d7-4701-9c4c-901f73163f12

📥 Commits

Reviewing files that changed from the base of the PR and between 8f34223 and ee2ad52.

📒 Files selected for processing (6)
  • CHANGELOG.md
  • crates/sl-viewer/tests/properties_session_ledger_okf.rs
  • crates/sl-viewer/tests/properties_session_ledger_worklog.rs
  • crates/sl-viewer/tests/properties_viewer_async_states.rs
  • docs/ops/TRACEABILITY.json
  • docs/ops/WBS.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Summary
⚠️ CI failures not shown inline (2)

GitHub Check: Summary: The current Mergify configuration is invalid

Conclusion: failure

View job details

* Invalid condition 'author=dependabot[bot] | renovate[bot]' @ root → pull_request_rules → item 1 → conditions → item 0 → author=dependabot[bot] | renovate[bot]
```
Invalid GitHub login
```
* Invalid condition 'author=trunk-io[bot] | mergify[bot] | github-actions[bot]' @ root → pull_request_rules → item 2 → conditions → item 0 → author=trunk-io[bot] | mergify[bot] | github-actions[bot]
```
Invalid GitHub login
```
* Invalid condition 'age&gt;=30d' @ root → pull_request_rules → item 8 → conditions → item 2 → age>=30d
```
Invalid attribute
```
* Extra inputs are not permitted @ root → pull_request_rules → item 0 → actions → post_merge
* Extra inputs are not permitted @ root → pull_request_rules → item 1 → actions → post_merge
* Extra inputs are not permitted @ root → pull_request_rules → item 3 → actions → request_reviews → github_accounts

GitHub Check: Mergify Merge Queue: The current Mergify configuration is invalid

Conclusion: failure

View job details

* Invalid condition 'author=dependabot[bot] | renovate[bot]' @ root → pull_request_rules → item 1 → conditions → item 0 → author=dependabot[bot] | renovate[bot]
```
Invalid GitHub login
```
* Invalid condition 'author=trunk-io[bot] | mergify[bot] | github-actions[bot]' @ root → pull_request_rules → item 2 → conditions → item 0 → author=trunk-io[bot] | mergify[bot] | github-actions[bot]
```
Invalid GitHub login
```
* Invalid condition 'age&gt;=30d' @ root → pull_request_rules → item 8 → conditions → item 2 → age>=30d
```
Invalid attribute
```
* Extra inputs are not permitted @ root → pull_request_rules → item 0 → actions → post_merge
* Extra inputs are not permitted @ root → pull_request_rules → item 1 → actions → post_merge
* Extra inputs are not permitted @ root → pull_request_rules → item 3 → actions → request_reviews → github_accounts
🧰 Additional context used
📓 Path-based instructions (5)
*

📄 CodeRabbit inference engine (AGENTS.md)

*: Perform feature work in a git worktree under .claude/worktrees/, created from origin/main on a branch named <type>/<topic>, rather than working directly on main.
Do not make direct commits to protected main; use a pull request.
Do not use git reset --hard, git stash, or git clean in worktrees.
Do not use --no-verify or bypass hooks without operator approval.
Do not work on a branch or worktree another actor is using.

Files:

  • CHANGELOG.md
**/*.{rs,toml}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,toml}: Use the Rust toolchain pinned in rust-toolchain.toml; the workspace MSRV is Rust 1.85.
Validate Rust workspace changes with the prescribed locked build, all-features test suite, Clippy, and rustfmt checks where applicable.

Files:

  • crates/sl-viewer/tests/properties_session_ledger_worklog.rs
  • crates/sl-viewer/tests/properties_viewer_async_states.rs
  • crates/sl-viewer/tests/properties_session_ledger_okf.rs
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

Fix Clippy warnings; do not add #[allow] unless it includes a tracking-issue comment.

Files:

  • crates/sl-viewer/tests/properties_session_ledger_worklog.rs
  • crates/sl-viewer/tests/properties_viewer_async_states.rs
  • crates/sl-viewer/tests/properties_session_ledger_okf.rs
crates/sl-viewer/**/*.{rs,toml}

📄 CodeRabbit inference engine (AGENTS.md)

crates/sl-viewer/**/*.{rs,toml}: The sl-viewer crate uses Dioxus 0.6; use the Dioxus CLI/toolchain for desktop development and bundling.
Use cargo check -p sl-viewer as the fast inner-loop check for viewer changes.

Files:

  • crates/sl-viewer/tests/properties_session_ledger_worklog.rs
  • crates/sl-viewer/tests/properties_viewer_async_states.rs
  • crates/sl-viewer/tests/properties_session_ledger_okf.rs
crates/sl-viewer/**/*

📄 CodeRabbit inference engine (AGENTS.md)

When packaging the macOS viewer, account for the documented Electrobun/Dioxus code-signing requirements.

Files:

  • crates/sl-viewer/tests/properties_session_ledger_worklog.rs
  • crates/sl-viewer/tests/properties_viewer_async_states.rs
  • crates/sl-viewer/tests/properties_session_ledger_okf.rs
🪛 LanguageTool
docs/ops/WBS.md

[uncategorized] ~32-~32: The official name of this software platform is spelled with a capital “H”.
Context: ...; fuzz/fuzz_targets/jsonl_ingest.rs; .github/workflows/ci.yml; .github/workflows/b...

(GITHUB)


[uncategorized] ~32-~32: The official name of this software platform is spelled with a capital “H”.
Context: ...ingest.rs; .github/workflows/ci.yml; .github/workflows/bench-gate.yml; docs/ops/pe...

(GITHUB)

🔇 Additional comments (6)
CHANGELOG.md (1)

60-65: LGTM!

docs/ops/TRACEABILITY.json (1)

328-330: LGTM!

docs/ops/WBS.md (1)

32-32: LGTM!

crates/sl-viewer/tests/properties_session_ledger_okf.rs (1)

27-31: 📐 Maintainability & Code Quality

Run the required Rust validation.

The recorded validation includes targeted tests and rustfmt only. Before merge, run the pinned toolchain with a locked cargo check -p sl-viewer, locked all-features tests, and locked Clippy checks.

As per coding guidelines, “Validate Rust workspace changes with the prescribed locked build, all-features test suite, Clippy, and rustfmt checks where applicable.”

Source: Coding guidelines

crates/sl-viewer/tests/properties_viewer_async_states.rs (1)

18-103: 📐 Maintainability & Code Quality

Run the required Rust validation.

The recorded validation includes targeted tests and rustfmt only. Before merge, run the repository-prescribed locked build, workspace all-features tests, Clippy, rustfmt, and cargo check -p sl-viewer with the toolchain pinned by rust-toolchain.toml.

As per coding guidelines, “Validate Rust workspace changes with the prescribed locked build, all-features test suite, Clippy, and rustfmt checks where applicable,” and “Use cargo check -p sl-viewer as the fast inner-loop check for viewer changes.”

Source: Coding guidelines

crates/sl-viewer/tests/properties_session_ledger_worklog.rs (1)

14-18: 📐 Maintainability & Code Quality

Run the required Rust workspace validation commands.

The rust-toolchain.toml-pinned cargo check, all-features suite, Clippy, and cargo fmt --all --check results are not available. Run those checks again until all pass before merging.

Comment on lines +240 to +260
fn every_error_has_nonempty_components(_seed in any::<u32>()) {
let bundle = ContinuationBundle::new("err-shape");
let mut document = OkfDocument::new(&bundle, "forge");
// Force every error class at once.
document.okf = "2.0".into();
document.provenance.source_id = "other".into();
document.entities = vec![
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
];
let errors = validate_okf_document(&document);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add dangling relations to the all-error-shape fixture.

The fixture only produces unsupported_version, source_id_mismatch, and duplicate_entity_id. It does not produce dangling_relation_source or dangling_relation_target, despite Line 243 claiming that it forces every error class.

Add a relation whose source and target are absent from document.entities. This makes the non-empty component check cover all validator error classes.

Proposed fix
         document.entities = vec![
             OkfEntity {
                 id: "x".into(),
@@
                 properties: serde_json::Value::Null,
             },
         ];
+        document.relations = vec![OkfRelation {
+            source: "missing-source".into(),
+            target: "missing-target".into(),
+            r#type: "grounds".into(),
+            provenance: document.provenance.clone(),
+        }];
         let errors = validate_okf_document(&document);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
fn every_error_has_nonempty_components(_seed in any::<u32>()) {
let bundle = ContinuationBundle::new("err-shape");
let mut document = OkfDocument::new(&bundle, "forge");
// Force every error class at once.
document.okf = "2.0".into();
document.provenance.source_id = "other".into();
document.entities = vec![
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
];
let errors = validate_okf_document(&document);
fn every_error_has_nonempty_components(_seed in any::<u32>()) {
let bundle = ContinuationBundle::new("err-shape");
let mut document = OkfDocument::new(&bundle, "forge");
// Force every error class at once.
document.okf = "2.0".into();
document.provenance.source_id = "other".into();
document.entities = vec![
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
OkfEntity {
id: "x".into(),
r#type: "intent".into(),
label: "x".into(),
properties: serde_json::Value::Null,
},
];
document.relations = vec![OkfRelation {
source: "missing-source".into(),
target: "missing-target".into(),
r#type: "grounds".into(),
provenance: document.provenance.clone(),
}];
let errors = validate_okf_document(&document);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_session_ledger_okf.rs` around lines 240 -
260, Update every_error_has_nonempty_components to add a relation whose source
and target IDs are absent from document.entities, ensuring validation produces
both dangling_relation_source and dangling_relation_target alongside the
existing error classes. Keep the fixture’s all-error-shape intent and existing
entity setup unchanged.

Comment on lines +11 to +12
//! Every `Role` ↔ `UnfinishedReason` mapping, the completion-marker
//! whitelist, and the `summarize` budget are pinned here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Add a Role::System reason assertion.

The file states that every Role to UnfinishedReason mapping is pinned. The tests cover User, Assistant, Tool, and Subagent, but not System. The projection test does not cover this gap because it compares WorklogProjection::from_session with detect_unfinished, not with the expected reason.

Add a final Role::System case that asserts UnfinishedReason::MissingCompletionMarker.

Also applies to: 64-78

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_session_ledger_worklog.rs` around lines 11
- 12, Extend the role-to-reason assertions in the session ledger worklog tests
with a final Role::System case, asserting that it maps to
UnfinishedReason::MissingCompletionMarker. Keep the existing User, Assistant,
Tool, and Subagent cases unchanged.

Comment on lines +99 to +107
fn assistant_without_completion_marker_is_unfinished(
body in "[a-zA-Z0-9 .,!?]{1,40}",
) {
let mut session = Session::new("a", Corpus::Forge);
session.messages = vec![
Message::new(Role::User, "do it"),
Message::new(Role::Assistant, body),
];
let item = detect_unfinished(&session).expect("missing marker is unfinished");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restrict this property to non-completion content.

body can be "complete" or "completed". Those are documented completion markers. For those inputs, detect_unfinished returns None, and expect("missing marker is unfinished") fails.

Generate content that cannot contain a completion marker, or use a fixed non-completion prefix and a generated numeric suffix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_session_ledger_worklog.rs` around lines 99
- 107, Update the assistant_without_completion_marker_is_unfinished property so
its generated body cannot equal or contain documented completion markers such as
“complete” or “completed”; use a fixed non-completion prefix with a generated
numeric suffix, while preserving the existing detect_unfinished assertion.

Comment on lines +140 to +150
/// The summary never carries embedded newlines or tab characters
/// (the content was whitespace-normalized).
#[test]
fn summary_is_single_line(
body in ".{1,200}",
) {
let mut session = Session::new("s", Corpus::Forge);
session.messages = vec![Message::new(Role::User, body)];
let item = detect_unfinished(&session).expect("user work is unfinished");
prop_assert!(!item.summary.contains('\n'));
prop_assert!(!item.summary.contains('\t'));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Generate an embedded newline in this property.

The Rust regex .{1,200} does not generate \n. This property therefore does not test its stated newline-normalization condition.

Build body from generated prefix and suffix values with a forced \n between them. Keep a forced \t if the test must also verify tab normalization.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_session_ledger_worklog.rs` around lines 140
- 150, Update the summary_is_single_line property to construct body from
generated prefix and suffix strings with a forced embedded newline between them,
rather than relying on ".{1,200}". Preserve a forced tab in the generated input
as well if tab normalization remains part of the assertions, while retaining the
existing summary checks.

Comment on lines +225 to +231
let projection = WorklogProjection::from_session(&session);
prop_assert_eq!(projection.message_count, n);
let detected = detect_unfinished(&session);
match detected {
Some(_) => prop_assert_eq!(projection.unfinished.len(), 1),
None => prop_assert!(projection.unfinished.is_empty()),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Compare the projected item with detect_unfinished.

This test only compares the number of unfinished items. A regression that preserves the count but changes session_id, summary, reason, metadata, or activity time still passes.

When detect_unfinished returns Some(item), assert that projection.unfinished == vec![item].

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_session_ledger_worklog.rs` around lines 225
- 231, Update the property test around WorklogProjection::from_session and
detect_unfinished so that when detect_unfinished returns Some(item), it asserts
projection.unfinished equals a single-item vector containing that exact item,
rather than comparing only lengths. Preserve the existing empty assertion for
the None case.

Comment on lines +31 to +60
fn skeleton_layout_has_three_variants(_seed in any::<u32>()) {
let variants = [
SkeletonLayout::Bundles,
SkeletonLayout::ListDetail,
SkeletonLayout::StreamFeed,
];
// Round-trip through Debug to confirm each variant's name
// survives stable serialisation.
let mut seen = std::collections::HashSet::new();
for v in variants {
let name = format!("{v:?}");
prop_assert!(name.is_ascii(), "variant {name:?} is not ASCII");
seen.insert(name);
}
prop_assert_eq!(seen.len(), 3, "variant count drifted");
}

/// Every variant's Debug label is non-empty, single-line, and
/// matches one of the documented variant names.
#[test]
fn skeleton_layout_labels_documented(variant in prop::sample::select(vec![
SkeletonLayout::Bundles,
SkeletonLayout::ListDetail,
SkeletonLayout::StreamFeed,
])) {
let label = format!("{variant:?}");
prop_assert!(!label.is_empty());
prop_assert!(!label.contains('\n'));
let valid = label == "Bundles" || label == "ListDetail" || label == "StreamFeed";
prop_assert!(valid, "label {label:?} is not a documented variant name");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Find relevant files"
git ls-files | rg '(^|/)properties_viewer_async_states\.rs$|SkeletonLayout|content.*skeleton|async_states\.rs$' || true

echo
echo "Target test file excerpt"
if [ -f crates/sl-viewer/tests/properties_viewer_async_states.rs ]; then
  nl -ba crates/sl-viewer/tests/properties_viewer_async_states.rs | sed -n '1,120p'
fi

echo
echo "Search SkeletonLayout definitions/usages"
rg -n "enum SkeletonLayout|struct ContentSkeleton|SkeletonLayout::|content_rows|list_rows|clamp\\(" crates/sl-viewer -S || true

echo
echo "Module/imports context"
rg -n "use .*SkeletonLayout|SkeletonLayout|AsyncState|ContentSkeleton" crates/sl-viewer/tests crates/sl-viewer/src -S || true

Repository: KooshaPari/SessionLedger

Length of output: 387


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Target test file excerpt"
if [ -f crates/sl-viewer/tests/properties_viewer_async_states.rs ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' crates/sl-viewer/tests/properties_viewer_async_states.rs | sed -n '1,130p'
fi

echo
echo "search SkeletonLayout definitions and async_states"
grep -RInE "enum SkeletonLayout|struct ContentSkeleton|SkeletonLayout::|content_rows|list_rows|clamp\\(|derive\\(.*Serialize|derive\\(.*Deserialize|Display|Serialize|Deserialize" crates/sl-viewer -S || true

echo
echo "read async_states relevant lines"
awk '{printf "%6d\t%s\n", NR, $0}' crates/sl-viewer/src/async_states.rs | sed -n '1,140p'

Repository: KooshaPari/SessionLedger

Length of output: 11449


Use production-owned variant coverage instead of a test-local list.

The skeleton_layout_labels_documented test still enumerates SkeletonLayout::Bundles, ListDetail, and StreamFeed, so a newly added enum variant can be missed. For stable serialisation or UI labels, define a production API or derive attribute that produces them, and assert coverage from that production source.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_viewer_async_states.rs` around lines 31 -
60, Update skeleton_layout_labels_documented and related coverage to use a
production-owned source of SkeletonLayout variants or labels instead of
test-local arrays and select values. Add or reuse a production API or
derive-based metadata that enumerates every variant, then assert each
production-provided variant has the expected stable, non-empty, single-line
label; ensure newly added variants are automatically covered.

Comment on lines +74 to +102
/// `list_rows.clamp(3, 6)` lands in `[3, 6]` for every input.
#[test]
fn list_rows_clamp_in_range(input in any::<usize>()) {
let clamped = input.clamp(3, 6);
prop_assert!((3..=6).contains(&clamped), "clamp produced {clamped} for input {input}");
}

/// The clamp is monotonic non-decreasing.
#[test]
fn list_rows_clamp_monotonic(
a in any::<usize>(),
b in any::<usize>(),
) {
let (lo, hi) = if a <= b { (a, b) } else { (b, a) };
let c_lo = lo.clamp(3, 6);
let c_hi = hi.clamp(3, 6);
prop_assert!(c_lo <= c_hi, "clamp not monotonic: {lo}→{c_lo}, {hi}→{c_hi}");
}

/// The clamp has the documented fixed points: `0` and `2` clamp
/// to `3`; `6` and `u64::MAX` clamp to `6`.
#[test]
fn list_rows_clamp_fixed_points(_seed in any::<u32>()) {
prop_assert_eq!(0_usize.clamp(3, 6), 3);
prop_assert_eq!(2_usize.clamp(3, 6), 3);
prop_assert_eq!(3_usize.clamp(3, 6), 3);
prop_assert_eq!(6_usize.clamp(3, 6), 6);
prop_assert_eq!(usize::MAX.clamp(3, 6), 6);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | rg '(^|/)crates/sl-viewer/(tests/properties_viewer_async_states\.rs|src/async_states\.rs)$' || true

echo "== test file relevant section =="
if [ -f crates/sl-viewer/tests/properties_viewer_async_states.rs ]; then
  nl -ba crates/sl-viewer/tests/properties_viewer_async_states.rs | sed -n '1,140p'
fi

echo "== async_states relevant section =="
if [ -f crates/sl-viewer/src/async_states.rs ]; then
  nl -ba crates/sl-viewer/src/async_states.rs | sed -n '1,140p'
fi

echo "== search ContentSkeleton and list_rows clamp =="
rg -n "ContentSkeleton|list_rows|clamp\(" crates/sl-viewer -S

Repository: KooshaPari/SessionLedger

Length of output: 341


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== test file relevant section =="
if [ -f crates/sl-viewer/tests/properties_viewer_async_states.rs ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' crates/sl-viewer/tests/properties_viewer_async_states.rs | sed -n '1,140p'
fi

echo "== async_states relevant section =="
if [ -f crates/sl-viewer/src/async_states.rs ]; then
  awk '{printf "%6d\t%s\n", NR, $0}' crates/shown_states.rs 2>/dev/null || awk '{printf "%6d\t%s\n", NR, $0}' crates/sl-viewer/src/async_states.rs | sed -n '1,140p'
fi

echo "== search ContentSkeleton and list_rows clamp =="
rg -n "ContentSkeleton|list_rows|clamp\\(" crates/sl-viewer -S || true

echo "== deterministic usize clamp facts =="
python3 - <<'PY'
def rust_usize_clamp(n, lo, hi):
    return max(lo, min(hi, n))
for n in [0, 2, 3, 5, 6, 7, 17, 2**64-1 if False else 2**32-1]:
    print(n, rust_usize_clamp(n, 3, 6))
PY

Repository: KooshaPari/SessionLedger

Length of output: 14708


Test ContentSkeleton clamping through the production path.

The documented crate helper clamp_rows() is not present, and the current tests prove usize::clamp(3, 6) directly. A change inside ContentSkeleton can affect production output without failing these tests.

Cover rendered layouts with list_rows values above and below the [3, 6] bounds, or extract the production clamp helper to crates/sl-viewer/src/async_states.rs and call it from these tests.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/sl-viewer/tests/properties_viewer_async_states.rs` around lines 74 -
102, Update the tests around list_rows_clamp_in_range,
list_rows_clamp_monotonic, and list_rows_clamp_fixed_points to exercise
ContentSkeleton’s production rendering path rather than usize::clamp directly.
Cover list_rows values below 3, within 3–6, and above 6, asserting the rendered
layout uses the clamped row count; alternatively extract a shared clamp_rows
helper from ContentSkeleton into async_states.rs and test that helper while
ensuring production code calls it.

@KooshaPari KooshaPari closed this Aug 10, 2026
@KooshaPari
KooshaPari deleted the fix/viewer-mock-data-properties-20260809 branch August 10, 2026 03:28
KooshaPari pushed a commit that referenced this pull request Aug 12, 2026
)

Shards the unique viewer proptest from #496 onto main without the
obsolete pre-rename daemon source churn.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XL This PR changes 500-999 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants