Commit Graph

67 Commits

Author SHA1 Message Date
James 257a021669 feat: add create_entry MCP tool for direct AI-to-inou writing
Adds a write-capable create_entry tool to the MCP server, enabling
Claude (Opus/Sonnet/Haiku) to save health insights directly into a
dossier instead of losing them in chat.

Tool: create_entry
- Required: dossier (16-char hex), category (e.g. 'supplement',
  'nutrition', 'tracker'), value (short label)
- Optional: type, summary, data (JSON), parent, timestamp (default: now)
- RBAC-enforced via EntryWrite — only writable dossiers accepted
- Category validated against CategoryFromString; unknown categories
  return a helpful error listing valid options
- data field validated as JSON before write

Use cases:
- Claude analyzing supplements: 'Create a supplement entry for Vitamin D3
  5000 IU with notes on dosing rationale'
- Nutrition logs from a meal conversation
- Tracker observations or AI-generated insights with provenance

The MCP server was previously read-only (all tools had readOnlyHint).
create_entry intentionally omits readOnlyHint as it mutates state.
2026-03-23 12:36:08 -04:00
James 13e991aa1c feat: enrich /api/v1/dashboard with family profile display fields
APIDossierEntry now includes initials, color, age, dob, sex, is_self —
computed server-side using the same avatar color palette and age logic
as the web portal dashboard. Self entry now includes name (was empty).
Relation field now uses proper name from RBAC relation int.

Native mobile family switcher UI can display rich profile cards without
any additional API calls.
2026-03-23 12:32:57 -04:00
James f58a4f804e feat: add AI commentary to lab trend charts
After lab trend charts render, a POST to /dossier/{id}/labs/commentary
sends compact series summaries (first→last value, % change, dates,
reference range) to Claude Haiku, which returns one sentence per metric.
Commentary appears below each chart as an italic insight, e.g.:
  'Cholesterol dropped 15% from 210→178 mg/dL since March — now within range.'

Implementation:
- New handleLabCommentary() in dossier_sections.go — auth-gated, POST only,
  gracefully skips if no Anthropic key configured
- Route: POST /dossier/{id}/labs/commentary (registered before /labs in mux)
- renderFilterChart() now wraps each SVG in .lab-chart-wrap with a
  .lab-commentary placeholder showing a pulsing '…' while loading
- fetchLabCommentary() fires async after chart render, patches commentary
  divs by data-abbr attribute when response arrives
- CSS: .lab-chart-wrap, .lab-commentary, .lab-commentary-text,
  .lab-commentary-loading with pulse animation
2026-03-23 12:27:20 -04:00
James 831ab61445 fix: add JPEG 2000 and RLE support via gdcmconv (install libgdcm-tools)
The decompressDICOM path used gdcmconv which wasn't installed on the
server, silently failing all compressed DICOM imports.

Fix:
- Install libgdcm-tools (gdcmconv 3.0.22) on forge
- Expand isCompressedTransferSyntax to explicitly cover:
  - JPEG 2000 Lossless (1.2.840.10008.1.2.4.90)
  - JPEG 2000 Lossy (1.2.840.10008.1.2.4.91)
  - JPEG 2000 Multi-component (1.2.840.10008.1.2.4.92/.93)
  - RLE Lossless (1.2.840.10008.1.2.5)
  - Deflated Explicit VR (1.2.840.10008.1.2.1.99)
  - JPEG-LS already covered by 1.2.840.10008.1.2.4 prefix
- gdcmconv -w handles all of these natively

gdcmconv is now available at /usr/bin/gdcmconv (v3.0.22).
JPEG 2000 lossless DICOMs from any scanner will now import correctly.
2026-03-23 12:20:36 -04:00
James f2e352ebcf feat: add Compare button for side-by-side study comparison
Adds compareStudies() to viewer.js:
- Switches to 2-panel layout
- Loads most recent study (studies[0]) in left panel
- Loads prior study (studies[1]) in right panel
- Auto-matches same orientation series (AX > SAG > COR)
- Enables sync scroll automatically
- Jumps both panels to middle slice

Adds 'Compare' button to viewer toolbar (viewer/main.go) next to
1/2/3 Panel and 3D buttons.

