28 minute read

TL;DRAI agents maintain a distilled working state from the eligible and accessible email, meeting-transcript, and Teams-chat streams in my consulting business. Schemas, deterministic routing, scoped context, typed contracts, hooks, and independent validation constrain the unreliable components. This note examines the mechanisms and the release that overwrote its own deliverables. It also provides a small reproducible example: the scaffold, decision schema, routing rules, prompts, and validator.
This piece argues
  1. The core design move is to treat the language model as an untrusted subcontractor inside a trusted harness of schemas, hooks, and deterministic scripts. see why — jump to the section that argues this
  2. The core knowledge-management problem in a consulting business is maintaining accurate state over time, not having enough storage or search capabilities. see why — jump to the section that argues this
  3. Designing schemas and matching rules before automation is the key architectural decision that prevents an AI-maintained archive becoming an unusable lake of markdown. see why — jump to the section that argues this
What to do
  • Define your model first: schemas, lifecycle, and matching rules for key entities (start with decisions) before building any automation, because a lake cannot be un-poured.
  • Engineer layered context—scopes, schemas, task profiles, routing—so the model sees only the right hundred lines, not the whole archive, whenever it acts.
  • Treat the language model as an untrusted subcontractor: enforce typed outputs, scope-limited prompts, non-negotiable hooks, and independent validation scripts around it.

I built a system that turns emails, meeting transcripts, Teams conversations, and structured business data into linked records of decisions, commitments, and obligations to help me become a better consultant. It helps me retain what the customer said and test my recommendations against the circumstances in which they have to work.

That includes more than conversations. Information about team structures and the customer’s systems helps me understand how the organisation operates. I bring that information together so I can test a hypothesis against actual data and check a proposed solution against concrete customer scenarios.

As a consultant, I can only hold so much context in my head. This system lets me bring more of the customer’s environment into the work: checking a recommendation against recorded information, surfacing constraints I might otherwise miss, and identifying where I still need to ask questions.

I call the system Engage. Its repository history records capture and reconciliation by late May 2026. In current operation, it pulls eligible and accessible email, available meeting transcripts, and Teams chats from my configured consulting streams into one git repository. Scripts pull it in, AI agents classify and file it, and what comes out the other end is a maintained account of the business drawn from those streams: what was said, what was decided, what I owe people, and what they owe me. That coverage is my operating account of the configured streams, not the result of an independent completeness audit. I correct the working state; I no longer write it.

You may know the personal version of this idea as a second brain: a system for filing your own notes and thoughts. Run one for a business rather than a person, with clients, engagements, invoices, and obligations flowing through it, and it becomes something with different engineering demands. I call it an organisational brain. Mine runs the business side of NKD Agility: pipeline, proposals, invoicing, partner records, and the delivery of every live client engagement. This is the engineering note: the mechanisms, the contracts, and the failures behind a maintained decision log with the conversations as evidence.

The problem was never storage

I am, most days, a consultancy of one, and a consulting business produces knowledge faster than one person can file it. A single week generates client calls, email threads with commitments buried in paragraph four, proposals in flight, and invoices with stories attached. Storage was never the problem: I had folders, note apps, and a search box. The problem was state. What did we decide on that call? What did I promise, and by when? What changed since the proposal was signed?

A call does not arrive as a transcript. In my current setup, I have Teams configured to record meetings and run Krisp locally as a backup, so I have a transcript to retain. That deliberate capture step comes before the filing and maintenance described below. If a conversation is not recorded, the system has no transcript from which to recover its decisions. Preserving a transcript verbatim keeps the captured text available for checking summaries; it does not establish transcription accuracy. The system did not need a better search box. It needed a maintainer that never gets tired, and enough structure that the maintainer could be a machine.

That framing decides the whole architecture. If the maintainer is a machine, and the machine is a language model, then the machine is also the least trustworthy component in the system: brilliant at reading a transcript, entirely capable of filing it somewhere plausible and wrong, and not reliably deterministic enough to govern itself. Most of the engineering below exists to answer one question: how do you let an unreliable component maintain your source of truth?

