{"id":336,"date":"2026-08-13T23:19:52","date_gmt":"2026-08-13T20:19:52","guid":{"rendered":"https:\/\/john-nessime.com\/blog\/?p=336"},"modified":"2026-08-13T23:19:54","modified_gmt":"2026-08-13T20:19:54","slug":"construction-dispute-intelligence-platform","status":"publish","type":"post","link":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/","title":{"rendered":"Designing a Construction Dispute Intelligence Platform That Survives Cross-Examination"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The question that kills these systems is always the same one, and it always comes from the expert, not the engineer: &#8220;Where did this date come from?&#8221;<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You have a screen showing a beautiful chronology. Two hundred events, colour-coded by discipline, cross-referenced against the programme. The delay expert points at row 47, the one that says the piling rig arrived late, and asks which document that came from, who produced it, what page, and whether the other side has seen it. If the answer involves anyone opening a folder and searching, the platform has already failed. Not technically. Evidentially. A chronology nobody can trace back to a produced document is a nice picture, and a nice picture is worth nothing in an adjudication.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is the constraint that shapes everything else. A construction dispute intelligence platform is not a search engine with a construction theme on it. It is a provenance system that happens to do search. Get that ordering wrong and you build something impressive that no expert will sign their name to.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This post walks through the architecture layer by layer: intake and chain of custody, normalization, the schedule data plane, the chronology model that joins the two, retrieval, and the tightly bounded generation layer on top. For each one I&#8217;ll say what it prevents, because that is the only reason any of these layers exist.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What a construction dispute intelligence platform actually has to do<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Strip away the vocabulary and the job is narrow. Take a pile of heterogeneous records from a capital project, some of them structured and some of them scanned faxes, and let a small number of expensive humans answer three questions quickly and defensibly:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>What happened, in what order, and who said so at the time?<\/li>\n\n\n\n<li>What did the programme say before and after each of those things happened?<\/li>\n\n\n\n<li>Which contemporaneous record supports every single assertion in the report?<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The third one is not a nice-to-have. Both of the industry references people actually argue from lean on it. The Society of Construction Law Delay and Disruption Protocol raised record keeping to a core principle in its second edition and stopped expressing a preference between retrospective delay analysis methods, on the reasoning that the records you have largely determine the method available to you. AACE International&#8217;s Recommended Practice 29R-03 codifies forensic schedule analysis into a taxonomy of Method Implementation Protocols, and its minimum implementation protocols are essentially statements about which source schedules you must be able to produce. Both documents are, underneath, about evidence handling. So is your architecture.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer one: intake that a forensic expert can defend<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Everything upstream of this layer is somebody else&#8217;s mess. Site photos on a phone, a shared drive nobody pruned since mobilization, an Aconex export, a hard drive that arrived in an envelope. Your job at intake is to make an irreversible statement about what you received and when, before anyone touches it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hash every file on arrival and store the hash next to the object, not in a spreadsheet. SHA-256 over the raw bytes, computed before any conversion, deduplication or renaming. The hash becomes the document&#8217;s identity for the rest of its life.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Build an arrival manifest before anything else runs\nfind .\/ACME-INTAKE-001 -type f -print0 \n  | xargs -0 sha256sum &gt; manifest-ACME-INTAKE-001.txt\n\n# Re-verify later, after the files have moved through processing\nsha256sum -c manifest-ACME-INTAKE-001.txt<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then put the originals somewhere they cannot be quietly edited. On AWS, S3 Object Lock in compliance mode with a retention period is the blunt instrument: once set, the object version cannot be overwritten or deleted by anyone, including the root account, until retention expires. Governance mode is the softer variant, where a specifically permissioned principal can lift the lock, which is fine for internal working copies and wrong for originals. If you are self-hosting on a Contabo or InterServer box because the client will not put project data in a hyperscaler, the equivalent discipline is a write-once staging volume plus an append-only manifest log, and you accept that you are relying on process rather than on the storage layer refusing the write.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The manifest is the product<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">If the matter is already in formal disclosure, you will be handed productions rather than raw folders, and those arrive as load files. A Concordance DAT carries one row per document with fields like BegBates, EndBates, Custodian, BegAttach, EndAttach and an MD5 hash. An Opticon OPT carries one row per page, mapping Bates numbers to image paths. The delimiters are deliberately weird characters that real text rarely contains, and different tools disagree about the exact code points, so parse defensively and log what you detected rather than assuming.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The failure mode here is quiet and expensive. Variable-width Bates numbers sort lexically instead of numerically, so ABC10 lands between ABC1 and ABC2, attachment ranges resolve to the wrong parents, and a family of documents silently splits. Nobody notices until an expert cites a covering email whose attachment, in your system, belongs to a different letter. Pad to fixed width on ingest and reconcile every BegAttach range against the documents you actually loaded.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer two: normalization, and the OCR tax nobody budgets for<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Construction corpora are the worst kind of mixed. Native Office files with clean text, born-digital PDFs, scanned site instructions with a signature and a coffee ring, photographs of whiteboards, and drawings where the only text that matters is inside a title block in the bottom right corner.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Route by what the file actually is, not by extension. For a PDF, try text extraction first; if the extracted layer is empty or nearly empty, it is a scan and needs OCR. Tesseract via OCRmyPDF is the cheap path and it is genuinely good on clean typed pages. It degrades badly on handwriting, on stamps, and on anything skewed, which describes most site paperwork. A managed service such as Amazon Textract handles tables and forms better and returns geometry for every detected block, which matters more than accuracy alone.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Geometry is the part teams skip and then rebuild six months later. If you store only the extracted string, you can prove a phrase exists somewhere in a document. If you store the page number and the bounding box, you can put a highlight on the exact line in the viewer, and the expert can confirm it in two seconds instead of reading eleven pages. That difference is the difference between a tool people use and a tool people abandon.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Keep the OCR text separate from the original in storage, and record which engine and which settings produced it. When a disputed reading of a handwritten date turns up, and it will, you want to be able to say exactly how that character was produced.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer three: the schedule plane is a separate data model<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This is where most teams underestimate the work. Programme data is not documents. It is a graph, versioned, with a calendar attached, and it needs its own tables, its own ingest path and its own validation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Primavera P6 exports as XER, a tab-delimited text format with a table-per-section layout, or as P6 XML. Either way you are pulling out projects, the WBS tree, activities, logic links and calendars. In XER these live in sections including PROJECT, PROJWBS, TASK, TASKPRED and CALENDAR. Parsing it by hand is a bad use of a week; PyP6Xer is an open source Python parser that will get you to objects quickly.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from xerparser.reader import Reader\n\nxer = Reader(\"update-14.xer\")\n\nfor project in xer.projects:\n    print(project.proj_short_name)\n    for activity in project.activities:\n        print(activity.task_code, activity.task_name)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Two things bite immediately. First, several duration and float fields in XER are stored in hours rather than days, which you can see in the column names themselves. If you load them straight into a &#8220;days&#8221; column because the numbers looked plausible on a five-day calendar, every float figure downstream is wrong by a factor that varies with the calendar. Check your own export before you trust any mapping, including this one.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Second, activity IDs are not stable across a project&#8217;s life. Activities get renumbered, split, deleted and re-added between updates. If your schema keys on the activity ID alone, two updates will silently merge activities that were never the same work. Key on a composite of schedule version plus activity ID, and maintain a separate mapping table for identity across versions that a human can correct. That table is boring, it is not automatable, and it is the difference between a windows analysis that holds together and one that quietly compares apples to a different orchard.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Store every update as an immutable snapshot with its data date. Never mutate a loaded schedule. The whole point of the layer is that you can reconstruct exactly what the programme said on a given day, which is precisely what a time slice or windows method needs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer four: the chronology is the join, and the join is the hard part<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Documents on one side, schedule snapshots on the other. The product is the thing in the middle: an event, with a date, a description, a set of supporting document references, and a set of affected activities.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Model it explicitly. An event is not a document and it is not an activity. One letter can create three events. One event can be evidenced by nine documents and a photograph. The relationship is many-to-many in both directions and every attempt I have seen to flatten it ends with someone maintaining a spreadsheet on the side.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>-- Every assertion resolves to a page and a box on that page\nCREATE TABLE evidence_ref (\n  id            BIGSERIAL PRIMARY KEY,\n  event_id      BIGINT NOT NULL REFERENCES event(id),\n  document_id   UUID   NOT NULL REFERENCES document(id),\n  page_number   INT    NOT NULL,\n  bbox          JSONB,             -- normalized coordinates from OCR\n  quoted_text   TEXT   NOT NULL,\n  asserted_by   TEXT   NOT NULL,   -- 'analyst' or 'model'\n  confirmed_by  TEXT,              -- NULL until a human confirms\n  confirmed_at  TIMESTAMPTZ\n);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Those last three columns are the ones that matter. They encode the difference between something the machine proposed and something a person stands behind. Report generation reads only rows where <code>confirmed_by<\/code> is set. Everything else is a suggestion queue. This is not a compliance box; it is the mechanism by which an expert can put their name on the output, and it needs to exist in the schema rather than in a workflow convention that erodes under deadline.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer five: retrieval that finds document numbers, not just meaning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Pure vector search is the wrong default for this corpus, and it fails in a way that looks like success. Embeddings are good at meaning and bad at tokens. Ask for &#8220;RFI 0442&#8221; and a dense retriever will cheerfully hand you eleven chunks about requests for information, none of which is RFI 0442, because the identifier got smeared into its semantic neighbourhood. Construction disputes run on identifiers: RFI numbers, variation order references, drawing revisions, activity IDs, Bates ranges.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Run lexical and semantic retrieval in parallel and fuse the rankings. PostgreSQL with the pgvector extension does both without adding infrastructure: an HNSW index over the embedding column, a GIN index over a generated tsvector column, and reciprocal rank fusion to merge the two candidate lists.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CREATE EXTENSION IF NOT EXISTS vector;\n\nCREATE TABLE chunk (\n  id          BIGSERIAL PRIMARY KEY,\n  document_id UUID NOT NULL,\n  page_number INT  NOT NULL,\n  body        TEXT NOT NULL,\n  body_tsv    tsvector GENERATED ALWAYS AS\n                (to_tsvector('english', body)) STORED,\n  embedding   vector(1536)\n);\n\nCREATE INDEX chunk_tsv_idx  ON chunk USING gin (body_tsv);\nCREATE INDEX chunk_hnsw_idx ON chunk USING hnsw (embedding vector_cosine_ops);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Over-fetch from each side and trim after fusion rather than pulling the final count from each; more candidates give the fusion step something to work with. Add a trigram index if your corpus has OCR noise, because &#8220;RFI 0442&#8221; and &#8220;RFl 0442&#8221; are the same document to a human and different tokens to Postgres.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Filter hard on metadata before ranking. Matter, custodian, date range, privilege status. A retrieval layer that can return a privileged document into a shared workspace is a liability, not a feature.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Layer six: generation, bounded hard<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Language models are useful here and the useful jobs are smaller than the demos suggest: proposing candidate events from a batch of correspondence, drafting neutral one-line summaries, clustering near-duplicate records, suggesting which activities an event might touch. Every one of those is a proposal that lands in a review queue.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The reason to be strict is not squeamishness. Stanford&#8217;s RegLab and HAI ran a preregistered evaluation of commercial legal research tools built on retrieval-augmented generation, with expert-scored answers, and found hallucination rates in the range of roughly 17 to 33 percent depending on the product, against vendor marketing that had claimed hallucination-free citations. The vendors disputed the characterization. The finding that should worry you more than the headline number is the failure mode underneath it: a citation that exists, is quoted accurately, and does not stand for what the answer says it stands for. Retrieval reduces fabrication. It does not produce correct reasoning about what a document means, and misgrounding is far harder to spot than an invented reference.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So bound it. Generation writes to the proposal table, never to the report. Every generated assertion carries the chunk IDs it was built from, and the UI shows the source page beside the claim by default rather than behind a click. If a proposed event has no evidence reference, it does not enter the queue at all.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Tenancy, privilege, and the thing that ends the engagement<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Matters do not share data. Ever. Not &#8220;logically separated by a WHERE clause that we always remember to add&#8221;. The cheapest defensible pattern is a database per matter, or at minimum a schema per matter with connection-level scoping, so that a missing predicate produces an empty result rather than another client&#8217;s correspondence.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two more that get discovered late. Privilege state is per document per matter and it changes; model it as a versioned attribute with an audit trail, not a boolean someone flipped. And plan the exit at the start: matters end, retention clocks expire, and someone has to certify destruction. That means knowing every copy you made, including OCR text, thumbnails, embeddings and backups. Tools like the O&amp;O Software utilities have a place at the endpoint end of that job; the platform side is a deletion manifest that enumerates derived artifacts per document, written at ingest rather than reconstructed under pressure.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If experts are working from home, from site cabins, or from three countries, the access path is part of the design. A managed VPN such as NordVPN or Surfshark on the client side plus network-level allowlisting on the platform side is a pragmatic combination, and it is worth writing into the engagement documentation so nobody improvises with a personal Dropbox at eleven at night.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where these platforms break in practice<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Chronology dates drift from document dates.<\/strong> Usually a timezone or a date-format collision: a scanned form read as DD\/MM, an email header parsed as UTC, a schedule data date stored as a naive timestamp. Store the raw string you extracted alongside the parsed value and show both in the review UI.<\/li>\n\n\n\n<li><strong>Float figures look wrong by a constant factor.<\/strong> Hours versus days in the schedule import. Check against the same activity opened in P6 before you debug anything else.<\/li>\n\n\n\n<li><strong>Retrieval misses a document everyone knows exists.<\/strong> Either it was never OCR&#8217;d because the PDF had a thin text layer that passed your emptiness check, or the identifier is only in the title block of a drawing. Log extraction character counts per page and alert on suspiciously low ones.<\/li>\n\n\n\n<li><strong>Two activities merged across updates.<\/strong> Composite keying was not applied, or an activity was renumbered. Diff activity code sets between consecutive snapshots and surface additions and deletions as a report the planner reviews.<\/li>\n\n\n\n<li><strong>Document families split.<\/strong> Bates padding or an unreconciled attachment range from the load file. Re-run the family reconciliation and count orphaned attachments.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Common mistakes<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Treating the schedule as documents. It is a graph with versions, and shoehorning it into the document store costs you every analysis you actually wanted.<\/li>\n\n\n\n<li>Storing extracted text without page and coordinate provenance. Cheap now, a full reprocessing run later.<\/li>\n\n\n\n<li>Vector-only retrieval. Looks great in a demo built from prose, falls over on the first RFI number.<\/li>\n\n\n\n<li>Letting model output reach a deliverable without a recorded human confirmation.<\/li>\n\n\n\n<li>Mutating loaded schedules to &#8220;fix&#8221; them. Fix on the way in, or hold the correction as an annotation. Never edit the snapshot.<\/li>\n\n\n\n<li>Deferring the deletion story to the end of the matter, when the derived artifacts are already scattered.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Best practices for building a construction dispute intelligence platform<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hash at the door, lock the originals, and never process in place.<\/li>\n\n\n\n<li>Make every derived artifact point back to a document ID, a page and a region.<\/li>\n\n\n\n<li>Give schedules their own immutable, version-keyed store with the data date as a first-class field.<\/li>\n\n\n\n<li>Model events separately from both documents and activities, with many-to-many links in each direction.<\/li>\n\n\n\n<li>Run hybrid retrieval and fuse the rankings; filter on matter and privilege before you rank.<\/li>\n\n\n\n<li>Keep generation in a proposal queue with confirmation recorded in the schema.<\/li>\n\n\n\n<li>Isolate matters at the database or schema boundary, not in application code.<\/li>\n\n\n\n<li>Instrument ingest as seriously as you would a production pipeline. Grafana over your extraction counts and OCR queue depth will tell you a corpus is going wrong days before an analyst does.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Frequently asked questions<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Do I need a dedicated vector database for this?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Almost certainly not. Matter corpora are large in gigabytes and small in chunk count compared to consumer-scale search. PostgreSQL with pgvector keeps the embeddings in the same transaction as the provenance rows, which means you cannot end up with a chunk whose document record was rolled back. That consistency is worth more here than the last few milliseconds of query latency.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Should the platform pick a delay analysis method?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">No. That choice belongs to the expert and depends on the contract, the forum, the records available and the timing of the analysis. What the platform should do is make it obvious which methods the record set can support, by reporting what you actually have: how many schedule updates, at what intervals, with what data dates, and how complete the contemporaneous correspondence is around each window.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">XER or P6 XML for schedule ingest?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Accept both, because you will be sent both. XER is what planners export by habit and it is easy to parse with existing libraries. P6 XML is more explicit about structure and less prone to encoding surprises. Whichever arrives, keep the original file, hashed, alongside the parsed rows.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How much of the review can realistically be automated?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The ranking and the grouping, most of it. The judgement, none of it. Automation that reduces a hundred thousand documents to a few thousand worth reading is enormously valuable and uncontroversial. Automation that decides what a letter means is where you inherit risk you cannot price.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Can I build this on top of a common data environment we already have?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Partly. A CDE such as Aconex is a good source of records and a poor system of record for a dispute, because access changes, exports are point-in-time, and the retention rules belong to the project rather than to the matter. Pull from it, hash what you pulled, and hold your own copy under your own lock.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">What does this cost to run?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The shape of the bill is more useful than a number. OCR and embedding are one-time per document and scale with page count, so a scan-heavy corpus costs multiples of a born-digital one. Storage is small and constant. Retrieval is negligible. Generation scales with how much you push through it, which is another argument for narrow, queued use rather than a chat box over the whole corpus.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Is a general eDiscovery platform not enough?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">For document review, often yes, and they are considerably better at productions and privilege logging than anything you will build in a quarter. What they do not have is the schedule plane. The value of a construction-specific build is the join between correspondence and programme versions, so a sensible architecture frequently uses an established review platform for documents and adds the schedule and chronology layers alongside it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The one thing worth remembering<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A construction dispute intelligence platform earns its place by making provenance cheap. Search, clustering and summarization are commodity capabilities now; anyone can bolt them onto a corpus in a fortnight. What is hard, and what decides whether an expert will rely on the output, is that every date, every event and every sentence in the eventual report resolves in one click to a specific page of a specific produced document, with a record of who confirmed it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Build the provenance spine first. Hash at intake, keep coordinates, version the schedules, model events as their own thing, and keep the model on the proposal side of a human confirmation. The clever features are easy to add afterwards. Retrofitting evidence handling into a system that was designed as a search tool is a rebuild, and it always arrives at the worst possible moment, which is about three weeks before a hearing.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Need help building or fixing this?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I work on the infrastructure side of exactly this problem: getting messy project records into a system where every claim is traceable. Things I can help with:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Designing the intake and chain-of-custody layer, including hashing, immutable storage, and load file parsing that survives odd delimiters and inconsistent Bates padding.<\/li>\n\n\n\n<li>Building the schedule ingest path for XER and P6 XML, with version-keyed snapshots and an activity identity map that holds across updates.<\/li>\n\n\n\n<li>Standing up hybrid retrieval on PostgreSQL and pgvector, including page-level and coordinate-level provenance so highlights land on the right line.<\/li>\n\n\n\n<li>Setting up the OCR and normalization pipeline, with routing rules, quality checks, and alerting on pages that extracted suspiciously little text.<\/li>\n\n\n\n<li>Matter isolation, privilege state modelling, audit logging, and a deletion manifest you can act on at the end of the engagement.<\/li>\n\n\n\n<li>Reviewing an existing platform and telling you plainly where the provenance chain breaks.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">If you have something concrete, send it over: an XER export, a sample DAT and OPT pair, a page count breakdown of your corpus, or the schema you are currently working from. It is much easier to say something useful about a real file than about a description of one.<\/p>\n\n\n\n<div class=\"wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link wp-element-button\" href=\"https:\/\/www.upwork.com\/freelancers\/~01f15a912ad84a6620\" target=\"_blank\" rel=\"noreferrer noopener\">Work with me on Upwork<\/a><\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>A layer-by-layer architecture for a construction dispute intelligence platform: hashed intake and immutable originals, OCR with page coordinates, a versioned Primavera P6 schedule plane, an event model that joins documents to activities, hybrid retrieval on pgvector, and a generation layer bounded by human confirmation. Written around what each layer prevents.<\/p>\n","protected":false},"author":1,"featured_media":337,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[508,498,52],"tags":[192,443,285,513,399,509,396,220,353,442,217,198,444,441,440,511,436,221,437,514,218,435,394,510,338,219,392,515,195,512,438,158,398,439,197,445],"class_list":["post-336","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-construction-technology","category-data-engineering","category-technical-guides","tag-architecture","tag-as-built-schedule","tag-audit-logging","tag-bates-numbering","tag-capital-projects","tag-chain-of-custody","tag-common-data-environment","tag-construction-technology","tag-data-lineage","tag-delay-analysis","tag-document-processing","tag-embeddings","tag-extension-of-time","tag-forensic-schedule-analysis","tag-human-in-the-loop","tag-hybrid-search","tag-intelligent-document-processing","tag-legal-tech","tag-litigation-hold","tag-load-files","tag-metadata-filtering","tag-ocr","tag-oracle-aconex","tag-pgvector","tag-postgresql","tag-primavera-p6","tag-project-controls","tag-provenance","tag-rag","tag-reciprocal-rank-fusion","tag-s3-object-lock","tag-schema-design","tag-system-of-record","tag-tenant-isolation","tag-vector-database","tag-xer-files","entry","has-media"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Construction Dispute Intelligence Platform Architecture<\/title>\n<meta name=\"description\" content=\"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Construction Dispute Intelligence Platform Architecture\" \/>\n<meta property=\"og:description\" content=\"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/\" \/>\n<meta property=\"og:site_name\" content=\"John Nessime\" \/>\n<meta property=\"article:published_time\" content=\"2026-08-13T20:19:52+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-13T20:19:54+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"627\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"John Nessime\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"John Nessime\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/\"},\"author\":{\"name\":\"John Nessime\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"headline\":\"Designing a Construction Dispute Intelligence Platform That Survives Cross-Examination\",\"datePublished\":\"2026-08-13T20:19:52+00:00\",\"dateModified\":\"2026-08-13T20:19:54+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/\"},\"wordCount\":3560,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/construction-dispute-intelligence-platform-provenance-chain.png\",\"keywords\":[\"Architecture\",\"As-Built Schedule\",\"Audit Logging\",\"Bates Numbering\",\"Capital Projects\",\"Chain of Custody\",\"Common Data Environment\",\"Construction Technology\",\"Data Lineage\",\"Delay Analysis\",\"Document Processing\",\"Embeddings\",\"Extension of Time\",\"Forensic Schedule Analysis\",\"Human In The Loop\",\"Hybrid Search\",\"Intelligent Document Processing\",\"Legal Tech\",\"Litigation Hold\",\"Load Files\",\"Metadata Filtering\",\"OCR\",\"Oracle Aconex\",\"pgvector\",\"PostgreSQL\",\"Primavera P6\",\"Project Controls\",\"Provenance\",\"RAG\",\"Reciprocal Rank Fusion\",\"S3 Object Lock\",\"Schema Design\",\"System of Record\",\"Tenant Isolation\",\"Vector Database\",\"XER Files\"],\"articleSection\":[\"Construction Technology\",\"Data Engineering\",\"Technical Guides\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/\",\"name\":\"Construction Dispute Intelligence Platform Architecture\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/construction-dispute-intelligence-platform-provenance-chain.png\",\"datePublished\":\"2026-08-13T20:19:52+00:00\",\"dateModified\":\"2026-08-13T20:19:54+00:00\",\"description\":\"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#primaryimage\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/construction-dispute-intelligence-platform-provenance-chain.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/08\\\/construction-dispute-intelligence-platform-provenance-chain.png\",\"width\":1200,\"height\":627,\"caption\":\"Diagram of a construction dispute intelligence platform provenance chain: a chronology row links to an evidence reference, then to a highlighted source page, then to a report line, with a red dashed shortcut showing an unsourced assertion that never reaches the report.\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/technical-guides\\\/construction-dispute-intelligence-platform\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Designing a Construction Dispute Intelligence Platform That Survives Cross-Examination\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/\",\"name\":\"John Nessime\",\"description\":\"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained\",\"publisher\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/#\\\/schema\\\/person\\\/ede0b56d0c808f123f57d5d796902105\",\"name\":\"John Nessime\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"contentUrl\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\",\"width\":512,\"height\":512,\"caption\":\"John Nessime\"},\"logo\":{\"@id\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/cropped-jn.png\"},\"description\":\"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.\",\"sameAs\":[\"https:\\\/\\\/john-nessime.com\\\/blog\",\"https:\\\/\\\/www.linkedin.com\\\/in\\\/john-m-nessime\"],\"url\":\"https:\\\/\\\/john-nessime.com\\\/blog\\\/author\\\/johnnessime\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Construction Dispute Intelligence Platform Architecture","description":"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/","og_locale":"en_US","og_type":"article","og_title":"Construction Dispute Intelligence Platform Architecture","og_description":"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.","og_url":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/","og_site_name":"John Nessime","article_published_time":"2026-08-13T20:19:52+00:00","article_modified_time":"2026-08-13T20:19:54+00:00","og_image":[{"width":1200,"height":627,"url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png","type":"image\/png"}],"author":"John Nessime","twitter_card":"summary_large_image","twitter_misc":{"Written by":"John Nessime","Est. reading time":"16 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#article","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/"},"author":{"name":"John Nessime","@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"headline":"Designing a Construction Dispute Intelligence Platform That Survives Cross-Examination","datePublished":"2026-08-13T20:19:52+00:00","dateModified":"2026-08-13T20:19:54+00:00","mainEntityOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/"},"wordCount":3560,"commentCount":0,"publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png","keywords":["Architecture","As-Built Schedule","Audit Logging","Bates Numbering","Capital Projects","Chain of Custody","Common Data Environment","Construction Technology","Data Lineage","Delay Analysis","Document Processing","Embeddings","Extension of Time","Forensic Schedule Analysis","Human In The Loop","Hybrid Search","Intelligent Document Processing","Legal Tech","Litigation Hold","Load Files","Metadata Filtering","OCR","Oracle Aconex","pgvector","PostgreSQL","Primavera P6","Project Controls","Provenance","RAG","Reciprocal Rank Fusion","S3 Object Lock","Schema Design","System of Record","Tenant Isolation","Vector Database","XER Files"],"articleSection":["Construction Technology","Data Engineering","Technical Guides"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/","url":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/","name":"Construction Dispute Intelligence Platform Architecture","isPartOf":{"@id":"https:\/\/john-nessime.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#primaryimage"},"image":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#primaryimage"},"thumbnailUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png","datePublished":"2026-08-13T20:19:52+00:00","dateModified":"2026-08-13T20:19:54+00:00","description":"How to architect a construction dispute intelligence platform: chain of custody, schedule data, hybrid retrieval, and provenance an expert will sign off on.","breadcrumb":{"@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#primaryimage","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/08\/construction-dispute-intelligence-platform-provenance-chain.png","width":1200,"height":627,"caption":"Diagram of a construction dispute intelligence platform provenance chain: a chronology row links to an evidence reference, then to a highlighted source page, then to a report line, with a red dashed shortcut showing an unsourced assertion that never reaches the report."},{"@type":"BreadcrumbList","@id":"https:\/\/john-nessime.com\/blog\/technical-guides\/construction-dispute-intelligence-platform\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/john-nessime.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Designing a Construction Dispute Intelligence Platform That Survives Cross-Examination"}]},{"@type":"WebSite","@id":"https:\/\/john-nessime.com\/blog\/#website","url":"https:\/\/john-nessime.com\/blog\/","name":"John Nessime","description":"Cloud, DevOps, Data &amp; AI \u2014 Built, Tested, Explained","publisher":{"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/john-nessime.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/john-nessime.com\/blog\/#\/schema\/person\/ede0b56d0c808f123f57d5d796902105","name":"John Nessime","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","url":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","contentUrl":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png","width":512,"height":512,"caption":"John Nessime"},"logo":{"@id":"https:\/\/john-nessime.com\/blog\/wp-content\/uploads\/2026\/07\/cropped-jn.png"},"description":"AWS Certified Solutions Architect helping businesses build reliable cloud, data, reporting, and automation solutions. I help startups, agencies, and growing businesses replace manual processes and disconnected data with practical AWS architectures, clean data pipelines, useful dashboards, and maintainable automation.","sameAs":["https:\/\/john-nessime.com\/blog","https:\/\/www.linkedin.com\/in\/john-m-nessime"],"url":"https:\/\/john-nessime.com\/blog\/author\/johnnessime\/"}]}},"_links":{"self":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/336","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/comments?post=336"}],"version-history":[{"count":1,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/336\/revisions"}],"predecessor-version":[{"id":338,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/posts\/336\/revisions\/338"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media\/337"}],"wp:attachment":[{"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/media?parent=336"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/categories?post=336"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/john-nessime.com\/blog\/wp-json\/wp\/v2\/tags?post=336"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}