-- Claim-centred case reports. A Claim is a pinned board exhibit; red-thread -- labels describe how connected source documents support it. Report acceptance -- is level-owned history, while authored report configuration clones with boards. INSERT INTO osint.exhibit_types (id,name,is_spatial) VALUES ('claim','Claim',TRUE) ON CONFLICT (id) DO NOTHING; CREATE TABLE osint.claim_exhibits ( exhibit_id UUID PRIMARY KEY REFERENCES osint.exhibits(id) ON DELETE CASCADE, statement TEXT NOT NULL CHECK (char_length(btrim(statement)) BETWEEN 1 AND 2000) ); ALTER TABLE osint.document_exhibits ADD COLUMN citation_text TEXT NOT NULL DEFAULT ''; CREATE TABLE osint.exhibit_citations ( board_id UUID NOT NULL REFERENCES osint.boards(id) ON DELETE CASCADE, exhibit_id UUID NOT NULL REFERENCES osint.exhibits(id) ON DELETE CASCADE, display_number INTEGER NOT NULL CHECK (display_number > 0), PRIMARY KEY (board_id,exhibit_id), UNIQUE (board_id,display_number), FOREIGN KEY (board_id,exhibit_id) REFERENCES osint.exhibits(board_id,id) ON DELETE CASCADE ); INSERT INTO osint.exhibit_citations (board_id,exhibit_id,display_number) SELECT board_id,id,ROW_NUMBER() OVER (PARTITION BY board_id ORDER BY created_at,id)::integer FROM osint.exhibits WHERE exhibit_type_id='document'; CREATE TABLE osint.case_reports ( board_id UUID PRIMARY KEY REFERENCES osint.boards(id) ON DELETE CASCADE, title TEXT NOT NULL DEFAULT 'Case Report' CHECK (char_length(btrim(title)) BETWEEN 1 AND 200), investigator_name TEXT NOT NULL DEFAULT '' CHECK (char_length(investigator_name) <= 300), required_for_completion BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE osint.case_report_submissions ( id UUID PRIMARY KEY, level_id UUID NOT NULL, board_id UUID NOT NULL, status TEXT NOT NULL CHECK (status IN ('evidence_insufficient','evidence_accepted_report_incomplete','accepted')), investigator_name TEXT NOT NULL CHECK (char_length(btrim(investigator_name)) BETWEEN 1 AND 300), feedback TEXT NOT NULL, submitted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), FOREIGN KEY (level_id,board_id) REFERENCES osint.levels(id,board_id) ON DELETE CASCADE ); CREATE INDEX case_report_submissions_level_idx ON osint.case_report_submissions (level_id,submitted_at DESC,id); CREATE TABLE osint.case_report_submission_issues ( submission_id UUID NOT NULL REFERENCES osint.case_report_submissions(id) ON DELETE CASCADE, issue_key TEXT NOT NULL CHECK (issue_key ~ '^[a-z][a-z0-9_.-]{0,63}$'), PRIMARY KEY (submission_id,issue_key) ); COMMENT ON TABLE osint.claim_exhibits IS 'Pinned propositions which can receive one or more supporting red-thread connections.'; COMMENT ON TABLE osint.exhibit_citations IS 'Stable board-local exhibit numbers used in reports independently of spatial or z-order.'; COMMENT ON TABLE osint.case_reports IS 'Clonable report configuration plus the mutable investigator byline for one board.'; COMMENT ON TABLE osint.case_report_submissions IS 'Immutable server evaluations of a mutable level report.'; COMMENT ON COLUMN osint.document_exhibits.citation_text IS 'Player-authored source/publication citation, distinct from the document title and optional source URI.';