Day one was schemas, not scripts

The first day’s commits, dated 28 May 2026, contain almost no automation. They establish the model: schema definitions for the things the system would hold. Decisions, meetings, actions, insights, risks, people, proposals. The entity types came before any script that would ever populate them, and that ordering was the single best call in the project.

The reason is contractual. If an agent files a meeting, it must file it as something: with fields, naming rules, and a defined relationship to the other things in the repository. Without a model, automation can produce records with inconsistent fields and relationships. Defining the model first lets you check records as they are created instead of restructuring them later. As checked on 19 September 2026, the repository contains 317 commits and the platform sits at version 2.39.0, and the schemas from day one still define the records: sixteen typed knowledge entities, each defined in a context file an agent loads before it touches that type.

A context file is not documentation. It is the contract the agent executes against, and it carries three things a human note-taker holds in their head without noticing. The fields: a decision has actors, an objective, a rationale, an outcome, signals, and a source. The lifecycle: proposed, accepted, rejected, superseded, revoked, and what each transition requires. And the matching rule, which is the one that keeps the corpus convergent. For decisions it reads:

Match on title similarity (>80% overlap) + date within 7 days. Merge over create.
If a decision supersedes an existing one, update the existing record's status
to 'superseded' and link to the new decision.

Merge over create keeps repeated captures from multiplying records. An agent that cannot tell whether this decision is that decision must be told what “the same” means, mechanically, or repeated captures can create duplicate records.

Two layers, one pipeline, and the pipeline is context engineering

Everything in the repository is either platform or instance. Platform is the definitions: the constitution, schemas, skills, routing rules, and the scripts that operate it all. Instance is the working data: one scope for the business itself, one scope per signed client engagement. You write freely in an instance; you change platform only deliberately, with a version bump and a changelog entry.

Every scope has the same internal shape:

<scope>/
  filing/
    sources/      raw records: the authority for checking downstream summaries
    data/         raw tool and export output
    evidence/     supporting records and correspondence
  knowledge/      distilled working state: meetings, decisions, actions, insights
  work/
    analysis/     synthesised internal views
    deliverables/ curated, client-facing outputs
  outbox/         rendered exports ready to send

Raw material arrives in an inbox, is routed to a scope, and then flows downward: filing, then knowledge, then work, then outbox. Two rules make the shape trustworthy rather than merely tidy. First, a source-of-truth hierarchy: when records disagree, the one nearer the raw source wins. A knowledge summary that contradicts the transcript is wrong by definition, whatever it says. Second, a separation between audiences: knowledge/ is internal and honest; work/deliverables/ is client-facing and curated, synthesised from knowledge and never produced by editing a knowledge record into politeness. The rule is to create a separate deliverable when material needs adapting for a client, preserving the internal record. Separate folders make that distinction visible; they do not by themselves prevent an agent from editing the wrong file.

But the layering has a third job, and it took me longest to see clearly: the pipeline is context engineering. Normal work does not load the undifferentiated raw archive. When an agent works on an engagement, it gets the distilled knowledge layer, typed and scoped. Individual originals remain available through the knowledge-summary-to-filing chain when a claim needs checking, and the filing stage necessarily reads each capture it processes. In my operating practice, answering skills compare an answer with the relevant original before responding, although the repository does not yet enforce that as one universal gate across every answering skill. Task profiles decide what a session must read before it acts; entity schemas decide what it loads per record type; the routing file decides which scope’s context applies at all. Deciding what the model sees, and when, reduces the opportunity to assemble a confident answer from irrelevant material. This is the working half of the argument our AI Adoption Guide makes about context as an operating asset: the archive is not the memory; the engineered slice of it is.