Sync scroll already worked across studies via slice_location matching —
this just makes the workflow one click.
2026-03-23 12:14:02 -04:00
James ade93669d3 chore: commit accumulated WIP (Mar 23)
Modified:
- api/api_contact_sheet.go, api/api_image.go — image/contact sheet cleanup
- cmd/import-lab/main.go — minor fix
- docs/schema-auth.sql — auth schema updates
- lib/normalize.go, lib/stubs.go — normalization refactor
- portal/defense.go — new defense middleware
- portal/dossier_sections.go — section updates
- portal/genome.go — genome fix
- portal/main.go — main portal updates
- portal/mcp_http.go, portal/mcp_tools.go — MCP refactor
- portal/static/viewer.js — viewer fix
- marketing/twitter/header-FINAL.png — updated asset

New files:
- portal/oauth_chatgpt.go — ChatGPT OAuth integration
- lib/loinc.go — LOINC code lookup
- lib/render.go — rendering engine
- lib/Sora-Regular.ttf, lib/Sora-SemiBold.ttf — fonts
- docs/chatgpt-actions-setup.md, docs/openapi.yaml — docs
- portal/static/claditor-logo.css — styling
- web/static/genetics.html — genetics page
- tools/loinc-lookup/ — LOINC lookup tool
- marketing/screens/ — screenshots
- import-renpho/import-renpho — renpho binary
2026-03-23 12:11:13 -04:00
James bf57e28e71 fix: replace naive byte-scan findTag with DICOM stream walker
The old findTag scanned raw bytes for the 4-byte tag pattern, which
caused false matches inside Siemens CSA private OB blobs (e.g. the
large 0029,1020 Series Header). This corrupted body_part and other
fields on Siemens MAGNETOM Sola MRIs because findTag(0x0018, 0x0015)
hit a matching byte sequence inside the binary payload before reaching
the real BodyPartExamined element.

Fix: walkToTag() walks the DICOM element stream sequentially, reading
VR and length fields to skip element values entirely. Falls back to
byte-scan only on corrupt/truncated length fields. findLastTag updated
to use the same walker.
2026-03-23 12:09:31 -04:00
James 989969375d TASK-019: Fix XSS vulnerability in DICOM series display
- Add html.EscapeString() to series_desc when building Series struct
- Prevents JavaScript injection via malicious DICOM metadata

Security impact: XSS payloads in series descriptions now render as harmless text.
2026-03-23 00:36:06 -04:00
James 5ebf9925ed TASK-018: Fix session management vulnerabilities
- Store session tokens server-side on login (was: generated but not stored)
- Add /api/v1/auth/logout endpoint for server-side session invalidation
- Delete old sessions on login to prevent session fixation attacks
- Add Cache-Control: no-store headers to all auth responses

Security fixes:
1. Session identifiers now rotated on login (old sessions deleted)
2. Logout properly invalidates server-side session
3. Auth responses include anti-caching headers
2026-03-23 00:35:36 -04:00
James 20795e1ea8 feat: add daemon mode to import-renpho, delete Python health-poller
Add --daemon and --interval flags to import-renpho for continuous polling.
Daemon handles SIGTERM/SIGINT gracefully, logs errors per account without
exiting. Systemd user service at ~/.config/systemd/user/renpho-poller.service.

Remove health-poller/ (Python stub that never wrote data).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 07:28:53 -04:00
James c0e7362970 chore: auto-commit uncommitted changes 2026-03-18 20:01:05 -04:00
James 20c33d2fb2 Document processing pipeline: OpenRouter OCR + Stepfun extraction
- Switch OCR from Fireworks Qwen to Gemini 3 Flash via OpenRouter
- Switch extraction/translation to Stepfun Step 3.5 Flash via OpenRouter
- Add CallOpenRouter() to lib/llm.go (OpenAI-compatible API)
- Add OpenRouter API key support to lib/config.go
- Fix extraction preamble causing Stepfun reasoning model to return null content
  (heavy user-message preamble → short system message)
- Fix timestamp fallback: no date = 0, not today
- Fix document type: use file extension instead of hardcoded "pdf"
- Fix findUploadByFilename: Value filter was silently ignored, returning all uploads
- Fix entryQuery: skip category filter when EntryID is specified
- Fix extraction prompt contamination: replace real patient examples with generic ones
- Add reprocess button (↻) for document uploads
- Add delete cascade for upload entries (removes created document/extraction entries)
- Add upload/reprocess/delete logging throughout pipeline
- Consolidated templates, translations, email templates, landing pages
- Portal: defense, genome, dossier sections, MCP, OAuth updates
- Tools: translate pipeline improvements

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 05:28:05 -04:00
James a2141bb5d3 Checkpoint: all pending changes across lib, portal, api, tools
Extraction prompts refined, dossier sections expanded, MCP tools
enhanced, genome/oauth/upload improvements, health-poller added,
import-genome removed, landing/pricing/dashboard template updates,
carousel images, consent/docs templates, rquery/dbquery tools,
CLAUDE.md and docs updates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 23:37:44 -04:00
James 7cbca827b3 Translation pipeline: translate tool, YAML-driven language infrastructure, auto-dispatch
- Add tools/translate: MiniMax/GPT-oss draft → GLM 5 review pipeline via OpenRouter
  Supports 10 templates + YAML, 21 languages, provider routing (Fireworks/Groq)
- portal/main.go: render() auto-picks localized templates (page_xx.tmpl)
  Removes per-handler Lookup boilerplate from landing/pricing/faq handlers
- base.tmpl: dynamic language dropdown from {{.Languages}}, current lang first
- en.yaml: lang_* keys define all 22 languages (YAML-driven, no hardcoded lists)
- All existing YAMLs: added language_name key
- New: tr.yaml (Turkish), translated templates for de/es/nl/da/ja
- Makefile: translate build target

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-11 23:34:09 -04:00
James 155d24ec2e Add comprehensive test suite: labs, entries, dossiers, readings
Test coverage:
- lib/lib_test.go (36 test functions):
  - Crypto: Pack/Unpack, CryptoEncrypt/Decrypt, deterministic encryption
  - Tokens: Create, Parse, expiration handling, invalid token handling
  - Lab references: LabScale conversions, MakeRefID, LabRefLookup with sex/age matching
  - Categories: FromString, CategoryName, CategoryKey, Categories() list
  - IDs: NewID uniqueness, FormatID/ParseID roundtrip
  - Entry CRUD: Write, Read, Delete, filter by type, filter by date range
  - Trackers: Add, Query, Respond, Dismiss
  - RBAC: CheckAccess for own dossier, system user, granted access, no access
  - Dossier helpers: DossierFromEntry, SexKey
  - Normalize: normalizeKey test name cleaning
  - JSON marshaling: Tracker data, DossierPreferences

- api/api_test.go (23 test functions):
  - Helper functions: intPtr, getString, seriesPriority
  - v2 helpers: deterministicID, metricLabel
  - Version endpoint
  - Error/JSON responses
  - Request/Response JSON marshaling: Entry, Dossier, Tracker, Parse, Readings
  - Method handling: token endpoint
  - Categories endpoint
  - isLocalhost detection
  - Schedule calculation

All tests use in-memory SQLite for isolation.
No external dependencies required.
2026-02-28 07:20:44 -05:00
James 93643d285b Code review 2026-02-28: fix critical/high findings + full review report
Critical fixes:
- CR-001: Remove auth backdoor code 250365 (lib/dbcore.go)

High fixes:
- HI-001: Fix CORS wildcard to use origin allowlist (portal/api_mobile.go, portal/mcp_http.go)
- HI-002: Fix LOINC skip logic - skip only if BOTH SearchKey2 AND LOINC are set (lib/normalize.go)

Also added:
- Full code review report at docs/CODE-REVIEW-2026-02-28.md

14 issues found: 2 critical, 4 high, 5 medium, 3 low
3 fixes applied, remaining are documented for follow-up
2026-02-28 07:20:38 -05:00
James ee40b3a81b import-renpho, v2 readings API, dashboard lab chips, portal updates
- Add import-renpho: Go binary to sync Renpho body composition data
  into inou vitals via direct EntryWrite (AES-128/ECB Renpho API client,
  13 body metrics, dedup, auto RBAC grants, -setup/-discover modes)
- Add POST /api/v2/readings endpoint for batch vital ingest
- Fix dashboard lab chip: filter by lab_order not lab_report
- Portal: upload handler, dossier page rework, dashboard updates
- Remove tools/fix-lang (replaced by toolkit translate)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 04:54:58 -05:00
James 033d558535 Portal: upload handler, dossier rework, dashboard updates, normalize fixes
- portal/upload.go: new file upload handler (360 lines)
- portal/templates/dossier.tmpl: major rework (469 changes)
- portal/templates/upload.tmpl, dashboard.tmpl: UI updates
- lib/normalize.go, llm.go, config.go: library fixes
- portal/dossier_sections.go, main.go: portal logic
- portal/lang/en.yaml: string updates
- cmd/populate-search-key: search key population tool
- Makefile, style.css: build and style updates
2026-02-25 20:01:11 -05:00
James cc1dd7690c Lab reference charts, import tracking, DossierFromEntry consolidation
- Fix lab chart reference bands: parse DOB in DossierFromEntry, generate
  deterministic ref_ids in import-caliper (was collapsing 4363 rows to 1)
- Consolidate DossierFromEntry into lib/dbcore.go (eliminate portal duplicate)
- Add Import field to entries for batch undo (NextImportID, all import paths)
- MyChart direct JSON parsing (skip Gemini for structured lab data)
- Multi-order extraction from markdown/text tables
- Normalize progress callback for UI feedback
- DICOM import, genome import, API, portal, MCP, translation updates
- Remove test DICOM data from repo

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 05:15:03 -05:00
James 715fdb9ba6 Fix misleading auth docs and Grok token instruction
api-docs.txt: token is NOT the dossier ID — it's a time-limited encrypted
string. Point users to /connect for token generation.

install_public.tmpl: Grok prompt now points to /connect (Grok tab) for
the token, not /dashboard which doesn't show it.
2026-02-23 02:21:57 -05:00
James d25725b208 Replace legacy bridge flow in install_public.tmpl with OAuth MCP setup
- Remove inou_bridge downloads, Desktop Commander step, manual config editing
- Claude tab now: OAuth connector setup, Sign In, Allow Tools, Custom Instructions, Test
- Fix Grok tab: /api/dossiers → /api/v1/dossiers
- No bridge references remain
2026-02-23 02:01:15 -05:00
James 432c6f80ea Replace legacy bridge download instructions with web MCP setup in NL and RU templates
- Remove inou_bridge_win/darwin download links (fully web-based MCP now)
- Mirror English connect.tmpl structure exactly
- Proper Dutch and Russian translations throughout
- Claude Desktop, Grok, ChatGPT, and Other AI tabs all translated
- Functional prompts (custom instructions, Grok API prompt) kept in English
2026-02-23 01:59:07 -05:00
James d5133fd56f WIP: DICOM import improvements and database query optimizations 2026-02-14 18:00:30 -05:00
James 75e9ec7722 Refactor: Remove legacy v2, data, roles modules; update auth, queries, translations, portal MCP tools 2026-02-13 15:30:22 -05:00
James 6ba57df6ae refactor: clean up reference data and remove rate limiting
Reference data simplification (choke point pattern):
- Remove RefSave/RefDelete from lib (import-time only, not runtime)
- Remove LabTestSave*, LabRefSave* from lib/lab_reference.go
- Remove PopulateReferences (LLM-based ref generation)
- Keep only RefQuery() for runtime reads
- Import tools handle their own SQL inserts

Rate limiting removed:
- Delete new_signups table and all rate limit code
- Solved via different approach (not in codebase)

Database consolidation (on staging):
- Moved genotypes table (30K SNPs) to reference.db
- Deleted empty DBs: portal.db, rate_limit.db, snpedia.db, ratelimit.db

Net -293 lines. Runtime code now only reads reference data via RefQuery().

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 01:20:18 -05:00
James 6486a52ad9 refactor: complete RBAC redesign with hierarchical permissions
Simplify access control from 500+ lines to ~50 lines of core logic:
- New permission bitmask (PermRead/Write/Delete/Manage)
- Hierarchical access (dossier → category → entry)
- Single choke points: CheckAccess(), EntryQuery(), DossierQuery()
- All data access now enforced through lib RBAC layer
- Removed complex role templates and permission caching

Also improved NewID() to use UUID v4 + SHA-256 hash for better
randomness distribution (was limited to 0-7 hex start).

