Generated 2026-01-31, references HTML templates and 8 migrations
that are now outdated. Superseded by STATUS.md and SPECIFICATION.md.
API.md and silo-spec.md were already deleted in earlier commits.
Add BOM merge endpoint for syncing assembly-derived BOM entries from
FreeCAD's silo-mod plugin.
Merge rules:
- Added: entries in request but not in server BOM are auto-created
with source='assembly'
- Quantity changed: existing entries with different quantity are
auto-updated
- Unchanged: same part and quantity are skipped
- Unreferenced: assembly-sourced entries in server BOM but not in
request are flagged as warnings (never auto-deleted)
- Manual entries are silently ignored in unreferenced detection
Also emits SSE 'bom.merged' event on successful merge (#46).
Promote BOM source from metadata JSONB to a dedicated VARCHAR(20)
column with CHECK constraint ('manual' or 'assembly').
- Add migration 012_bom_source.sql (column, data migration, cleanup)
- Add Source field to Relationship and BOMEntry structs
- Update all SQL queries (GetBOM, GetWhereUsed, GetExpandedBOM, Create)
- Update API response/request types with source field
- Update CSV/ODS export to read e.Source instead of metadata
- Update CSV import to set source on relationship directly
- Update frontend types and BOMTab to use top-level source field
Closes#43
Adds a new read-only endpoint to resolve a Silo item UUID to its full
ItemResponse. Used by silo-mod to resolve FreeCAD document SiloUUID
properties to part numbers during BOM sync.
- Reuses existing ItemRepository.GetByID() (items.id is the stable UUID)
- Returns 404 for archived items
- Registered in viewer-accessible route group (no editor role required)
Implements #17, #18, #19, #20, #21
- Add CSS custom properties for density-dependent spacing (--d-* vars)
in theme.css with comfortable (default) and compact modes
- Create useDensity hook with localStorage persistence and DOM attribute sync
- Add FOUC prevention in main.tsx (sync density before first paint)
- Create shared PageFooter component merging stats + pagination
- Refactor AppShell to flex layout with density toggle button (COM/CMP)
- Consolidate inline pagination from ItemsPage/AuditPage into PageFooter
- Delete FooterStats.tsx (replaced by PageFooter)
- Replace all hardcoded padding/font/gap values in ItemTable, AuditTable,
ItemsToolbar, and AuditToolbar with var(--d-*) references
Comfortable mode is already tighter than the previous hardcoded values.
Compact mode reduces further for power-user density.
Add server-sent events at GET /api/events for live mutation
notifications. Add server mode (normal/read-only/degraded) exposed
via /health, /ready, and SSE server.state events.
New files:
- broker.go: SSE event hub with client management, non-blocking
fan-out, ring buffer history for Last-Event-ID replay, heartbeat
- servermode.go: mode state machine with periodic MinIO health
check and SIGUSR1 read-only toggle
- sse_handler.go: HTTP handler using http.Flusher and
ResponseController to disable WriteTimeout for long-lived SSE
- broker_test.go, servermode_test.go: 13 unit tests
Modified:
- handlers.go: Server struct gains broker/serverState fields,
Health/Ready include mode and sse_clients, write handlers
emit item.created/updated/deleted and revision.created events
- routes.go: register GET /api/events, add RequireWritable
middleware to all 8 editor-gated route groups
- middleware.go: RequireWritable returns 503 in read-only mode
- csv.go, ods.go: emit bulk item.created events after import
- storage.go: add Ping() method for health checks
- config.go: add ReadOnly field to ServerConfig
- main.go: create broker/state, start background goroutines,
SIGUSR1 handler, graceful shutdown sequence
Closes#38, closes#39
The /api/items/{pn}/projects endpoint returns Project objects
({id, code, name, created_at}), but MainTab typed them as string[].
React error #31 was thrown when trying to render the object as a
child node.
Change itemProjects state from string[] to Project[] and use
proj.code in all rendering and comparison logic.
Closes#33
- Mark Phase 4 (remove Go templates) as complete
- Update architecture: SPA serves at / via NotFound handler
- Update overview to past tense (migration is done)
- Update file/line counts (~40 files, ~7,600 lines)
- Mark backend changes 1-3 and 5 as implemented
- Reorganize remaining work section
Closes#30
- Remove silo-spec.md from doc table (deleted)
- Add CONFIGURATION.md, AUTH.md, AUTH_USER_GUIDE.md, frontend-spec.md to doc table
- Mark Configuration Reference as complete
- Fix audit_handlers.go filename (was handlers_audit.go)
- Update Appendix A file structure to match actual codebase
- Fix future migration numbering (012/013, since 011 is taken)
- Add file attachment endpoints to Appendix B
- Remove completed Documentation Overhaul from medium-term
Closes#28
- Replace all htmx references with React SPA
- Update endpoint count from 74 to 75 (6 occurrences)
- Update migration count from 10 to 11
- Update test file count from 1 to 9
- Fix comparison matrix web access entry
Closes#29
- Update migration count to 11, add 011_item_files.sql
- Update endpoint count from 74 to 75
- Replace htmx reference with React SPA
- Add file attachments as complete feature
- Update test file count from 1 to 9
- Update date to 2026-02-08
Closes#27
Remove maxWidth: 1400 and margin: 0 auto from AppShell main container.
Reduce padding from 2rem to 1rem so content fills available browser width.
Adjust height calc in ItemsPage and AuditPage from 80px to 64px offset
to account for reduced top padding.
Closes#16
Rewrite CreateItemPane from single-column scrolling form to a
two-column CSS Grid layout (1fr 280px):
Left column (scrollable form):
- Identity section: Type select, Description input, CategoryPicker
- Sourcing section: Sourcing Type, Standard Cost, Sourcing Link
- Details section: Long Description textarea, Projects TagInput
- Category Properties: dynamic fields from schema (2-column sub-grid)
- Section headers with uppercase labels and horizontal dividers
Right column (sidebar):
- Metadata: auto-assigned revision ('A'), created by (current user)
- Attachments: FileDropZone with presigned upload integration
- Thumbnail: 4:3 preview box, click to upload image
Submission flow:
1. POST /api/items with form data
2. Associate uploaded attachments via POST /api/items/{pn}/files
3. Set thumbnail via PUT /api/items/{pn}/thumbnail
4. File failures are non-blocking (item already created)
Integrates: CategoryPicker (#13), TagInput (#11), FileDropZone (#14),
useFileUpload presigned upload hook (#12)
Closes#15
New files:
- web/src/hooks/useFileUpload.ts: presigned upload hook that gets
a presigned PUT URL from POST /api/uploads/presign then uploads
via XMLHttpRequest for progress tracking
- web/src/components/items/FileDropZone.tsx: drag-and-drop file
upload zone with file list, type-colored badges (CAD/PDF/IMG),
progress bars, and remove buttons
Features:
- Dashed border drop zone with drag-over visual feedback
- Click to browse or drag files to add
- File type detection by extension with colored badges
- Upload progress bar (2px mauve) during active uploads
- Error state display per file
- Configurable accepted file types via accept prop
Closes#14
Replace the flat <select> dropdown for category selection in
CreateItemPane with a searchable, scrollable CategoryPicker component.
New files:
- web/src/hooks/useCategories.ts: fetches and caches category enum
values from GET /api/schemas/kindred-rd, extracts the category
segment values map
- web/src/components/items/CategoryPicker.tsx: scrollable list with
search input filtering by code and description, selected item
highlighted with mauve, breadcrumb showing current selection
Modified:
- web/src/components/items/CreateItemPane.tsx: replaced <select>
with <CategoryPicker>, uses useCategories hook instead of
fetching full schema inline
Closes#13
Build the full Audit page replacing the minimal stub with:
Phase 1 - Audit Overview:
- useAudit hook with server-side filtering by project, category,
tier (mapped to min_score/max_score), sort, and pagination
- AuditSummaryBar: horizontal stacked bar with tier counts, colored
segments (critical/low/partial/good/complete), clickable to filter
- AuditToolbar: project and category dropdowns, sort selector,
layout toggle
- AuditTable: sortable table with score badge (colored by tier),
part number, description, category, sourcing type, missing count
- SplitPanel integration (reused from items) with persistent layout
Phase 2 - Inline Edit Panel:
- AuditDetailPanel: split-panel detail view with field-by-field
breakdown from GET /api/audit/completeness/{pn}
- Fields grouped into Required, Procurement, Category Properties,
and Computed sections
- Color-coded left border per field: green if filled, red if empty
- Critical fields (weight >= 3) marked with asterisk
- Inline editing with debounced auto-save on blur (500ms)
- Item-level fields saved via PUT /api/items/{pn}
- Property fields saved via PUT with merged properties + new revision
- Score progress bar updates live after each save
- Computed fields (has_bom) shown read-only
All components use inline CSS with Catppuccin Mocha theme variables,
matching the existing frontend patterns.
Closes#5
Add 56 tests covering the core backend packages:
Unit tests (no database required):
- internal/partnum: 7 tests for part number generation logic
(sequence, format templates, enum validation, constants)
- internal/schema: 8 tests for YAML schema loading, property
merging, validation, and default application
Integration tests (require TEST_DATABASE_URL):
- internal/db/items: 10 tests for item CRUD, archive/unarchive,
revisions, and thumbnail operations
- internal/db/relationships: 10 tests for BOM CRUD, cycle detection,
self-reference blocking, where-used, expanded/flat BOM
- internal/db/projects: 5 tests for project CRUD and item association
- internal/api/bom_handlers: 6 HTTP handler tests for BOM endpoints
including flat BOM, cost calculation, add/delete entries
- internal/api/items: 5 HTTP handler tests for item CRUD endpoints
Infrastructure:
- internal/testutil: shared helpers for test DB pool setup,
migration runner, and table truncation
- internal/db/helpers_test.go: DB wrapper for integration tests
- internal/db/db.go: add NewFromPool constructor
- Makefile: add test-integration target with default DSN
Integration tests skip gracefully when TEST_DATABASE_URL is unset.
Dev-mode auth (nil authConfig) used for API handler tests.
Fixes: fmt.Errorf Go vet warning in partnum/generator.go
Closes#2
Add section 8.4 to SPECIFICATION.md describing the flat BOM flattening
and assembly cost roll-up endpoints with example request/response JSON.
- GET /api/items/{pn}/bom/flat — consolidated leaf parts with
rolled-up quantities and cycle detection
- GET /api/items/{pn}/bom/cost — per-line extended costs and total
assembly cost using standard_cost
Update endpoint count from 74 to 76 in SPECIFICATION.md and README.md.
Add checklist entries for flat BOM and assembly costing features.
Phase 1 of frontend migration (epic #6, issue #7).
Project setup (web/):
- React 19, React Router 7, Vite 6, TypeScript 5.7
- Catppuccin Mocha theme CSS variables matching existing Go templates
- Vite dev proxy to Go backend at :8080 for /api/*, /login, /logout,
/auth/*, /health, /ready
Shared infrastructure:
- api/client.ts: typed fetch wrapper (get/post/put/del) with 401
redirect and credentials:include for session cookies
- api/types.ts: TypeScript interfaces for all API response types
(User, Item, Project, Schema, Revision, BOMEntry, Audit, Error)
- context/AuthContext.tsx: AuthProvider calling GET /api/auth/me
- hooks/useAuth.ts: useAuth() hook exposing user/loading/logout
UI shell:
- AppShell.tsx: header nav matching current Go template navbar
(Items, Projects, Schemas, Audit, Settings) with role badges
(admin=mauve, editor=blue, viewer=teal) and active tab highlighting
- LoginPage: redirects to Go-served /login during transition
- Placeholder pages: Items, Projects, Schemas fetch from API and
display data in tables; Audit shows summary stats; Settings shows
current user profile
Go server changes:
- routes.go: serve web/dist/ at /app/* with SPA index.html fallback
(only activates when web/dist/ directory exists)
- .gitignore: web/node_modules/, web/dist/
- Makefile: web-install, web-dev, web-build targets
Closes#3 — README now reflects the full scope of Silo.
- Updated subtitle: 'structured item database and part lifecycle server'
- 10 overview bullets covering all major features
- Complete component tree (internal/, cmd/, migrations/, docs/)
- Docker Compose quick start with config.yaml example
- Authentication section: local, LDAP/FreeIPA, OIDC/Keycloak, API tokens
- Client integrations section linking silo-mod and silo-calc repos
- Documentation table linking all docs/*.md files
Move pkg/calc/ to its own repository (19 files, ~4,600 lines):
- silo_calc_component.py, sync_engine.py, dialogs.py, push/pull
- AI client, completion wizard, settings, sheet format
- Addons.xcu, manifest.xml, description.xml, tests
Replace docs/CALC_EXTENSION.md with redirect to silo-calc repo
and reference to server-side ODS export endpoints.
The Calc extension now lives at:
https://git.kindred-systems.com/kindred/silo-calc
Closes#1 — Bring documentation in line with implemented features.
GAP_ANALYSIS.md:
- Mark auth system and audit log gaps as Implemented
- Replace FreeCAD Integration section with Client Integration (silo-mod)
- Update Phase 2 sections: auth and audit marked COMPLETE
- Update Appendix A file structure and Appendix B endpoints
STATUS.md:
- Update client integrations to reference silo-mod and silo-calc repos
- Update unit tests row to remove pkg/calc/tests reference
ROADMAP.md:
- Update executive summary with links to silo-mod and silo-calc
- Update unit tests row, CAD gap section references
SPECIFICATION.md:
- Update architecture overview to reference silo-mod and silo-calc
- Update Section 5 Client Integration with both repos
REPOSITORY_STATUS.md:
- Remove Python/FreeCAD row from language stats, update totals
Add openDocumentInteractive() and saveDocumentAsInteractive() methods
to support the updated FileOrigin interface from Issue #10/#12.
- openDocumentInteractive: Delegates to Silo_Open command for search dialog
- saveDocumentAsInteractive: Triggers new item creation form for Save As
These methods enable the Std_* commands in FreeCAD to delegate to
SiloOrigin when Silo is the active origin.
Implements Issue #11: Silo origin adapter
This commit creates the SiloOrigin class that implements the FileOrigin
interface introduced in Issue #9, enabling Silo to be used as a document
origin in the unified file origin system.
## SiloOrigin Class (silo_origin.py)
New Python module providing the FileOrigin implementation for Silo PLM:
### Identity Methods
- id(): Returns 'silo' as unique identifier
- name(): Returns 'Kindred Silo' for UI display
- nickname(): Returns 'Silo' for compact UI elements
- icon(): Returns 'silo' icon name
- type(): Returns OriginType.PLM (1)
### Workflow Characteristics
- tracksExternally(): True - Silo tracks documents in database
- requiresAuthentication(): True - Silo requires login
### Capabilities
- supportsRevisions(): True
- supportsBOM(): True
- supportsPartNumbers(): True
- supportsAssemblies(): True
### Connection State
- connectionState(): Checks auth status and API connectivity
- connect(): Triggers Silo_Auth dialog if needed
- disconnect(): Calls _client.logout()
### Document Identity (UUID-based tracking)
- documentIdentity(): Returns SiloItemId (UUID) as primary identity
- documentDisplayId(): Returns SiloPartNumber for human display
- ownsDocument(): True if document has SiloItemId or SiloPartNumber
### Core Operations (delegate to existing commands)
- newDocument(): Delegates to Silo_New command
- openDocument(): Uses find_file_by_part_number or _sync.open_item
- saveDocument(): Saves locally + uploads via _client._upload_file
- saveDocumentAs(): Triggers migration workflow for local docs
### Extended Operations
- commitDocument(): Delegates to Silo_Commit
- pullDocument(): Delegates to Silo_Pull
- pushDocument(): Delegates to Silo_Push
- showInfo(): Delegates to Silo_Info
- showBOM(): Delegates to Silo_BOM
### Module Functions
- get_silo_origin(): Returns singleton instance
- register_silo_origin(): Registers with FreeCADGui.addOrigin()
- unregister_silo_origin(): Cleanup function
## UUID Tracking (silo_commands.py)
Added SiloItemId property to all locations where Silo properties are set:
1. create_document_for_item() - Assembly objects (line 1115)
2. create_document_for_item() - Fallback Part objects (line 1131)
3. create_document_for_item() - Part objects (line 1145)
4. Silo_New.Activated() - Tagged existing objects (line 1471)
The SiloItemId stores the database UUID (Item.ID) which is immutable,
while SiloPartNumber remains the human-readable identifier that could
theoretically change.
Property structure on tracked objects:
- SiloItemId: UUID from database (primary tracking key)
- SiloPartNumber: Human-readable part number
- SiloRevision: Current revision number
- SiloItemType: 'part' or 'assembly'
## Workbench Integration (InitGui.py)
SiloOrigin is automatically registered when the Silo workbench
initializes:
def Initialize(self):
import silo_commands
try:
import silo_origin
silo_origin.register_silo_origin()
except Exception as e:
FreeCAD.Console.PrintWarning(...)
This makes Silo available as a file origin via:
- FreeCADGui.listOrigins() -> includes 'silo'
- FreeCADGui.getOrigin('silo') -> returns origin info dict
- FreeCADGui.setActiveOrigin('silo') -> sets Silo as active
## Design Decisions
1. **Delegation Pattern**: SiloOrigin delegates to existing Silo
commands rather than reimplementing logic, ensuring consistency
and easier maintenance.
2. **UUID as Primary Identity**: documentIdentity() returns UUID
(SiloItemId) for immutable tracking, while documentDisplayId()
returns part number for user display.
3. **Graceful Fallback**: If SiloItemId is not present (legacy docs),
falls back to SiloPartNumber for identity/ownership checks.
4. **Exception Handling**: All operations wrapped in try/except to
prevent origin system failures from breaking FreeCAD.
Refs: #11
- Add SiloEventListener class for SSE-based real-time notifications
- Add SiloPullDialog for revision selection when pulling
- Add conflict detection before pull (unsaved changes, stale revision, mtime)
- Add progress callback to _download_file() for UI feedback
- Integrate SSE listener into SiloAuthDockWidget with status indicator
- Refresh activity panel on remote change events
- Add top-level PySide.QtCore import for QThread/Signal usage
The sequence counter (sequences_by_name table) can get out of sync
with the items table if items were seeded/imported directly or if a
previous create failed after incrementing the sequence but before
the insert committed. This causes Generate() to return a part number
that already exists, hitting the unique constraint on items.part_number.
Add a retry loop (up to 5 attempts) in HandleCreateItem that detects
PostgreSQL unique violation errors (SQLSTATE 23505) via pgconn.PgError
and retries with the next sequence value. Non-duplicate errors still
fail immediately. If all retries are exhausted, returns 409 Conflict
instead of 500.