Three capabilities fall out of that layering, and they are what the build was for. Businesses have always kept records; what a records pile has never supported is this. The record can be interrogated: a question is answered from the distilled layer, in the flow of work, with the verbatim evidence reachable through its linked summary. It is validated: the source-of-truth hierarchy tells the reader or answering skill which record to check when accounts disagree, reconciliation surfaces contradictions and supersessions, and signals provide criteria for reassessing a decision. Recognising a contradiction still requires judgement; the hierarchy does not automate that judgement. And it is applied: task profiles and propagation put the relevant governed context in front of each session before it acts on anything new. That last one deserves precision, because it defines what consistency means in this system. It is not that outcomes repeat; a consulting business is not a machine, and mine does not want to be one. It is that the context is selected by the same rules: each piece of work starts with the governed memory relevant to its scope. Consistent context selection, honest judgment on top, whatever outcome the circumstances deserve.

The inbox is a contract, not a folder

Intake starts with a source that has been recorded or exported and is accessible to the system. It determines what enters the knowledge system, so each capture must move through defined states. A capture arrives in inbox/raw/ as a folder with a typed name (rec-- for recordings, email- for mail, doc- for documents) and a metadata.json describing what it is. From there it moves through triage/, and ends in exactly one of processed/, rejected/, or a blocked/ state that names why.

The first gate never reads the content at all. A deterministic pre-filter reads metadata.json only and applies reject patterns from the routing file: calendar acceptance notifications, for instance, carry no knowledge value and are rejected by subject prefix before any model sees them. Cheap, fast, and auditable, and it means the expensive judgment only runs on captures that might matter.

Everything after that gate must land in a named outcome. The classification stage is forced, by schema, to return exactly one of:

filed | rejected-deterministic | blocked-low-confidence |
blocked-missing-artifact | blocked-file-access-boundary

There is no “did my best” in that enum, and that is the point. A capture whose scope cannot be resolved with high confidence is not guessed into a folder; it moves to blocked/low-confidence/ and waits for me. An automated filing system earns trust by refusing to file what it is not sure about, because one confidently misfiled client email costs more trust than fifty blocked captures cost time.

Two more filing rules preserve the evidence. Filed sources must contain the full verbatim content of the capture, inline: transcripts under a ## Transcript heading, email bodies whole. Metadata-only stubs are forbidden, and truncating a source requires an explicit approval recorded in the document’s own frontmatter, because the filing layer is the source of truth and a truncated source omits evidence that later checks may need. And every filed capture is registered twice: once in a machine-readable source index (a script upserts source_id, path, type, and status), and once as an evidence record in the scope’s knowledge, carrying where it came from, how it was routed, and a processing_status that the downstream extraction stage reads. Nothing enters the corpus without an audit trail saying how it got there.

Routing is a file, and identity comes first

Scope resolution, deciding whether a capture belongs to the business or to a specific engagement, is the highest-consequence call in the pipeline, so it does not live in a prompt. It lives in a single routing authority file every agent loads at bootstrap, and its most important rules are about identity, not topics. The file opens by defining who I am, what the company is, and what a partner is, because the failure it guards against is exactly the one a naive classifier commits: I attend every meeting, so without a rule that says otherwise, a classifier will eventually decide I am a client and open a workspace for me. The identity rule prevents my name appearing as author or attendee from creating a client contact or engagement for me. The content still needs scope classification against the business and engagement rules.

Below identity sit the mechanics: a canonical list of live engagement workspaces keyed by engagement rather than client, because a client can have several engagements over time; a known-parties index for deterministic matching on senders and domains; always-business and always-engagement content-type lists; and an explicit ambiguity policy. For a known client with an active engagement, the file classifies the topic: commercial and relationship material belongs to business; engagement delivery material belongs to the engagement. Topic ambiguity in that identified context defaults to business. That fallback is not permission to file an unidentified capture. If metadata cannot establish the party, the filing stage reads the body and checks known parties and engagement records; if the evidence still cannot establish a scope with high confidence, it blocks. Genuinely mixed content is split by segment rather than filed twice. All of it is written down, versioned, and identical for every agent that will ever run, which is what makes the routing arguable: when a capture lands in the wrong scope, the fix is an edit to a file, not a hope that the next prompt behaves.

