Skip to content

Latest commit

 

History

History
261 lines (213 loc) · 12.2 KB

File metadata and controls

261 lines (213 loc) · 12.2 KB

Construction Document Relationships

M16.5 adds explicit, project-scoped links between FieldFlow documents, drawings, and construction records. Relationships are created by authorized users; FieldFlow does not infer, extract, classify, or automatically create links.

Architecture

One EntityRelationship model stores links across an explicit resolver registry. This keeps the API and frontend workflow reusable without creating another document system or a relationship table for every module.

The model stores:

  • id, project_id, and created_by
  • source_type and positive source_id
  • target_type and positive target_id
  • relationship_type
  • created_at, updated_at, and nullable deleted_at

The project and creator have native foreign keys. Polymorphic source and target IDs cannot have correct foreign keys to ten different tables, so integrity is enforced through an allowlisted resolver registry, project-owned lookups, availability checks, a controlled relationship matrix, and service transactions. This is a deliberate tradeoff; arbitrary model names, table lookups, and SQL identifiers are never accepted from clients.

Migration d9a2f5c8e173 follows c8f1a4d7e290 and creates only entity_relationships. It adds checks for allowlisted types, positive IDs, and distinct endpoints; project/source, project/target, relationship-type, and created-order indexes; and a partial unique index for each active canonical pair. Project deletion cascades relationship rows. Relationship deletion itself is a soft delete.

Supported Entities

The resolver registry supports exactly:

  • document
  • drawing_set
  • drawing_sheet
  • drawing_revision
  • drawing_issue
  • rfi
  • submittal
  • punch_item
  • change_order
  • daily_log

Each resolver owns its model, safe identifier and title, status, route hint, search fields, deterministic ordering, availability, and new-link eligibility. Response summaries contain only those safe fields. They never contain storage keys, provider metadata, raw backend URLs, tokens, or model names.

Active Documents, active sheets, non-archived sets, and non-void issues may be selected. Superseded drawing revisions remain selectable and navigable. Archived drawing records remain factual in existing relationship lists but cannot receive new links. A soft-deleted Document or hard-missing workflow record is returned as Related record unavailable, without revealing whether an inaccessible record exists in another project.

Relationship Types

The controlled vocabulary and perspective labels are:

Type Forward label Reverse label Direction
references References Referenced by Directional
responds_to Responds to Has response Directional
supersedes Supersedes Superseded by Directional
supports Supports Supported by Directional
impacts Impacts Impacted by Directional
originated_from Originated from Origin of Directional
resolves Resolves Resolved by Directional
documents Documents Documented by Directional
includes Includes Included in Directional
associated_with Associated with Associated with Symmetric
located_on Located on Contains Directional
generated_by Generated by Generated Directional

supersedes and generated_by are reserved vocabulary but have no generic M16.5 matrix entries. Drawing revision lineage remains authoritative, and Inspection support was intentionally deferred.

Allowed Matrix

Only the following source, relationship, and target combinations can be created:

Source Relationship Targets
RFI References Drawing Sheet, Drawing Revision
RFI Responds to Drawing Revision
RFI Originated from Submittal
RFI Impacts Change Order
Submittal References Drawing Sheet, Drawing Revision
Submittal Responds to RFI
Submittal Impacts Change Order
Punch Item Located on Drawing Sheet, Drawing Revision
Punch Item Impacts Change Order
Punch Item Resolves RFI
Change Order Originated from RFI, Submittal, Punch Item
Change Order References Drawing Revision
Change Order Impacts Drawing Sheet
Change Order Resolves RFI
Daily Log Documents RFI, Submittal, Punch Item, Change Order
Daily Log References Drawing Revision
Document Supports RFI, Submittal, Change Order
Drawing Issue Includes Document
Document Associated with Drawing Set, Drawing Sheet, Drawing Revision, Drawing Issue, RFI, Submittal, Punch Item, Change Order, Daily Log

The UI exposes both perspectives of directional rows, but the database stores one row. For example, an RFI can show that it is Origin of a Change Order stored as Change Order originated_from RFI. associated_with endpoints are sorted deterministically by type and ID before persistence, so selecting the same pair in reverse cannot create another active row.

Generic links do not replace DrawingIssueRevision, drawing revision supersession, Document version lineage, Attachment parent ownership, or Folder hierarchy. Drawing Issue includes Document is supplementary context and does not duplicate formal revision membership.

API

Every route requires authentication and get_owned_project:

  • GET /projects/{project_id}/relationships
  • POST /projects/{project_id}/relationships
  • DELETE /projects/{project_id}/relationships/{relationship_id}
  • GET /projects/{project_id}/relationship-candidates

The list route requires one entity_type and positive entity_id, supports both, outgoing, incoming, or symmetric direction, and optionally filters by relationship or related entity type. It defaults to 50 rows, allows at most 100, uses a nonnegative offset, orders newest first with ID as a stable tiebreaker, and returns total and has_more.

Candidate search accepts one allowlisted entity type, a normalized metadata query of at most 200 characters, and 1-50 results with a default of 20. Optional exclusion type and ID must be supplied together. Search is limited to safe identifiers and titles such as filenames, sheet numbers, revision codes, record numbers, dates, and summaries; it never searches file content or downloads binary data.