Net -210 lines across 28 files. Ready for staging deployment.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-11 00:06:02 -05:00
James 6546167d67 fix: route all MCP data queries through lib RBAC, kill API roundtrip
list_dossiers, list_studies, list_series, list_slices, query_entries,
get_categories, query_genome — all now call lib directly with
AccessContext{AccessorID: dossierID}. No more HTTP roundtrip to the
internal API with its separate auth path.

Image and journal tools still use API (image rendering logic lives
there, and the API already enforces RBAC via lib.CheckAccess).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 03:17:59 -05:00
James e1b40ab872 refactor: unexport raw DB functions, enforce RBAC at data layer
Rename Query→dbQuery, Save→dbSave, Load→dbLoad, Delete→dbDelete,
Count→dbCount in lib/db_queries.go. Go compiler now prevents any code
outside lib/ from bypassing RBAC checks.

All external callers migrated to RBAC-checked functions:
- EntryCategoryCounts, EntryCount, EntryListByDossier (new)
- LabTestList, LabEntryListForIndex, LabRefLookupAll (new)
- GenomeQuery now requires AccessContext
- EntryDeleteByCategory/EntryDeleteTree now require AccessContext

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 02:34:51 -05:00
James 77db02a6eb feat: optimize genome queries with IN clauses, dedup, repute filter
- Replace N separate SQL queries with single IN clause for rsids and genes
- Dedup results by rsid, merging categories from multiple tiers
- Add repute filter (Good/Bad/Clear) to genome queries
- Expose limit/offset as MCP parameters
- Add genotype to search check
- Fix category filter in genomeEntriesToResult
- Remove deprecated api/api_categories.go and api/api_genome.go
- Change GenomeMatch to use Categories []string instead of Category+Subcategory

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 02:13:18 -05:00
James d2d77d1503 refactor: move genome query to lib, add sensitive variant redaction
- Move GenomeQuery logic from api/api_genome.go to lib/v2.go so MCP
  handler calls lib directly instead of HTTP round-trip (fixes 403 on
  genome queries via Claude.ai MCP - was hitting RBAC table mismatch)
- Generate CategoryFromString from categoryNames in init() (single
  source of truth, removes 9 unused aliases)
- Redact sensitive variants (Bad repute, magnitude >4) in targeted
  queries: genotype/summary replaced with "hidden" + hint to use
  include_hidden=true. Broad queries still suppress entirely.
- API handler is now a thin wrapper parsing query params

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 17:23:25 -05:00
James e8d0656fc6 fix: return category names instead of category keys in v1 API
Changed v1Entries and v1Entry to return category names ('genome', 'upload')
instead of keys ('category004', 'category005'). This makes the API consistent
and prevents MCP from passing back the wrong format.

Removed category004 format parsing since API no longer returns it.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 14:50:20 -05:00
James 21bd173d70 feat: add error codes to genome API responses
Replace generic 'no genome data' message with specific error codes:
- GENOME_NO_EXTRACTION: extraction entry not found
- GENOME_VARIANT_QUERY_FAILED: variant query failed

Makes debugging MCP issues much faster by pinpointing exact failure point.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 14:49:31 -05:00
James ba668ed5fd fix: restore system context for genome queries and support category format in v1 API
Two bugs fixed:
1. genome query RBAC: Someone removed sysCtx and changed back to user ctx after Saturday's fix, causing RBAC to block genome data access. Restored system context usage with dossier access check first.
2. query_entries category filter: API expected category names like 'genome' but MCP returns 'category004' format. Now supports both formats.

Fixes:
- api/api_genome.go: Restore system context for GenomeGetExtraction, GenomeGetTiers, GenomeGetVariants
- api/api_v1.go: Parse both 'category004' and 'genome' formats in v1Entries

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 14:48:56 -05:00
James c88c50bc12 Revert 'fix: add legacy genome data format support to API' - investigating actual issue 2026-02-09 14:04:50 -05:00
James 0ac8f0f42d fix: add legacy genome data format support to API
Root cause: Sophia's genome data was stored in legacy flat format where
all variants were top-level category 4 entries with rsID as the type
field. The API expected new hierarchical format (extraction → tier →
variant), causing "no genome data" errors.

Changes:
- GenomeGetExtraction(): Detect legacy data, return synthetic extraction
- GenomeGetTiers(): Return synthetic "all" tier for legacy format
- GenomeGetVariants(): Query top-level entries for legacy format