Treat the agent as the untrusted component

The part of the system I would defend in a design review is not any single schema. It is the stance: the language model is an unreliable component, constrained by explicit instructions and separate checks.

That shows up in four mechanisms. First, typed outputs. Every agent stage in the orchestration is forced to return JSON against a declared schema: the routing stage returns its outcome enum, resolved scope, filed path, and source id; the validation stage returns booleans and error strings; the extraction stage returns counts of entities written by type. An agent that cannot fill the schema fails validation. Free-text reports make missing fields harder to detect.

Second, scope-limited prompts. Each workflow stage tells its agent exactly which steps of the relevant skill it may execute and which are forbidden, because the orchestrator owns the pipeline. The filing stage, for instance, is hard-stopped before validation and archiving: those belong to a later stage that needs the capture still in place, so the prompt forbids the agent from helpfully finishing the job. The lesson cost me several silently self-archived captures: an agent given a whole procedure will complete it, so give it only the steps you intend it to perform.

Third, hooks that enforce write controls. A pre-write hook blocks any modification to the platform directories unless a platform_update_mode flag is deliberately set in the version file, so a session cannot casually edit the constitution it is supposed to be governed by. A post-write hook validates every document written to a filing path: required frontmatter present, status: filed set, or the write is flagged on the spot. These checks run as scripts, and they run whether or not the model read the instructions carefully.

Fourth, validation that the agent does not perform on itself. After the filing stage, separate deterministic scripts re-check every filed source and only then archive the capture out of triage. The agent that filed a document is never the authority on whether it was filed correctly.

The same economics govern which model runs where. Discovery and indexing stages run on the cheapest model available; routing runs mid-tier; only extraction and propagation, the stages that actually require judgment across a whole scope, get the strongest model. Intake itself, pulling recordings and transcripts, mail, and chats, is plain PowerShell with an OAuth pre-flight that checks the token cache and handles interactive sign-in before the imports run. In my operating experience the token cost for processing a week is modest because most stages are mechanical, but I have not gathered a comparative cost baseline.

The decision log is the payoff

Of everything the system maintains, the piece that has repaid the build many times over is the decision log. A decision is a first-class entity. This synthetic composite is constructed for publication and preserves the schema without retaining a client record, date, identifier, path, or distinctive subject:

entity_type: decision
id: Decision-service-changes-require-an-owner-0001
title: "Every production service change requires a named owner"
status: accepted
confidence: high
actors: [consultant, service-owner, delivery-lead]
objective: >
  How the team keeps responsibility visible when a production service changes.
rationale: >
  Why named ownership was chosen, including the alternative considered and
  the reason it was set aside.
outcome: >
  Each production change records one accountable owner before deployment.
signals:
  - Every deployed change has an accountable owner
  - Unowned changes are blocked before deployment
source: knowledge/meetings/Meeting-service-reliability-review.md

Four field choices make the decision inspectable. source points to a knowledge summary, which links onward to the original Markdown in filing/; that chain lets “did we really agree that?” be checked against the recorded conversation rather than whoever remembers loudest. The rationale records the alternatives that lost and why, which is the part no one can reconstruct later. signals states what to check when reassessing whether the decision is still right. In this example they show whether the ownership rule is being followed, not whether it improves service reliability. Assessing that would need evidence of its effects. The fields do not monitor themselves. And the lifecycle means a reversed decision is never deleted: it is superseded with a link, and the history of your own mind-changes turns out to be worth as much as the decisions themselves.

As recorded in the source research on 27 August 2026, one live engagement carried more than fifty of these. A dedicated lower-cost model stage maintains the decision index, making missing index entries a detectable defect rather than claiming the index is complete by definition. A platform rule requires changes to these decisions to reach the records that depend on them: a decision reflected in only one place is not finished.

