Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
4356280
test(zotero): require snapshot-bound review worksheet
seonghobae Sep 4, 2026
cfc3321
feat(zotero): build complete steward review worksheet
seonghobae Sep 4, 2026
b272249
docs(zotero): define steward review worksheet
seonghobae Sep 4, 2026
3a9dc1f
test(zotero): cover invalid worksheet reports
seonghobae Sep 4, 2026
f34cf24
fix(zotero): reject contradictory review reports
seonghobae Sep 4, 2026
c401f5e
style(zotero): format worksheet validation
seonghobae Sep 4, 2026
6564e29
test(zotero): select worksheet cases by disposition
seonghobae Sep 4, 2026
cef7374
fix(zotero): import worksheet disposition contract
seonghobae Sep 4, 2026
eeb0797
test(zotero): exercise incomplete worksheet provenance
seonghobae Sep 4, 2026
79228e3
refactor(zotero): remove unreachable worksheet guard
seonghobae Sep 4, 2026
cda2399
Merge current review-evaluation parent into steward worksheet
seonghobae Sep 4, 2026
26badf5
Merge repaired write receipt evidence into zotero-steward-review-work…
seonghobae Sep 4, 2026
00b8335
Merge current receipt repair parent into steward worksheet
seonghobae Sep 4, 2026
7b75d16
Merge remote-tracking branch 'origin/autoresearch/zotero-complete-rev…
seonghobae Sep 4, 2026
b2de999
merge(zotero): adopt current complete-review parent
seonghobae Sep 4, 2026
8a239ee
merge(zotero): adopt current complete-review parent and gap baseline
seonghobae Sep 5, 2026
9b27419
merge(research): inherit verified source and proposal approval binding
seonghobae Sep 5, 2026
b92bc57
test(research): adopt captured-source worksheet fixture
seonghobae Sep 5, 2026
3712bd6
merge(research): inherit canonical local transport repairs into PR #25
seonghobae Sep 5, 2026
45c5993
merge(research): inherit deterministic transport framing regression i…
seonghobae Sep 5, 2026
6af5111
merge(zotero): propagate validated approval ordering through PR 25
seonghobae Sep 5, 2026
c6b4c17
merge(research): inherit bounded metadata reads into PR #25
seonghobae Sep 6, 2026
4a1a3bb
merge(research): retain PR25 worksheet and verified source scope
seonghobae Sep 6, 2026
900038e
test(research): reproduce worksheet source inventory admission gaps
seonghobae Sep 6, 2026
5b54d06
fix(research): reuse complete report validation for blank worksheets
seonghobae Sep 6, 2026
30aa091
test(research): reproduce unbound worksheet content identity
seonghobae Sep 6, 2026
97046c7
fix(research): bind blank worksheet to complete proposal scope
seonghobae Sep 6, 2026
51631fb
docs(research): record worksheet scope evidence and downstream adoption
seonghobae Sep 6, 2026
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ All notable changes to ConceptWeave are documented here.
- Minimal, nonduplicated local abstract context for Zotero items that require steward classification.
- Owner-only file permissions for sensitive local Zotero classification reports.
- A complete-review evaluator that rejects partial steward labels as full reclassification evidence.
- A snapshot-bound steward worksheet with one blank decision per bibliographic item and no duplicated bibliographic text.

### Security

Expand Down
88 changes: 87 additions & 1 deletion crates/conceptweave-zotero/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ pub enum Disposition {
}

/// Deterministic reason that a bibliographic item requires steward review.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum AbstentionReason {
/// Title, abstract, and tags contain no classification metadata.
Expand Down Expand Up @@ -1106,6 +1106,92 @@ pub struct ClassificationAudit {
pub disposition_counts: BTreeMap<Disposition, usize>,
}

/// One editable local steward decision without duplicated bibliographic text.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct StewardReviewDecision {
/// Stable Zotero item key used to join the sensitive classification report.
pub item_key: String,
/// Exact item revision observed in the classified snapshot.
pub item_version: u64,
/// Deterministic proposal supplied for comparison, never as approval.
pub proposed_disposition: Disposition,
/// Deterministic abstention reason when the proposal requires review.
pub abstention_reason: Option<AbstentionReason>,
/// Steward decision to fill; abstention is rejected by completion evaluation.
pub reviewed_disposition: Option<Disposition>,
}

/// Snapshot-bound local worksheet for one decision per bibliographic item.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct StewardReviewWorksheet {
/// Zotero library revision observed with the source snapshot.
pub library_version: u64,
/// Classifier revision that produced the proposals.
pub rule_revision: String,
/// Canonical content digest of the complete raw snapshot.
pub snapshot_digest: String,
/// Versioned identity of proposals, review context and retained source scope.
/// Older worksheets require regeneration; this binding is not approval.
pub proposal_digest: String,
/// Complete parent and child item-revision coordinates.
pub snapshot_items: Vec<SnapshotItemRevision>,
/// Deterministically ordered editable decisions for bibliographic items.
pub decisions: Vec<StewardReviewDecision>,
}

/// A classification report cannot safely produce a review worksheet.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WorksheetError {
/// Report identity or coverage is incomplete, duplicated, or inconsistent.
InvalidReport,
}

impl fmt::Display for WorksheetError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("classification report is invalid for steward review")
}
}

impl std::error::Error for WorksheetError {}

/// Builds a complete local worksheet without copying titles, abstracts, or evidence.
///
/// Complete inventory validation precedes projection. Valid unresolved sources
/// may be reviewed, but worksheet creation never proves completed review.
pub fn build_steward_review_worksheet(
report: &ClassificationReport,
) -> Result<StewardReviewWorksheet, WorksheetError> {
validate_classification_report(report).map_err(|_| WorksheetError::InvalidReport)?;
if report.rule_revision.trim().is_empty() || report.snapshot_digest.trim().is_empty() {
return Err(WorksheetError::InvalidReport);
}
let mut decisions = Vec::with_capacity(report.classified_items.len());
for item in &report.classified_items {
if (item.proposed_disposition == Disposition::NeedsStewardReview)
!= item.abstention_reason.is_some()
{
return Err(WorksheetError::InvalidReport);
}
decisions.push(StewardReviewDecision {
item_key: item.item_key.clone(),
item_version: item.item_version,
proposed_disposition: item.proposed_disposition,
abstention_reason: item.abstention_reason,
reviewed_disposition: None,
});
}
decisions.sort_by(|left, right| left.item_key.cmp(&right.item_key));

Ok(StewardReviewWorksheet {
library_version: report.library_version,
rule_revision: report.rule_revision.into(),
snapshot_digest: report.snapshot_digest.clone(),
proposal_digest: classification_proposal_digest(report),
snapshot_items: report.snapshot_items.clone(),
decisions,
})
}

/// One steward-reviewed expected disposition in a local golden set.
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct GoldenLabel {
Expand Down
Loading