Test results:
- Sophia's 4960 genome variants now accessible via /api/genome
- Gene queries work: ?gene=MTHFR,COMT,MTR,MTRR returns 5 matches
- MCP tools (query_genome) now function correctly

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-09 12:21:08 -05:00
James 7e9395ed6c fix: update API routes from /api/prompts to /api/trackers 2026-02-09 11:26:09 -05:00
James 0e5c60dab6 fix: update Open button URL from /prompts to /trackers 2026-02-09 10:01:11 -05:00
James 17bfdf8f83 fix: update all /prompts routes to /trackers 2026-02-09 05:22:05 -05:00
James 131a41037b fix: update /prompts/respond route to /trackers/respond 2026-02-09 05:21:35 -05:00
James 49d7f31514 fix: rename ShowBuildPrompt to ShowBuildTracker 2026-02-09 02:15:05 -05:00
James c389fac78a fix: update Makefile and paths for tracker_prompts directory 2026-02-09 02:06:39 -05:00
James 96fec23e22 refactor: rename prompt to tracker everywhere
- Rename prompts table to trackers
- Rename all Prompt types/functions to Tracker
- Rename prompt_id to tracker_id throughout
- Rename API endpoints /api/prompts -> /api/trackers
- Rename URL paths /dossier/{id}/prompts -> /dossier/{id}/trackers
- Rename template files and references
- Add migration script for schema changes
- Next: implement self-contained entries with metadata
2026-02-09 02:05:17 -05:00
James 9781b31c7d feat: fix year interpretation, new schedule format, exclude today from backfill 2026-02-09 02:00:18 -05:00
James 3014f21d72 refactor: prompts UI and LLM API cleanup 2026-02-08 08:30:27 -05:00
James 37b7602027 chore: update DPO contact information across legal pages
- Replace specific DPO name with generic privacy email across all legal templates
- Update DPA to clarify third-party services vs sub-processors distinction
- Add privacy policy and DPA cross-references in Terms
- Add intellectual property section to Terms
- Improve prompts UI with Yes/No buttons, section headers, and better visual hierarchy

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 05:13:31 -05:00
James 35e9e2a84b feat: add Terms of Service page and legal page updates
- Add /legal/terms with comprehensive ToS content
- Add terms link to footer navigation
- Add /legal/terms to defense.go whitelist for external access
- Update privacy policy and DPA templates with improved styling
- Refactor RBAC editor template formatting
- Add prompts AI setup documentation
- Include database migration scripts

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 04:59:59 -05:00
James 7192f39bc1 fix: remove backward compat, migrate old access to proper RBAC grants
Removed the backward compatibility fallback that checked the old
dossier_access table from CanManageDossier/CanAccessDossier - it was
a security risk (hidden path that bypassed the new RBAC system).

Instead, added MigrateOldAccess() that converts existing dossier_access
entries to proper access grants on startup (idempotent - skips existing).

Migration rules:
- Self-references (accessor == target) skipped (owner access is automatic)
- can_edit = 1 → "rwdm" root grant
- can_edit = 0 → "r" root grant
- Role set to "Migrated" for tracking

Result: 12 grants migrated from old table.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 23:27:23 -05:00
James 08139ada28 chore: compact caliper JSON files (192K lines → 2 lines) 2026-02-07 22:39:03 -05:00
James c3b5381c4c fix: add backward compatibility for old dossier_access table
RBAC editor was failing with 403 Forbidden when trying to edit permissions
for users who have access via the old dossier_access table but not the new
access grants table.

Added fallback logic to CanManageDossier and CanAccessDossier:
1. Check new RBAC system (access table) first
2. If no grant found, check old dossier_access table
3. For manage: check can_edit = 1
4. For access: check status = 1

This allows existing access relationships to work with the new RBAC editor
while we migrate data from old to new system.

Fixes: "Forbidden" error when editing permissions for legacy access grants

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 18:24:27 -05:00
James 7cd450cb49 feat: link RBAC editor from dossier privacy section
Updated "Edit" button in dossier Privacy section to open new RBAC editor
instead of old access editor. Users can now access granular per-category
permissions directly from the dossier page.

Location: Privacy section → Edit button next to each person with access

Route changed: /dossier/{id}/access/{grantee_id} → /dossier/{id}/rbac/{grantee_id}

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-07 17:39:08 -05:00