Reconciliation is automatic after routing and filing

That rule is enforced by the reconciliation workflow, which runs after every intake batch and is the most engineered part of the platform. It works scope by scope, in six stages: assess structure, apply structure, assess content, apply content, index decisions, propagate.

The assess stages write their findings to report files before anything acts: a structure gap report listing what the scope’s knowledge layer is missing against the schemas, and an extraction gap report classifying every filed source as not extracted, shallow, or complete. After a capture passes routing and filing, the system automatically adopts, integrates, and reconciles the resulting knowledge. Structure gaps like a missing index or an unregistered folder are fixed mechanically; low-confidence captures and unresolved exceptions are flagged for human assessment. Apply-content runs extraction on the remaining sources, writing meetings, actions, decisions, and insights against their schemas, and reporting counts per type. Finally, propagation cascades accepted and superseded decisions down through every analysis, deliverable, and plan that references them, with the decision record as tie-breaker wherever documents disagree.

Report first, act second, and never delete: the same shape as the audit tooling I build for clients, arrived at independently because the failure mode is the same. A pipeline that silently “fixes” your knowledge is indistinguishable from one that silently corrupts it. The reports are what let me audit what the machine thought needed doing, including the things it declined to do.

The human is not an approval gate

Worth being precise about, because it is the design decision I get challenged on most. There is no per-record approval queue in this system. Once routing and filing succeed, the machine distils and incorporates the material, then reconciliation updates the knowledge without waiting for me. Low-confidence material is flagged for assessment. That was a deliberate platform decision, and like every correction of the platform’s behaviour it is written down in the memory folder where every future session loads it. A human approval queue would let work accumulate until the memory stopped keeping pace with the business. Post-moderation makes the information available as a better starting point than unaided recollection, then source comparison and corrective feedback improve it through use.

So where do I actually live in this system? I correct records, change maintenance rules and reconsider the system’s purpose. Those changes have different consequences.

I fix records, mostly at the moment of use: interrogating the memory is also inspecting it, a wrong distillation can be followed through its summary to the verbatim source, and the reconciliation runs are hunting contradictions and supersessions from the machine’s side between my visits. A record correction repairs that instance. When the same error recurs, I use it to identify a rule or policy that needs changing; that is how feedback affects future work. Record fixes are free: instances are writable at will.

I fix rules: the routing entry that misfiled a capture, the reject pattern that let noise through, a schema’s matching threshold. Rule fixes are versioned: every one bumps the platform version and lands a changelog entry, because a rule change alters what every future session does.

And rarely, I change the frame: what gets filed at all, what stays internal and what is shared with clients, what must never be automated. Both rule and frame changes encounter the same pre-write platform guard and require its flag to be deliberately set. The additional care at the frame level is mine: I reconsider what the system should do and who it serves before changing those boundaries. The hook controls platform writes; it does not judge the consequences of the policy I choose.

I use situational prevention as an analogy for this design. Clarke’s 1995 review of the field examines how changing the effort and opportunity around a behaviour can reduce it, with displacement proving less serious than critics expected. It does not measure this system. Friction is a design material. Routine import and filing from the configured, accessible streams no longer depend on me doing them by hand. They still consume compute, and exceptions require assessment. At the points where a mistake could affect later work, the controls add effort: an unresolved capture blocks, and platform edits require the guard to be deliberately opened. The machine maintains records within rules and boundaries that I remain responsible for changing. That, and not an approval queue, is where the judgment lives, and it is why the judgment compounds instead of piling up unread.

What went wrong

Patch 2.32.2 of the platform exists because the batch PDF renderer enumerated every deliverable in a folder and fed slide decks, which a different skill had already rendered correctly, through a document pipeline that overwrote the good output with garbage. Real deliverables, destroyed by the tool that existed to produce them. The root cause was a discovery loop that never read deliverable_kind from the documents it was consuming: the metadata existed, and the tool ignored it.

The fix was ordinary engineering: a shared frontmatter reader, a refusal list so the document pipeline declines presentation-kind deliverables by type, the same test applied in defence at three layers, and a batch summary that reports rendered, skipped, and failed instead of a bare processed count that no longer meant anything. The changelog entry reads like an incident report because it is one. That is the lesson I did not expect when I started: a personal tool that a business depends on has to be run like a product. Versioned platform, changelog discipline, regression thinking. The alternative is a system you slowly stop trusting, and records I no longer trust add maintenance work without helping me make decisions.

Honesty also requires a coverage note. Once a source is available, eligible and accessible email, meeting transcripts, and Teams chats in the configured streams are imported and processed automatically. Teams chats inside a customer’s tenant remain a separate access gap where security restrictions prevent the system from retrieving them. A manual export may be needed where the customer permits it; that is not an automated integration, and my local meeting-recording backup does not supply those missing chats. Board exports and invoicing data are pulled on demand. WhatsApp content gets filed by hand when it matters, and Slack is not ingested at all. The repository history begins on 28 May 2026. That is enough evidence to describe the mechanics; it is not evidence of long-term compounding.

Recreate it in a weekend

The following example is a small version of the filing and decision-record process, designed to build in a weekend. It assumes git, PowerShell 7, an agent that can read and write local files, an available source such as a meeting transcript, authority to retain it, and appropriate access controls. With those prerequisites in place, the chain can run on day one. Build it or don’t; either way, it makes the mechanism inspectable.

You need five things, and the entity model comes first, because it defines what the system can hold.

One: the scaffold. A git repository with this shape (one scope to start; you saw the full version earlier):

my-brain/
  rules/
    routing.yaml
    decision.context.md
  inbox/
    blocked/
  business/
    filing/sources/
    knowledge/decisions/

Two: one entity schema. Decisions are the highest-value entity, so start there. This simplified decision schema defines the records the model will create. For this example, each decision links directly to the filed original; Engage instead links through a knowledge summary. All paths below are relative to the repository root, my-brain/, and the example uses business/ as its only scope:

---
entity_type: decision
item_pattern: Decision-<slug>-####.md
location: business/knowledge/decisions/
fields:
  title: what was decided, as one declarative sentence
  date: when it was decided
  status: proposed | accepted | superseded | revoked
  actors: who made the decision or was significantly in it
  objective: the question this decision answers
  rationale: why, including the options that lost and the reason they lost
  outcome: what was actually agreed, in enough detail to act on
  signals: what would tell you this decision is still right
  source: repository-root-relative path to the original in business/filing/sources/
matching_rule: >
  Match on title similarity (>80%) and date within 7 days. Merge over create.
  If this decision supersedes an existing one, mark the old record superseded
  and link it to the new one.
create_when: >
  The source contains an explicit choice between options with a confirmed
  outcome, an agreed commercial or legal position, or a constraint that will
  govern future work. Not preferences, not options still open.
---

Three: the routing rules. Identity first, because the classifier’s most reliable mistake is deciding that you are your own client:

identity:
  consultant: [Your Name, you@yourfirm.com]   # authors content; NEVER a client
  company: [Your Firm Ltd]                    # the business; NEVER an engagement
scopes:
  default: business/      # apply only after the party and safe scope are established
  engagements: {}          # add one per signed client engagement, when you scale
always_business: [invoices, proposals, pipeline, partner conversations]
if_unresolvable: inbox/blocked/    # missing party/scope evidence overrides the default

Four: two prompts. These are the two stages of the chain, dumbed down from my pipeline to run in any AI chat or agent tool you already have. The filing stage:

You are the filing stage of my business memory. You never guess and you never
summarise. 1) Read the attached capture (email thread, transcript, or notes).
2) Resolve its scope using rules/routing.yaml and the capture content. Do not
use the business default to hide missing party or scope evidence. If you cannot
resolve scope with high confidence, stop and reply "blocked:" with the reason. 3) File the capture
VERBATIM to business/filing/sources/ under a dated, typed filename; the filing
layer is the ground truth, so truncation is forbidden. 4) Reply with the filed
repository-root-relative path and one line on why the scope is right.

And the distillation stage:

You are the distillation stage. Read the filed source at <path>. Extract every
decision that meets the bar in rules/decision.context.md: explicit choice,
confirmed outcome. Draft each as a record against the schema, set status:
proposed, and set `source` to the repository-root-relative filed path. Write records
in business/knowledge/decisions/. Check that same directory for existing decisions
first and obey the matching rule: merge over create, supersede over duplicate.

Five: an independent validator. Save this as validate.ps1. It checks that the filed source exists and is non-empty, that the decision has the expected type, and that its source points back into the filing layer. The agent that created either file does not run this check on itself.

param(
  [Parameter(Mandatory)][string]$RepositoryRoot,
  [Parameter(Mandatory)][string]$FiledSource,
  [Parameter(Mandatory)][string]$Decision
)

$ErrorActionPreference = 'Stop'
$rootPath = (Resolve-Path -LiteralPath $RepositoryRoot).Path.TrimEnd('\', '/')
$rootPrefix = $rootPath + [IO.Path]::DirectorySeparatorChar

function Assert-NoReparsePoint([string]$Path) {
  $current = $rootPath
  $rootItem = Get-Item -LiteralPath $current -Force
  if ($rootItem.Attributes -band [IO.FileAttributes]::ReparsePoint) {
    throw 'Repository root is a reparse point.'
  }
  $relative = [IO.Path]::GetRelativePath($rootPath, $Path)
  foreach ($segment in $relative -split '[\\/]') {
    if ([string]::IsNullOrWhiteSpace($segment) -or $segment -eq '.') { continue }
    $current = Join-Path $current $segment
    $item = Get-Item -LiteralPath $current -Force
    if ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) {
      throw "Path contains a reparse point: $current"
    }
  }
}

function Resolve-InRepository([string]$Path) {
  $candidate = if ([IO.Path]::IsPathRooted($Path)) {
    [IO.Path]::GetFullPath($Path)
  } else {
    [IO.Path]::GetFullPath((Join-Path $rootPath $Path))
  }
  if (-not $candidate.StartsWith($rootPrefix, [StringComparison]::OrdinalIgnoreCase)) {
    throw "Path leaves the repository: $Path"
  }
  Assert-NoReparsePoint $candidate
  (Resolve-Path -LiteralPath $candidate).Path
}

$sourcePath = Resolve-InRepository $FiledSource
$decisionPath = Resolve-InRepository $Decision
$sourceText = Get-Content -LiteralPath $sourcePath -Raw
$decisionText = Get-Content -LiteralPath $decisionPath -Raw

if ([string]::IsNullOrWhiteSpace($sourceText)) { throw 'Filed source is empty.' }
$frontmatterMatch = [regex]::Match($decisionText, '\A---\r?\n(.*?)\r?\n---(?:\r?\n|$)', 'Singleline')
if (-not $frontmatterMatch.Success) { throw 'Decision has no YAML frontmatter.' }
$frontmatter = $frontmatterMatch.Groups[1].Value
if ($frontmatter -notmatch '(?m)^entity_type:\s*decision\s*$') {
  throw 'Knowledge record is not a decision.'
}
if ($frontmatter -notmatch '(?m)^source:\s*(.+?)\s*$') {
  throw 'Decision has no source path.'
}
$declaredSource = $Matches[1].Trim().Trim('"', "'")
$declaredPath = Resolve-InRepository $declaredSource
$relativeSource = [IO.Path]::GetRelativePath($rootPath, $declaredPath)
if ($relativeSource -notmatch '(^|[\\/])filing[\\/]') {
  throw 'Decision source does not point into the filing layer.'
}
if (-not [string]::Equals($declaredPath, $sourcePath, [StringComparison]::OrdinalIgnoreCase)) {
  throw 'Decision source does not match the filed source being validated.'
}

'Validated the filed source and the decision type/source link.'

Run it after distillation:

.\validate.ps1 `
  -RepositoryRoot (Get-Location).Path `
  -FiledSource business\filing\sources\Meeting-latest.md `
  -Decision business\knowledge\decisions\Decision-latest-0001.md

That is the complete small example: source in by hand (drop your last meeting transcript into inbox/; automating intake is a later luxury, the model in the middle is not), filing verbatim, knowledge distilled, you moderating. Run it after every meeting that matters for a fortnight, and when an error repeats, repair the record and update the responsible rule, because that is the loop that compounds. Scale when you are happy with what comes out: automate the highest-volume stream first, add a scope per client engagement. Or skip the typing entirely and do the on-brand thing: hand this note to a coding agent and tell it to build the scaffold and wire the prompts.

The gotchas, so you hit fewer of mine:

  • Prevent duplicate records. Without a matching rule, repeated captures can create separate records of the same decision. Define how to recognise and merge an existing record before adding another.
  • Verbatim or nothing. The first time you let the filing stage “summarise for brevity”, you lose the original wording and later records rely on that paraphrase. In my platform, truncating a source requires written approval recorded in the file itself.
  • Give the agent only its assigned steps. An agent handed the whole pipeline will helpfully finish it, including steps a later stage needed undone. Mine silently archived captures a validation stage still required; scope every prompt to its stage.
  • Blocked must be cheaper than wrong. Create inbox/blocked/ on day one and treat entries in it as the system working, not failing. One confidently misfiled client email costs more trust than fifty blocked captures cost time.
  • Keep the model out of the rules. A long session will eventually try to “improve” its own instructions mid-task. My platform folders are physically write-blocked behind a deliberate flag; do the cheap version on day one and make the rules folder read-only to the agent.
  • Validate outputs with code, never with the model that made them. My worst data loss came from a pipeline that ignored metadata it already had. Existence and parse checks are only the start; type-aware validation must also reject a structurally valid artefact sent through the wrong pipeline.
  • Do not ingest what you should not. Check each platform’s terms, access controls, confidentiality requirements, and the expectations of the people using the channel before automating capture. Consent is a design input, not a legal afterthought.
  • Spend the tokens where the judgment is. In this system, lower-cost models handle bounded filing and indexing stages, while the strongest model is reserved for extraction and reconciliation. That is an operating choice rather than a measured cost comparison, and every stage is validated independently.

What you have at the end of the weekend is not my platform. It is the chain at its smallest: no orchestration, no hooks, no reconcile pipeline, no fifty skills, and it does not need them yet. Everything the full system adds is the same pattern hardened: more streams, more scopes, more entity types, deterministic validation around the same untrusted middle. Expand it when its records are useful and you can detect and correct its errors.

What I would carry forward

The architecture lets the machine maintain working records while I remain responsible for correcting the rules and boundaries it operates within. The small example makes that division inspectable: one source, one decision, and a separate check of the link between them. Expanding it means carrying those controls into each new stream and scope.

The system maintains my business records so that I can spend more attention on my clients. After nearly four months of repository history, I still consider building it the best decision I have made this year.


If you are trying to work out what an organisational brain would look like for your own business, and which parts of yours are mechanical enough to automate honestly, book a call.

Enjoyed this? One click, no account.

Comments Subscribe

Questions this answers

How can I recreate a minimal version of this organisational brain in a weekend?

The author outlines a weekend‑buildable starter version consisting of a simple git scaffold with a single scope, one high‑value entity schema for decisions, a routing rules file focused on identity, and two prompts for filing and distillation that you can run in any AI tool. You manually drop important meeting transcripts into the inbox, run the filing and knowledge‑distillation steps after each meeting for a couple of weeks, and whenever a correction repeats you update the rules file rather than individual records, then later scale by automating the highest‑volume streams and adding scopes per client engagement.