Create validates both endpoints within the owned project, the allowed combination, availability, self-link rules, and active duplicates before one commit. The server supplies project, creator, direction, labels, routes, and timestamps. Unknown fields and lifecycle or route metadata are rejected. Database uniqueness provides a final concurrency backstop, with duplicate links returning 409. Failures roll back and do not update either related entity or touch object storage.

Delete resolves only an active relationship in the owned project and marks it deleted. A repeated or foreign delete receives the same safe 404. Deleting a link never deletes a record, document, attachment, or specialized domain association.

Lifecycle

Relationship rows remain stored when a linked record is archived, soft-deleted, or hard-deleted. This preserves historical context and lets an authorized user remove a stale link. Existing archived drawing records and superseded revisions remain navigable where their domain routes permit it; unavailable entities lose their navigation route and cannot be selected for new links. A future permanent-purge lifecycle must explicitly account for polymorphic relationship rows.

Frontend Workflow

useRelationships owns list, pagination, create, delete, retry, cancellation, Strict Mode deduplication, and stale project/entity response rejection. The reusable RelationshipPanel composes semantic relationship lists and the CreateRelationshipDialog. It loads only when one persisted record context is explicitly opened; create forms, table rows, and the dashboard do not preload relationships.

The dialog derives relationship and entity choices from the same explicit matrix, performs debounced bounded candidate search, excludes the current record and active duplicates, rejects stale searches, and submits a server-valid directional payload. It uses labeled controls, a keyboard listbox, live search status, focus trapping and restoration, Escape handling, and pending/error states. Relationship items show label, entity type, identifier, title, status or availability, creation date, a descriptive navigation action, and a named remove action; color is never the only status signal.

Integrations use one active panel per page:

  • Document details opens a page-level panel for the active Document.
  • Drawing Register opens a panel for the selected Drawing Sheet.
  • Drawing Viewer exposes Related Records in metadata for the exact current or historical Drawing Revision; opening or mutating it does not refetch the PDF.
  • RFI, Submittal, Punch Item, Change Order, and Daily Log pages open a panel only for the selected persisted record.

Non-drawing links navigate to their existing project page. Drawing revisions navigate to the exact sheet/revision viewer route; drawing sets, sheets, and issues navigate to the existing Drawing Register. Unavailable summaries have no navigation action.

Security and Performance

Source, target, list, candidate, and delete operations all inherit project ownership. Two-user tests cover every resolver type, cross-project endpoints, guessed IDs, foreign relationships, and candidate isolation. API responses use Cache-Control: no-store for lists and candidates.

Relationship summaries are enriched in batches by entity type. The mixed relationship integration fixture remains within 11 SQL SELECT statements, including authentication and project resolution, rather than issuing a query per row. Lists and searches are bounded; there is no full-project graph, polling, table-row fan-out, dashboard request, attachment binary access, or viewer PDF refetch.

M16.6 project document search remains a separate read-only capability. Extracted terms do not create, suggest, rank, or modify relationships, and the search route does not load the relationship graph. Users continue to create every link explicitly through the controlled candidate workflow.

Responsive CSS allows long labels, identifiers, and titles to wrap, stacks actions at narrow widths, bounds the dialog to the viewport, and keeps the viewer metadata integration usable. Automated semantic, keyboard, stale state, request-count, and page-integration coverage passes. Manual browser checks at 320, 375, 768, 1024, and wide desktop widths and at 200% zoom remain not verified in the current command-only environment.

Verification

The complete suites pass 720 primary tests: 433 frontend tests across 62 files and 287 backend tests, with 317 backend subtests reported separately. ESLint and the production build pass with no dependency changes. M16.5 adds a 17.96 kB raw / 5.45 kB gzip lazy relationship chunk. Relative to M16.4, main JavaScript changes by +1.23 kB raw / +0.27 kB gzip, the viewer by +0.93 kB raw / +0.23 kB gzip, and CSS by +5.02 kB raw / +0.64 kB gzip.

M16.7 reconfirms the same complete test totals and a final 17.97 kB raw / 5.45 kB gzip relationship chunk. The two-user API matrix, batched resolver query test, lazy loading, stale-response, keyboard, and navigation tests pass; live browser and responsive execution remains Not Verified and is listed in DOCUMENT_QA.md.

Limitations and Deferred Work

The relationship system does not include automatic or AI-suggested links, document classification, graph visualization, bulk imports, comments, relationship version history, custom relationship types, or cross-project links. Document content search now exists independently, while production OCR remains unavailable. Inspection and delay-note resolvers were deferred to keep the initial matrix focused. Permanent-purge cleanup, drawing comparison, annotations, markups, and relationship analytics are also deferred; none is implied by the current explicit-link workflow.

Preconstruction Independence

M18.1 Review Sources and immutable run manifests are analysis inputs, not construction-record relationships. They do not create, infer, or mutate EntityRelationship rows. Existing human-created links remain authoritative, and automatic relationship generation remains deferred.