Session 4 of the four-session plan. Final two engineering planning
docs, plus master-plan-status.md updated to reflect that the
engineering layer planning sprint is now complete.
docs/architecture/human-mirror-rules.md
---------------------------------------
The Layer 3 derived markdown view spec:
- The non-negotiable rule: the Mirror is read-only from the
human's perspective; edits go to the canonical home and the
Mirror picks them up on regeneration
- 3 V1 template families: Project Overview, Decision Log,
Subsystem Detail
- Explicit V1 exclusions: per-component pages, per-decision
pages, cross-project rollups, time-series pages, diff pages,
conflict queue render, per-memory pages
- Mirror files live in /srv/storage/atocore/data/mirror/ NOT in
the source vault (sources stay read-only per the operating
model)
- 3 regeneration triggers: explicit POST, debounced async on
entity write, daily scheduled refresh
- "Do not edit" header banner with checksum so unchanged inputs
skip work
- Conflicts and project_state overrides surface inline so the
trust hierarchy is visible in the human reading experience
- Templates checked in under templates/mirror/, edited via PR
- Deterministic output is a V1 requirement so future Mirror
diffing works without rework
- Open questions for V1: debounce window, scheduler integration,
template testing approach, directory listing endpoint, empty
state rendering
docs/architecture/engineering-v1-acceptance.md
----------------------------------------------
The measurable done definition:
- Single-sentence definition: V1 is done when every v1-required
query in engineering-query-catalog.md returns a correct result
for one chosen test project, the Human Mirror renders a
coherent overview, and a real KB-CAD or KB-FEM export round-
trips through ingest -> review queue -> active entity without
violating any conflict or trust invariant
- 23 acceptance criteria across 4 categories:
* Functional (8): entity store, all 20 v1-required queries,
tool ingest endpoints, candidate review queue, conflict
detection, Human Mirror, memory-to-entity graduation,
complete provenance chain
* Quality (6): existing tests pass, V1 has its own coverage,
conflict invariants enforced, trust hierarchy enforced,
Mirror reproducible via golden file, killer correctness
queries pass against representative data
* Operational (5): safe migration, backup/restore drill,
performance bounds, no new manual ops burden, Phase 9 not
regressed
* Documentation (4): per-entity-type spec docs, KB schema docs,
V1 release notes, master-plan-status updated
- Explicit negative list of things V1 does NOT need to do:
no LLM extractor, no auto-promotion, no write-back, no
multi-user, no real-time UI, no cross-project rollups,
no time-travel, no nightly conflict sweep, no incremental
Chroma, no retention cleanup, no encryption, no off-Dalidou
backup target
- Recommended implementation order: F-1 -> F-8 in sequence,
with the graduation flow (F-7) saved for last as the most
cross-cutting change
- Anticipated friction points called out in advance:
graduation cross-cuts memory module, Mirror determinism trap,
conflict detector subtle correctness, provenance backfill
for graduated entities
master-plan-status.md updated
-----------------------------
- Engineering Layer Planning Sprint section now marked complete
with all 8 architecture docs listed
- Note that the next concrete step is the V1 implementation
sprint following engineering-v1-acceptance.md as its checklist
Pure doc work. No code, no schema, no behavior changes.
After this commit, the engineering planning sprint is fully done
(8/8 docs) and Phase 9 is fully complete (Commits A/B/C all
shipped, validated, and pushed). AtoCore is ready for either
the engineering V1 implementation sprint OR a pause for real-
world Phase 9 usage, depending on which the user prefers next.
15 KiB
Human Mirror Rules (Layer 3 → derived markdown views)
Why this document exists
The engineering layer V1 stores facts as typed entities and relationships in a SQL database. That representation is excellent for queries, conflict detection, and automated reasoning, but it's terrible for the human reading experience. People want to read prose, not crawl JSON.
The Human Mirror is the layer that turns the typed entity store into human-readable markdown pages. It's strictly a derived view — nothing in the Human Mirror is canonical, every page is regenerated from current entity state on demand.
This document defines:
- what the Human Mirror generates
- when it regenerates
- how the human edits things they see in the Mirror
- how the canonical-vs-derived rule is enforced (so editing the derived markdown can't silently corrupt the entity store)
The non-negotiable rule
The Human Mirror is read-only from the human's perspective.
If the human wants to change a fact they see in the Mirror, they change it in the canonical home (per
representation-authority.md), NOT in the Mirror page. The next regeneration picks up the change.
This rule is what makes the whole derived-view approach safe. If the human is allowed to edit Mirror pages directly, the canonical-vs-derived split breaks and the Mirror becomes a second source of truth that disagrees with the entity store.
The technical enforcement is that every Mirror page carries a header banner that says "this file is generated from AtoCore entity state, do not edit", and the file is regenerated from the entity store on every change to its underlying entities. Manual edits will be silently overwritten on the next regeneration.
What the Mirror generates in V1
Three template families, each producing one or more pages per project:
1. Project Overview
One page per registered project. Renders:
- Project header (id, aliases, description)
- Subsystem tree (from Q-001 / Q-004 in the query catalog)
- Active Decisions affecting this project (Q-008, ordered by date)
- Open Requirements with coverage status (Q-005, Q-006)
- Open ValidationClaims with support status (Q-010, Q-011)
- Currently flagged conflicts (from the conflict model)
- Recent changes (Q-013) — last 14 days
This is the most important Mirror page. It's the page someone
opens when they want to know "what's the state of this project
right now". It deliberately mirrors what current-state.md does
for AtoCore itself but generated entirely from typed state.
2. Decision Log
One page per project. Renders:
- All active Decisions in chronological order (newest first)
- Each Decision shows: id, what was decided, when, the affected Subsystem/Component, the supporting evidence (Q-014, Q-017)
- Superseded Decisions appear as collapsed "history" entries with a forward link to whatever superseded them
- Conflicting Decisions get a "⚠ disputed" marker
This is the human-readable form of the engineering query catalog's Q-014 query.
3. Subsystem Detail
One page per Subsystem (so a few per project). Renders:
- Subsystem header
- Components contained in this subsystem (Q-001)
- Interfaces this subsystem has (Q-003)
- Constraints applying to it (Q-007)
- Decisions affecting it (Q-008)
- Validation status: which Requirements are satisfied, which are open (Q-005, Q-006)
- Change history within this subsystem (Q-013 scoped)
Subsystem detail pages are what someone reads when they're working on a specific part of the system and want everything relevant in one place.
What the Mirror does NOT generate in V1
Intentionally excluded so the V1 implementation stays scoped:
- Per-component detail pages. Components are listed in Subsystem pages but don't get their own pages. Reduces page count from hundreds to dozens.
- Per-Decision detail pages. Decisions appear inline in Project Overview and Decision Log; their full text plus evidence chain is shown there, not on a separate page.
- Cross-project rollup pages. No "all projects at a glance" page in V1. Each project is its own report.
- Time-series / historical pages. The Mirror is always "current state". History is accessible via Decision Log and superseded chains, but no "what was true on date X" page exists in V1 (Q-015 is v1-stretch in the query catalog for the same reason).
- Diff pages between two timestamps. Same reasoning.
- Render of the conflict queue itself. Conflicts appear
inline in the relevant Mirror pages with the "⚠ disputed"
marker and a link to
/conflicts/{id}, but there's no Mirror page that lists all conflicts. UseGET /conflicts. - Per-memory pages. Memories are not engineering entities; they appear in context packs and the review queue, not in the Human Mirror.
Where Mirror pages live
Two options were considered. The chosen V1 path is option B:
Option A — write Mirror pages back into the source vault.
Generate /srv/storage/atocore/sources/vault/mirror/p05/overview.md
so the human reads them in their normal Obsidian / markdown
viewer. Rejected because writing into the source vault
violates the "sources are read-only" rule from
tool-handoff-boundaries.md and the operating model.
Option B (chosen) — write Mirror pages into a dedicated AtoCore
output dir, served via the API. Generate under
/srv/storage/atocore/data/mirror/p05/overview.md. The human
reads them via:
- the API endpoints
GET /mirror/{project}/overview,GET /mirror/{project}/decisions,GET /mirror/{project}/subsystems/{subsystem}(all return rendered markdown as text/markdown) - a future "Mirror viewer" in the Claude Code slash command
/atocore-mirror <project>that fetches the rendered markdown and displays it inline - direct file access on Dalidou for power users:
cat /srv/storage/atocore/data/mirror/p05/overview.md
The dedicated dir keeps the Mirror clearly separated from the canonical sources and makes regeneration safe (it's just a directory wipe + write).
When the Mirror regenerates
Three triggers, in order from cheapest to most expensive:
1. On explicit human request
POST /mirror/{project}/regenerate
Returns the timestamp of the regeneration and the list of files written. This is the path the human takes when they've just curated something into project_state and want to see the Mirror reflect it immediately.
2. On entity write (debounced, async, per project)
When any entity in a project changes status (candidate → active, active → superseded), a regeneration of that project's Mirror is queued. The queue is debounced — multiple writes within a 30-second window only trigger one regeneration. This keeps the Mirror "close to current" without generating a Mirror update on every single API call.
The implementation is a simple dict of "next regeneration time"
per project, checked by a background task. No cron, no message
queue, no Celery. Just a dict[str, datetime] and a thread.
3. On scheduled refresh (daily)
Once per day at a quiet hour, every project's Mirror regenerates unconditionally. This catches any state drift from manual project_state edits that bypassed the entity write hooks, and provides a baseline guarantee that the Mirror is at most 24 hours stale.
The schedule runs from the same machinery as the future backup retention job, so we get one cron-equivalent system to maintain instead of two.
What if regeneration fails
The Mirror has to be resilient. If regeneration fails for a project (e.g. a query catalog query crashes, a template rendering error), the existing Mirror files are not deleted. The existing files stay in place (showing the last successful state) and a regeneration error is recorded in:
- the API response if the trigger was explicit
- a log entry at warning level for the async path
- a
mirror_regeneration_failurestable for the daily refresh
This means the human can always read the Mirror, even if the last 5 minutes of changes haven't made it in yet. Stale is better than blank.
How the human curates "around" the Mirror
The Mirror reflects the current entity state. If the human doesn't like what they see, the right edits go into one of:
| What you want to change | Where you change it |
|---|---|
| A Decision's text | PUT /entities/Decision/{id} (or PUT /memory/{id} if it's still memory-layer) |
| A Decision's status (active → superseded) | POST /entities/Decision/{id}/supersede (V1 entity API) |
| Whether a Component "satisfies" a Requirement | edit the relationship directly via the entity API (V1) |
| The current trusted next focus shown on the Project Overview | POST /project/state with category=status, key=next_focus |
| A typo in a generated heading or label | edit the template, not the rendered file. Templates live in templates/mirror/ (V1 implementation) |
| Source of a fact ("this came from KB-CAD on day X") | not editable by hand — it's automatically populated from provenance |
The rule is consistent: edit the canonical home, regenerate (or let the auto-trigger fire), see the change reflected in the Mirror.
Templates
The Mirror uses Jinja2-style templates checked into the repo
under templates/mirror/. Each template is a markdown file with
placeholders that the renderer fills from query catalog results.
Template list for V1:
templates/mirror/project-overview.md.j2templates/mirror/decision-log.md.j2templates/mirror/subsystem-detail.md.j2
Editing a template is a code change, reviewed via normal git PRs. The templates are deliberately small and readable so the human can tweak the output format without touching renderer code.
The renderer is a thin module:
# src/atocore/mirror/renderer.py (V1, not yet implemented)
def render_project_overview(project: str) -> str:
"""Generate the project overview markdown for one project."""
facts = collect_project_overview_facts(project)
template = load_template("project-overview.md.j2")
return template.render(**facts)
The "do not edit" header
Every generated Mirror file starts with a fixed banner:
<!--
This file is generated by AtoCore from current entity state.
DO NOT EDIT — manual changes will be silently overwritten on
the next regeneration.
Edit the canonical home instead. See:
https://docs.atocore.../representation-authority.md
Regenerated: 2026-04-07T12:34:56Z
Source entities: <commit-like checksum of input data>
-->
The checksum at the end lets the renderer skip work when nothing relevant has changed since the last regeneration. If the inputs match the previous run's checksum, the existing file is left untouched.
Conflicts in the Mirror
Per the conflict model, any open conflict on a fact that appears in the Mirror gets a visible disputed marker:
- Lateral support material: **GF-PTFE** ⚠ disputed
- The KB-CAD import on 2026-04-07 reported PEEK; conflict #c-039.
The disputed marker is a hyperlink (in renderer terms; the markdown
output is a relative link) to the conflict detail page in the API
or to the conflict id for direct lookup. The reviewer follows the
link, resolves the conflict via POST /conflicts/{id}/resolve,
and on the next regeneration the marker disappears.
Project-state overrides in the Mirror
When a Mirror page would show a value derived from entities, but project_state has an override on the same key, the Mirror shows the project_state value with a small annotation noting the override:
- Next focus: **Wave 2 trusted-operational ingestion** (curated)
The (curated) annotation tells the reader "this is from the
trusted-state Layer 3, not from extracted entities". This makes
the trust hierarchy visible in the human reading experience.
The "Mirror diff" workflow (post-V1, but designed for)
A common workflow after V1 ships will be:
- Reviewer has curated some new entities
- They want to see "what changed in the Mirror as a result"
- They want to share that diff with someone else as evidence
To support this, the Mirror generator writes its output
deterministically (sorted iteration, stable timestamp formatting)
so a git diff between two regenerated states is meaningful.
V1 doesn't add an explicit "diff between two Mirror snapshots" endpoint — that's deferred. But the deterministic-output property is a V1 requirement so future diffing works without re-renderer-design work.
What the Mirror enables
With the Mirror in place:
- OpenClaw can read project state in human form. The
read-only AtoCore helper skill on the T420 already calls
/context/build; in V1 it gains the option to call/mirror/{project}/overviewto get a fully-rendered markdown page instead of just retrieved chunks. This is much faster than crawling individual entities for general questions. - The human gets a daily-readable artifact. Every morning,
Antoine can
cat /srv/storage/atocore/data/mirror/p05/overview.mdand see the current state of p05 in his preferred reading format. No API calls, no JSON parsing. - Cross-collaborator sharing. If you ever want to send someone a project overview without giving them AtoCore access, the Mirror file is a self-contained markdown document they can read in any markdown viewer.
- Claude Code integration. A future
/atocore-mirror <project>slash command renders the Mirror inline, complementing the existing/atocore-contextcommand with a human-readable view of "what does AtoCore think about this project right now".
Open questions for V1 implementation
- What's the regeneration debounce window? 30 seconds is the starting value but should be tuned with real usage.
- Does the daily refresh need a separate trigger mechanism, or is it just a long-period entry in the same in-process scheduler that handles the debounced async refreshes? Probably the latter — keep it simple.
- How are templates tested? Likely a small set of fixture
project states + golden output files, with a single test that
asserts
render(fixture) == golden. Updating golden files is a normal part of template work. - Are Mirror pages discoverable via a directory listing
endpoint?
GET /mirror/{project}returns the list of available pages for that project. Probably yes; cheap to add. - How does the Mirror handle a project that has zero entities yet? Render an empty-state page that says "no curated facts yet — add some via /memory or /entities/Decision". Better than a blank file.
TL;DR
- The Human Mirror generates 3 template families per project (Overview, Decision Log, Subsystem Detail) from current entity state
- It's strictly read-only from the human's perspective; edits go to the canonical home and the Mirror picks them up on regeneration
- Three regeneration triggers: explicit POST, debounced async-on-write, daily scheduled refresh
- Mirror files live in
/srv/storage/atocore/data/mirror/(NOT in the source vault — sources stay read-only) - Conflicts and project_state overrides are visible inline in the rendered markdown so the trust hierarchy shows through
- Templates are checked into the repo and edited via PR; the rendered files are derived and never canonical
- Deterministic output is a V1 requirement so future diffing works without rework