feat: publish reviewed architecture and ADR batch
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 19s
Some checks failed
Build and publish policy-nexus image / build-and-push (push) Failing after 19s
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a058f3-8ba0-7692-a042-9a870fc3d663
This commit is contained in:
parent
023badb512
commit
93608c1f17
120 changed files with 17791 additions and 727 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<!doctype html>
|
||||
<html lang="en"><meta charset="utf-8">
|
||||
<meta name="policy-source-revision" content="4039c9d1c08c92014ecc0a65dda63cc73ba187bb">
|
||||
<meta name="policy-source-digest" content="350c26d86c573716eb12333473917d91b8cd68b8798197b0077af1a6ba8c6480">
|
||||
<meta name="policy-source-revision" content="44500fc85cf29d8e9b2ee5c91994032ed3d04e5b">
|
||||
<meta name="policy-source-digest" content="fcb49719e2b85b9120c0bd5bebd5e82748ca713f16b44951c028b60205514e2b">
|
||||
<title>Materialized Derived State with Fingerprint Invalidation for Repo-Sourced Data</title>
|
||||
<style>
|
||||
:root{
|
||||
|
|
@ -191,8 +191,8 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off
|
|||
@media (prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
||||
|
||||
</style>
|
||||
<div class="wrap"><header><div class="eyebrow"><span>CUST-ADR-003</span> <span class="stat">accepted · accepted-1</span> <span>the-custodian</span> <span>reviewed 2026-03-20</span><span>generated from canonical source — do not edit</span></div><h1>Materialized Derived State with Fingerprint Invalidation for Repo-Sourced Data</h1><p class="sub">Source: <code>the-custodian · canon/architecture/adr-003-materialized-derived-state.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</code></p><p class="sub">Review due: 2026-09-20</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#status"><span class="n">·</span>Status</a></li><li><a href="#context"><span class="n">·</span>Context</a></li><li><a href="#pattern-name"><span class="n">·</span>Pattern Name</a></li><li><a href="#decision"><span class="n">·</span>Decision</a></li><li><a href="#consequences"><span class="n">·</span>Consequences</a></li><li><a href="#implementation-checklist"><span class="n">·</span>Implementation Checklist</a></li><li><a href="#current-implementations"><span class="n">·</span>Current Implementations</a></li><li><a href="#planned-applications"><span class="n">·</span>Planned Applications</a></li><li><a href="#related"><span class="n">·</span>Related</a></li></ol></nav><main><section id="status"><h2>Status</h2>
|
||||
<p>Accepted.</p>
|
||||
<div class="wrap"><header><div class="eyebrow"><span>CUST-ADR-003</span> <span class="stat">accepted · accepted-2</span> <span>the-custodian</span> <span>reviewed 2026-08-31</span><span>generated from canonical source — do not edit</span></div><h1>Materialized Derived State with Fingerprint Invalidation for Repo-Sourced Data</h1><p class="sub">Source: <code>the-custodian · canon/architecture/adr-003-materialized-derived-state.md · 44500fc85cf29d8e9b2ee5c91994032ed3d04e5b</code></p><p class="sub">Review due: 2027-02-28</p></header><div class="layout"><nav class="rail" aria-label="Sections"><ol><li><a href="#status"><span class="n">·</span>Status</a></li><li><a href="#context"><span class="n">·</span>Context</a></li><li><a href="#pattern-name"><span class="n">·</span>Pattern Name</a></li><li><a href="#decision"><span class="n">·</span>Decision</a></li><li><a href="#consequences"><span class="n">·</span>Consequences</a></li><li><a href="#implementation-checklist"><span class="n">·</span>Implementation Checklist</a></li><li><a href="#current-implementations"><span class="n">·</span>Current Implementations</a></li><li><a href="#planned-applications"><span class="n">·</span>Planned Applications</a></li><li><a href="#related"><span class="n">·</span>Related</a></li></ol></nav><main><section id="status"><h2>Status</h2>
|
||||
<p>Accepted, and <strong>partially superseded by <code>ADR-012</code></strong> (accepted 2026-08-25). Decision 2's fingerprint composition is invalidated in part; decision 5's rebuild principle is given a concrete source and a required operation. See the notes on each.</p>
|
||||
</section>
|
||||
<section id="context"><h2>Context</h2>
|
||||
<p>The Custodian State Hub is a <strong>read model</strong> (CQRS terminology) — its data is fully derivable from canonical sources that live in repositories and the filesystem. No state-hub data is authoritative; it is always a derived view of what the repos contain.</p>
|
||||
|
|
@ -213,14 +213,18 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off
|
|||
<h3>2. Each materialised table MUST carry a <code>fingerprint</code> column</h3>
|
||||
<p>The fingerprint is a deterministic string encoding all inputs that affect the computed result. It is compared on each read; if unchanged, the stored result is returned without recomputation. If changed, the result is recomputed and the stored value is updated.</p>
|
||||
<p><strong>Fingerprint composition rules:</strong></p>
|
||||
<ul><li>Include the <code>updated_at</code> timestamp of every DB record that feeds the computation (repo record, related domain, goals, snapshots).</li><li>Include the <code>mtime</code> (filesystem modification time) of every file that feeds the computation (<code>SCOPE.md</code>, <code>CLAUDE.md</code>, lockfiles, <code>tpsc.yaml</code>, etc.).</li><li>Join all components with <code>|</code> as a pipe-separated string — no hashing needed since the string is compared by equality, not transmitted to clients.</li><li>If a file is absent, encode <code>filename:absent</code> rather than omitting it, so file creation also triggers invalidation.</li></ul>
|
||||
<ul><li>Include the <code>updated_at</code> timestamp of every DB record that feeds the computation (repo record, related domain, goals, snapshots).</li><li>Include the <code>mtime</code> (filesystem modification time) of every file that feeds the computation (<code>SCOPE.md</code>, <code>CLAUDE.md</code>, lockfiles, <code>tpsc.yaml</code>, etc.).</li></ul>
|
||||
<div class="rule-quote"><p><strong>Invalidated in part 2026-08-25 by <code>ADR-012</code> decisions 1 and 2.</strong> Filesystem <code>mtime</code> is not a property of the source. It differs between machines, changes on a fresh clone, and says nothing about content — so a fingerprint built from it describes one workstation's filesystem rather than the repository. Under <code>ADR-012</code> the projection derives from the forge, and the commit that produced a record is both the correct input and the auditable one. This was not merely theoretical drift. <code>git_fingerprint</code> for <code>the-custodian</code> held the repository's <em>initial</em> commit while <code>last_state_synced_at</code> was minutes old: the field meant to identify what a projection reflects was wrong by the entire history of the repository, and nothing noticed. Replace <code>mtime</code> inputs with the source commit.</p></div>
|
||||
<ul><li>Join all components with <code>|</code> as a pipe-separated string — no hashing needed since the string is compared by equality, not transmitted to clients.</li><li>If a file is absent, encode <code>filename:absent</code> rather than omitting it, so file creation also triggers invalidation.</li></ul>
|
||||
<p><strong>Reference implementation:</strong> <code>state-hub/api/doi_engine.py::compute_fingerprint()</code></p>
|
||||
<h3>3. Every materialised endpoint MUST support <code>?force_refresh=true</code></h3>
|
||||
<p>Callers must always be able to bypass the cache and trigger a fresh computation. This is the escape hatch for debugging, post-ingest verification, and scheduled background refresh jobs.</p>
|
||||
<h3>4. Writes to source data SHOULD update the repo record's <code>updated_at</code></h3>
|
||||
<p>Operations that change source data (SBOM ingest, TPSC ingest, capability ingest) must ensure <code>managed_repos.updated_at</code> is refreshed so the fingerprint detects the change on the next read. Where data lives in a related table (e.g. <code>tpsc_snapshots</code>), the fingerprint must include that table's <code>max(snapshot_at)</code> directly rather than relying on the repo record.</p>
|
||||
<h3>5. The DB is never the source of truth — the rebuild principle holds</h3>
|
||||
<p>Per ADR-001, the state-hub must be rebuildable from scratch by re-ingesting all canonical sources. Materialised tables are <strong>caches</strong>, not records of authority. They may be wiped and repopulated at any time without data loss. This means:</p>
|
||||
<p>Per ADR-001, the state-hub must be rebuildable from scratch by re-ingesting all canonical sources. Materialised tables are <strong>caches</strong>, not records of authority. They may be wiped and repopulated at any time without data loss.</p>
|
||||
<div class="rule-quote"><p><strong>Given concrete form 2026-08-25 by <code>ADR-012</code> decision 7.</strong> This principle was correct and, until now, never exercised — an untested rebuild path is an assumption rather than a capability, and this one was believed for long enough that a divergence survived seven weeks behind it. <code>ADR-012</code> requires the reconstruction to exist as a routine operation, scoped per repository, sourced from the forge, and verifiable against it. The claim "without data loss" also needs its precondition stated: it holds only while the rule immediately below does. On 2026-08-25, 111 work records existed only in the hub, so a rebuild at that moment would have destroyed them. <code>ADR-012</code> therefore requires reset to refuse, per repository, when records have no counterpart in the forge.</p></div>
|
||||
<p>This means:</p>
|
||||
<ul><li>No materialised table may be the only copy of any information.</li><li>Schema migrations that wipe a materialised table are safe and expected.</li><li>Background jobs that periodically re-ingest all repos are valid and encouraged.</li></ul>
|
||||
</section>
|
||||
<section id="consequences"><h2>Consequences</h2>
|
||||
|
|
@ -241,4 +245,4 @@ a:focus-visible,.rail a:focus-visible{outline:2px solid var(--brass);outline-off
|
|||
</section>
|
||||
<section id="related"><h2>Related</h2>
|
||||
<ul><li>ADR-001: Workplans and Work Items Are Repository Artefacts</li><li>ADR-002: Custodian Agent Runtime Design</li><li><code>state-hub/api/doi_engine.py</code> — reference implementation</li><li><code>state-hub/api/models/doi_cache.py</code> — reference schema</li><li><code>state-hub/migrations/versions/k8f9a0b1c2d3_doi_cache.py</code> — reference migration</li></ul>
|
||||
</section><footer><span>CUST-ADR-003 · accepted-1 · accepted</span><span>the-custodian · canon/architecture/adr-003-materialized-derived-state.md · 4039c9d1c08c92014ecc0a65dda63cc73ba187bb</span></footer></main></div></div></html>
|
||||
</section><footer><span>CUST-ADR-003 · accepted-2 · accepted</span><span>the-custodian · canon/architecture/adr-003-materialized-derived-state.md · 44500fc85cf29d8e9b2ee5c91994032ed3d04e5b</span></footer></main></div></div></html>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue